Primer: Use RBAC for Applications to Control App Use of the Mail.Send Permission

Demise of SMTP AUTH Means Potential Misuse of the Mail.Send Permission

With time slipping away as Microsoft moves to retire basic authentication for the SMTP AUTH client submission protocol from Exchange Online, tenants must update PowerShell scripts that send email using SMTP AUTH and replace the Send-MailMessage cmdlet (which uses basic authentication) with an alternative based on OAuth, In many cases, the easiest way to upgrade is to ruse the Send-MgUserMail cmdlet from the Microsoft Graph PowerShell SDK (or the underlying Graph API request). The cmdlet uses OAuth to secure its connection to an Exchange Online mailbox to submit and send email.

Send-MgUserMail depends on the Graph Mail.Send permission. In its delegated form, the permission allows a message to be sent from the mailbox of the signed-in user. The application form of Mail.Send is a high-profile permission that allows an app to send email from any mailbox. In effect, the Mail.Send application permission allows an app to impersonate any user when sending email. Mail.Read is another high-profile permission that allows apps to read content from any mailbox.

When updating scripts under time pressure, the temptation often exists to take the quick and easy path. In practice, this can lead to administrators granting scripts consent to access mailboxes without guard rails. Fortunately, a solution exists: use RBAC for Applications to restrict app access to selected mailboxes.

RBAC for Applications

Microsoft launched RBAC for Applications in December 2022. The mechanism depends on scripts being executed in app-only mode (including for Azure Automation runbooks) because RBAC for Applications uses apps as the basis for assigning permission to access Exchange Online data. Over three years on, the number of scripts that make unrestricted use of the Mail.Send permission is testament that many script developers are unaware that they should and can restrict access to mailboxes in scripts. It’s possible that a lack of knowledge is the root cause, so let’s explain how to apply RBAC for Applications to a script. Further information is available in Microsoft’s documentation.

As an example, I use the script to report Microsoft 365 service health data via email. An updated version of the script that can run in delegated or app-only mode can be downloaded from the Office 365 for IT Pros GitHub repository.

Create a Registered Application

We need an app for RBAC for Applications to work, so the first task is to create a new registered app, which I called Service Health Notifications (Figure 1). You can see that the app has consent for application permissions to read the organization directory and to read service health and message data. RBAC for Applications only controls access to Exchange Online data. For other data, apps still need consent for Graph (and perhaps other) permissions.

RBAC for Applications means that an app doesn't need the Mail.Send permission.
Figure 1: The registered application and its Graph permissions – no sign of Mail.Send

The app has no consent to use the Mail.Send permission, so a script that connects to the Microsoft Graph using this app cannot send email. RBAC for Applications will grant the necessary access.

Create the Service Principal

The link between RBAC for Applications and the Entra ID app is via a service principal object. This is an Exchange Online service principal rather than the Entra ID service principals that are usually discussed when referring to apps. To create the service principal, we need:

  • The application identifier.
  • The identifier of the Entra ID service principal for the application.
  • The application’s name.

This information can be found by looking up the Entra ID service principal before creating the service principal with the New-ServicePrincipal cmdlet. You’ll need to sign into Exchange Online PowerShell as an administrator before you can create a service principal, management scope, or management role assignment:

$SP = Get-MgServicePrincipal -Filter "displayName eq 'Service Health Notifications'"

New-ServicePrincipal -AppId $SP.AppId -ObjectId $SP.Id -DisplayName $SP.DisplayName

DisplayName                              ObjectId                                                            AppId
-----------                              --------                                                            -----
Service Health Notifications             e6105d20-c4f6-4bc6-bdc7-7a07e6f6e242                                a40e80e0-7e1b-4339-a961-d6ecbc0ec7e1

Create the Management Scope

A management scope identifies a set of mailboxes for RBAC of Applications to assign permissions over. Any of the filterable mailbox properties can be used in a management scope. Our app needs access to send email from a specific mailbox, which we can identify with its display name:

New-ManagementScope -Name "Azure Management Account" -RecipientRestrictionFilter "displayName -eq 'Azure Management Account'"

Before creating the service principal, it’s a good idea to check that the filter returns the correct objects by running the filter with the Get-Recipient cmdlet:

Get-Recipient -RecipientPreviewFilter "displayName -eq 'Azure Management Account'"| Format-Table DisplayName, PrimarySMTPAddress

DisplayName              PrimarySmtpAddress
-----------              ------------------
Azure Management Account Azure.Management.Mailbox@office365itpros.com

Create the Management Role Assignment

A management role assignment connects an app, target mailboxes (scope), and an application permission. In our case, we want to connect the app with the management scope that we just created and the “Application Mail.Send” permission. You can see the set of available permissions supported by RBAC for Applications by running the Get-ManagementRole cmdlet:

Get-ManagementRole | Where-Object {$_.Name -like "Application *"}

The New-ManagementRoleAssignment cmdlet creates the assignment to link the components together. In this example, you can see how the application identifier, permission, and management scope are specified:

New-ManagementRoleAssignment -App $SP.AppId -Role "Application Mail.Send" 
-CustomResourceScope "Azure Management Account"

The assignment is effective immediately. Assuming no errors, the application should now be able to send email from any of the mailboxes identified in the management scope without consent being granted for the Mail.Send permission.

Administrative Unit Support

Our example management scope uses a recipient filter to find the set of mailboxes available to the app. RBAC for Applications also supports the use of Entra ID administrative units to define the sets of resources made available through management scopes and role assignments.

A Simple Fix for Misuse of the Mail.Send Permission

Like anything else, RBAC for Applications takes a little getting used to, but it’s really quite straightforward. App, scope, and assignment combine to make the right permission available to an app for limited Exchange Online resources. That’s so much better than allowing apps to have free rein over mailbox contents. Your CEO might just agree as I’m sure they don’t fancy apps sending mail on their behalf.


Need help to write and manage PowerShell scripts for Microsoft 365, including Azure Automation runbooks? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.

2 Replies to “Primer: Use RBAC for Applications to Control App Use of the Mail.Send Permission”

  1. Does this work on Enterprise Apps we bring in from different Tenants? is this only limited to Application level or delegated as well?

    1. When you use Mail.Send as a delegated permission, you can only send email from a mailbox that the signed in user has access to. You wouldn’t use RBAC for Applications in this scenario.

      RBAC for Applications works against Entra ID service principals, so it works for applications created outside your tenant.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.