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).

Finding app identifiers for RBAC for Applications

Role-based access controls for Applications
Figure 1: Finding app identifiers for RBAC for Applications

As everything happens in PowerShell, you can also fetch the object identifiers and store them in variables.  Here’s what I did:

$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

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
----                           ----              ----------------  ----------------  ----------------  ----------------
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!

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to Practical365.com, Tony also writes at Office365itpros.com to support the development of the eBook. He has been a Microsoft MVP since 2004.


  1. Adam

    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 !

    1. Avatar photo
      Tony Redmond

      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/

      1. Adam

        ‘ 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”: [

        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?

        1. Avatar photo
          Tony Redmond

          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.

          1. Adam

            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?

          2. Avatar photo
            Tony Redmond

            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.

          3. Adam

            Thank you for clarifying ! really enjoy your posts, keep them coming 😊!

          4. Avatar photo
            Tony Redmond

            I will do my best to continue to throw light into the shadowy parts of Microsoft 365…

  2. Andy Jackson

    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!

Leave a Reply