RBAC for Applications Only for Exchange Online
On December 1, Microsoft launched the public preview of role-based access control (RBAC) for applications for Exchange Online. The new functionality extends the RBAC model introduced in Exchange 2010 to control access by Azure AD apps to Exchange data, or as Microsoft says, “resource-scoped permissions” that “better protect access to your tenant’s data.” Of course, although Exchange Online is a massive Microsoft 365 workload, it’s not the only source of data that needs similar protection. Alas, that isn’t available yet, so you’ll have to wait for RBAC permissions to extend to SharePoint Online, Teams, Yammer, Planner, etc.
RBAC for applications will eventually replace application access policies (see below). Today, the public preview shows some ragged edges in terms of the integration between Exchange Online and Azure AD that Microsoft say they’ll fix before general availability. Even with some bumps to navigate, the new functionality is a welcome addition that extends tenant control over their data, which is always a good thing.
It’s worth making the point that RBAC for Applications does nothing to stop administrators interacting with mailboxes at an administrative level (setting properties and so on). RBAC for Applications is all about controlling app access to mailbox contents.
Application Access Policies
Application access policies seemed like a good idea when Microsoft introduced them in 2019. An application access policy is a protocol-agnostic mechanism to allow or deny an app access to a set of mail-enabled objects with security principals (usually defined as mailboxes in a security group). The mechanism is effective but has two significant limits. First, Exchange Online supports 300 application access policies per tenant. Second, access is all or nothing. Once a policy grants an app access to mailboxes, the app can use all available Exchange Web Services (EWS) and Microsoft Graph APIs to interact with mailbox data.
Three hundred policies sounds like a lot. It is, especially in an environment where management of these policies is a manual process. The limit is probably acceptable for small to medium tenants but becomes an issue in enterprise tenants where more automated operations are common.
Several factors have increased the use of registered Azure AD apps with PowerShell:
- Microsoft has deprecated basic authentication for many email connection protocols. Exchange Online still supports the SMTP AUTH protocol, but scripts that use SMTP AUTH to send email will need to upgrade to a Graph-based method to create and send email eventually.
- Microsoft will deprecate the Azure AD and Microsoft Online Services (MSOL) PowerShell modules in June 2023. The advice given to tenants is to replace the cmdlets from these modules with Microsoft Graph APIs (including the Microsoft PowerShell Graph SDK).
- PowerShell scripts that use Microsoft Graph APIs gain access via registered Azure AD apps.
- Although multiple PowerShell scripts can use a single app, the need to restrict Graph permissions means that, in many instances, an app supports a single script. An app permission policy can apply to multiple apps but only if apps use the same permissions with the same set of mailboxes.
Given these factors, it’s obvious how quickly a tenant might reach the limit for application access policies.
A New Approach
With the experience of application access policies in mind, Microsoft decided to take a different tack with RBAC for Applications. The new model allows administrators to grant permissions to an Azure AD app to access data via a role assignment. Administrators can then restrict permissions to certain target resources by applying a management scope, which tells Exchange Online what mailboxes an app can and cannot access.
Being able to control apps like this is important. The transition toward scripts that make Graph requests to interact with data means that there’s been an explosion of Azure AD apps in many tenants. Unless an app is restricted by an application access policy, the script that it runs has free rein over what data it can access. To some, this is the natural order. After all, if an administrator connects to Exchange Online interactively in a PowerShell window, they can access the configuration and all the management objects in the tenant. The difference with Graph-based scripts is that you can access user data. For instance, a script can use the Graph Mail.Send API to send email from any mailbox. That’s not a good thing, which is why controls are necessary.
Testing RBAC for Applications
To test RBAC for Applications, I used the mailbox report script. This script reads the folders and items from a mailbox using the Graph Mail.Read permission. Without limits, the script can read items in any mailbox.
To begin, I registered a new Azure AD app. The authentication method used by the app doesn’t matter (app secret or certificate-based). To validate that the script works as expected, assign the Mail.Read application permission to the app and grant administrator consent. You should now be able to run the script to read data from any mailbox.
Now revoke the Mail.Read permission from the app. This is an important step because you’re going to replace the access granted through the administrator consent to use the permission with a permission managed by Exchange Online. If you leave the Mail.Read permission in place, it will supersede the control available through RBAC for Applications.
Defining a Service Principal Object
To use RBAC for Applications, Exchange Online must know about an application. Exchange Online uses a service principal object for this purpose. This is somewhat confusing because Azure AD apps have service principals that represent the app. In Azure AD, security principals hold the permissions assigned to apps. A similar model exists in Exchange Online, where the service principal object represents an Azure AD application. RBAC for Applications extends the RBAC model used by Exchange to allow administrators to assign management roles to apps via their service principals. The roles define what actions the app can take, like reading or sending email.
You can only create new service principal objects via PowerShell. Before running the New-ServicePrincipal cmdlet, you need to know the application identifier and service principal identifier for the app. One way to do this is to open the app overview in the enterprise applications section of the Azure AD admin center and copy the values from there (Figure 1).
As everything happens in PowerShell, you can also fetch the object identifiers and store them in variables. Here’s what I did:
Connect-MgGraph $SP = Get-MgServicePrincipal -All $ServicePrincipalId = $SP | Where-Object {$_.displayName -eq "PS-RBAC App Test"} | Select-Object -ExpandProperty Id $AppId = $SP | Where-Object {$_.displayName -eq "PS-RBAC App Test"} | Select-Object -ExpandProperty AppId Write-Host ("AppId is {0} and Service Principal Id is {1}" -f $AppId, $ServicePrincipalId) AppId is 5f75e1d1-d059-410e-8ad5-1959580d4110 and Service Principal Id is 9561fc3e-b982-4775-a0e3-cac6d1750ecb
With the identifiers, we can run New-ServicePrincipal to create the service principal object:
New-ServicePrincipal -AppId $AppId -ServiceId $ServicePrincipalId -DisplayName 'PS RBAC Test' DisplayName ServiceId AppId ----------- --------- ----- PS RBAC Test 9561fc3e-b982-4775-a0e3-cac6d1750ecb 5f75e1d1-d059-410e-8ad5-1959580d4110
Creating a Management Role Scope
Next, we create a management role scope to tell Exchange Online what mailboxes the app can access. If you’re used to mailbox filtering, there’s nothing new here as you can use any of the filterable mailbox properties to create a scope. This example creates a very simple scope to find any mailbox that has the “Manager” value in CustomAttribute1:
New-ManagementScope -Name "Management employees" -RecipientRestrictionFilter "CustomAttribute1 -eq 'Manager'"
Before testing, make sure that some mailboxes come within the management role scope by updating their attributes. For example:
Set-Mailbox -Identity James.Ryan -CustomAttribute1 "Manager"
To validate that the scope finds the expected set of mailboxes, use the same filter with the Get-EXOMailbox cmdlet:
Get-EXOMailbox -Filter {CustomAttribute1 -eq "Manager"} | Format-Table DisplayName DisplayName ----------- James Ryan
Creating a Management Role Assignment
To bring everything together, we create a management role assignment to connect the management scope with the requested access (the role). You can see that the role assigned is “Application Mail.Read,” which corresponds to the Graph Mail.Read permission required to read the contents of user mailboxes.
New-ManagementRoleAssignment -App $AppId -Role "Application Mail.Read" -CustomResourceScope "Management Employees" Name Role RoleAssigneeName RoleAssigneeType AssignmentMethod EffectiveUserNam e ---- ---- ---------------- ---------------- ---------------- ---------------- Application Mail.Read-9561f... Application Ma... 9561fc3e-b982-... ServicePrincipal Direct
RBAC for Applications also supports the use of Azure AD administrative units for management role assignments. In these commands, we list the administrative units available in Azure AD and use one in a role assignment to allow the app to send mail from the mailboxes of the users within the administrative unit:
Get-MgAdministrativeUnit | Format-Table Id, Description, DisplayName Id Description DisplayName -- ----------- ----------- 0ee53a45-bbee-4571-a407-56acc0b944a1 Ireland Region Ireland 4d3ae8ee-212b-4be4-965c-8b5111d4488e U.S. Region United States 66faad17-cc6d-45bb-8d88-35789b9b3c00 Engineering Engineering New-ManagementRoleAssignment -App $AppId -Role “Application Mail.Send” -RecipientAdministrativeUnitScope "4d3ae8ee-212b-4be4-965c-8b5111d4488e" Name Role RoleAssigneeName RoleAssigneeType AssignmentMethod EffectiveUserNam e ---- ---- ---------------- ---------------- ---------------- ---------------- Application Mail.Send-9561f... Application Ma... 9561fc3e-b982-... ServicePrincipal Direct
Available Roles for RBAC for Applications
Running the Get-ManagementRole cmdlet exposes the set of permissions supported by RBAC for Applications. All permissions granted by RBAC for Applications are application rather than delegate permissions.
Get-ManagementRole | Where-Object {$_.Name -like "Application *"} Name RoleType ---- -------- Application Mail.Read ApplicationMailRead Application Mail.ReadBasic ApplicationMailReadBasic Application Mail.ReadWrite ApplicationMailReadWrite Application Mail.Send ApplicationMailSend Application MailboxSettings.Read ApplicationMailboxSettingsRead Application MailboxSettings.ReadWrite ApplicationMailboxSettingsReadWrite Application Calendars.Read ApplicationCalendarsRead Application Calendars.ReadWrite ApplicationCalendarsReadWrite Application Contacts.Read ApplicationContactsRead Application Contacts.ReadWrite ApplicationContactsReadWrite Application Mail Full Access MailFullAccessApp Application Exchange Full Access ExchangeFullAccessApp Application EWS.AccessAsApp ApplicationEWSAccessAsApp
Many of these permissions are among the set of high-priority permissions that hackers attempt to exploit. It’s a good idea to include apps governed by RBAC for Applications in the periodic reviews of apps and permissions.
Waiting for Caching
After creating the management role assignment, the situation should be:
- The registered Azure AD app identified in the service principal object has no Graph permissions. You can confirm this by examining the roles specified in the access token granted by the Graph. The access token should contain no trace of the Graph permission that the app will gain through the role assignment.
- A management scope or Azure AD administrative unit is available to identify target mailboxes.
- The management role assignment will allow the Azure AD app to run the permission specified in the management role assignment against the target mailboxes.
Exchange Online caches permissions so it can take up to 30 minutes before the management role assignment is effective. To avoid the effect of caching, restart your PowerShell session to make sure that Exchange Online uses the latest entitlements.
The Test-ServicePrincipalAuthorization cmdlet allows administrators to test that an app can access a target mailbox. For example, this test returns a False result, meaning that the selected mailbox is outside the management scope.
Test-ServicePrincipalAuthorization -Identity $AppId -Resource Jack.Smith RoleName GrantedPermissions AllowedResourceScope ScopeType InScope -------- ------------------ -------------------- --------- ------- Application Mail.Read Mail.Read Management employees CustomRecipientScope False
After allowing caching to happen, test that Exchange Online allows the app to access the in-scope mailboxes and blocks attempted access (403 Forbidden error) when the app tries to open mailboxes outside the scope.
Marching to General Availability
Microsoft expects RBAC for Applications to reach general availability sometime in the first half of 2023. When the feature reaches that point, I expect Microsoft will improve the current rough edges around RBAC for Applications, like the interaction with Azure AD apps and permissions, and perhaps even create some UX in an admin portal. Once RBAC for Applications is generally available, Microsoft will proceed with the depreciation of application access policies. Until that happens, the two mechanisms can run side-by-side.
Early tests show that RBAC for Applications is a good way to control app access to mailboxes. The generally available version will be easier to use, but there’s no good reason not to use PowerShell commands to configure access today. Like regular RBAC, once you get your head around what must be done, it’s straightforward.
The Microsoft 365 Kill Chain and Attack Path Management
An effective cybersecurity strategy requires a clear and comprehensive understanding of how attacks unfold. Read this whitepaper to get the expert insight you need to defend your organization!
Well written article and easy to follow. Thank you for this. Excellent work indeed!
Thanks for the article — this was much clearer than Microsoft’s documentation. I’m sure lots more people will be referring to this as the deprecation date draws near.
I have a question and was hoping you’d be able to answer:
We have a vendor that insists on full_access_as_app.
I’ve taken it upon myself to attempt to add lesser permissions, namely Mail.Send, but I don’t think the app is using Graph because the SMTP config screen requires scope (https://outlook.office365.com/.default) and EWS URL (https://outlook.office365.com/EWS/Exchange.asmx). Is my assessment right?
I’ve also set up the Exchange Online Service Principal, Management Scope and assigned the principal to the Application Mail.Send role.
Test-ServicePrincipalAuthorization shows what I expect. Mailboxes in my -RecipientRestrictionFilter are True, and all others are False. When I run Get-MailboxPermission, I can see the object ID of my ApplicationID from Entra in there with full access and NT AUTHORITY\SELF as the user (I assume this is internal to Exchange Online).
However, SMTP test sends still fail though, with a 403 Unauthorized response from Exchange Online. Is there anything else I can try? It seems like we’re at the mercy of the vendor and their code but I’m wondering if I’m perhaps missing permissions. Since the vendor doc only mentions full_access_as_app, and we’re trying to avoid that, it is hard for me to tell what else is needed. Maybe Exchange Online User.Read.All?
Many thanks Tony for your great article. It was very easy to follow and understand compared to Microsoft official documention. I was able to restrict permissions of one of our app registrations with you instructions. Thanks again!
No worries. A good example is always a solid starting point to helping people understand how technology works.
Hi Tony, very well written article ! question which of the roles Get-ManagementRole | Where-Object {$_.Name -like “Application *”} would map exactly to the full_access_as_app Exchange Web Service permission scope? Would that be Application EWS.AccessAsApp ? I’m also a bit confused about the returned access token (The registered Azure AD app identified in the service principal object has no Graph permissions.). Traditionally when using the client credentials flow I would I would define the https://outlook.office365.com/.default scope so that the access token returned would contain all the app-level permissions the admin has consented to (that is, the roles claim would contain permissions that the requesting app has been given permission to call), now with the RBAC approach one has to remove the consent from the Entra ID app registration… how would that be reflected in the access token then? Thank you !
First, the intention is that you don’t use an all-encompassing permission like EWS.AccessAsApp. Instead, you should use granular, limited permissions to access the information you need. There is no equivalent of the EWS full_access_as_app permission (which Microsoft is seeking to remove ASAP).
The app will only have Graph permissions if they are assigned to the app… what permissions do you mean? Maybe I am not following the question. Perhaps this example of using an app running with a managed identity that uses a limited calendar read permission to access specific mailboxes using RBAC for applications: https://practical365.com/rbac-for-applications-azure-automation/
‘ what permissions do you mean?’ I mean the permissions consented in Entra app registration for the given app. If one adds permissions and provides consent, the returned access token in a client credential flow will have them within its claims for example:
“roles”: [
“full_access_as_app”
],
Now the articel states to remove the permission consent in my case for full_access_as_app. How will the returned token satisfy any permissions if they are removed from the app registration?
The whole point of RBAC for Applications is that the app secures its permissions through Exchange Online rather than Entra ID. What you’re describing is the classic Entra ID flow to grant an access token with permissions listed in the token claims. When RBAC for applications is used, Exchange recognizes the permission granted to the service principal and allows the app to use that permission (in the example I cited, Calendar.Read). The permission does not appear in the access token because Exchange Online controls the access to the mailboxes. Only specific permissions are available with RBAC for Applications and full_access_as_app is not one of these.
Okay, that makes sense. Is there any modification then needed within the application code itself or will this just work fine out of the box?
If your app only needs to use the permissions supported by RBAC for applications, everything should work as if the app is assigned consented Graph permissions by Entra ID. I certainly haven’t hit any major problems that weren’t due to my own ineptitude.
Thank you for clarifying ! really enjoy your posts, keep them coming 😊!
I will do my best to continue to throw light into the shadowy parts of Microsoft 365…
I just wanted to say thanks for this great blog/posts about sending mail via Graph with PowerShell. Its been greatly helpful in some of my efforts to modernize some older workflows.
Keep up the great work!