Entra - Exchange Online

Limit application permissions to a single Exchange Online mailbox

The need to allow access to a shared mailbox hosted in an exchange environment is a request that most exchange admins encounter on a regular basis.

Back in the old days you just gave it full access to the shared mailbox. If an application requested the access you instead created an active directory account (or group managed service account if you were fancy) and gave it the permissions. Maybe you tried to exclude the service account from some default permissions, block the login to specific time slots or you limited the access on an ip address basis and chose a very long and probably very secure password. Good old messy times …

And how do you deal with that challenge in the present? Maybe exclude an service account from conditional access or somehow try to limit the permissions that comes with it like browsing the entra id user and groups or reading public teams? Not to mention the need to assign the correct user license.

In Short: It’s a security nightmare and definitely not the way to go. Service accounts are a bad thing from the past.

And thanks to the Microsoft Graph API there is no need to do so. To access a shared mailbox (or any other mailbox) as a background service or daemon without being a human being you simply create a service principal and assign the needed API application permissions.

Most of you will know that by design application permissions are affecting all objects within a microsoft 365 tenant. So if I choose to add the Mail.ReadWrite API to a service principal I would allow the access to every mailbox in that microsoft 365 tenant. The missing part here are the application access policies. With these you can create a rule that limits the permissions to only a single mailbox or a group of mailboxes. But Microsoft wouldn’t be Microsoft if they would not already replace that feature with another one, namely Roles Based Access Control for (Exchange) Applications. And if you still need the legacy protocols like IMAP, POP or SMTP instead of Graph API, you can use Exchange Online Service Principals instead.

What a wonderful world!

Preperations

PowerShell Modules needed:

First we need a service principal, so lets create one simple App in Entra ID for all three purposes. To connect we also need a client secret oder certificate.

What are Application Access Policies? (deprecated)

Microsoft describes application access policies as follows:

Administrators can use ApplicationAccessPolicy cmdlets to control mailbox access of an app that has been granted any of the following Microsoft Graph application permissions or Exchange Web Services permissions.

Microsoft Graph application permissions:

  • Mail.Read
  • Mail.ReadBasic
  • Mail.ReadBasic.All
  • Mail.ReadWrite
  • Mail.Send
  • MailboxSettings.Read
  • MailboxSettings.ReadWrite
  • Calendars.Read
  • Calendars.ReadWrite
  • Contacts.Read
  • Contacts.ReadWrite

Exchange Web Services permission scope: full_access_as_app.

For more information about configuring application access policy, see the PowerShell cmdlet reference for New-ApplicationAccessPolicy.

Limiting application permissions to specific Exchange Online mailboxes – Microsoft Graph | Microsoft Learn

Microsoft officially announced that application access policies are deprecated and will be replaced by Role Based Access Control for Applications in Exchange Online. I will cover these in the second part of this blog post and instead of ignoring the application access policy I rather include them for completeness.

So to accomplish our goal with application access policies we needed to do the following tasks after creating our previously created app.

Assign the needed and supported Microsoft Graph API application permissions (e.g. Mailbox.ReadWrite) to the app registration.

After that we have to create a mail-enabled security group in exchange online. Mail-enabled security groups are some kind of very dark legacy tech from on premise Exchange and I hope Microsoft will abandon these in the future.

You can create the group via Exchange Admin Center or with the ExchangeOnlineManagement PowerShell Module. The latter ist needed in the next step anyway. Choose what best suites you.

AAP_9eacb92b-xxxx-xxxx-xxxx-00e3c999471d0

I named the group like my application id and used AAP_ as a prefix. After the creation you can add the shared mailbox you want to access to the members of it.

Now we can finally create the application access policy and link the app and the new mail-enabled security group together and make it all work.

New-ApplicationAccessPolicy -AppId "9eacb92b-xxxx-xxxx-xxxx-00e3c999471d0" -PolicyScopeGroupId "AAP_9eacb92b-xxxx-xxxx-xxxx-00e3c999471d0@cloudkreise.de" -AccessRight RestrictAccess -Description "Restrict app access to members of mail-enabled security group."

After that you can test the access to the shared mailbox with the Test-ApplicationAccessPolicy Cmdlet. The AccessCheckResult output will tell you if you are able to access the content of the mailbox.

Test-ApplicationAccessPolicy -AppId "9eacb92b-xxxx-xxxx-xxxx-00e3c999471d" -Identity "sharedmailbox@cloudkreise.de"

AppId : 9eacb92b-xxxx-xxxx-xxxx-00e3c999471d0
Mailbox : Shared Mailbox
MailboxId : 79a72c36-xxxx-xxxx-xxxx-2c2855e5a967
MailboxSid : S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-654753350-xxxxxxxxxx
AccessCheckResult : Granted

Test-ApplicationAccessPolicy -AppId "9eacb92b-xxxx-xxxx-xxxx-00e3c999471d0" -Identity "anothermailbox@cloudkreise.de"

AppId : 9eacb92b-xxxx-xxxx-xxxx-00e3c999471d0
Mailbox : Another Mailbox
MailboxId : c068af42-xxxx-xxxx-xxxx-b14d33962949
MailboxSid : S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-654753350-xxxxxxxxxx
AccessCheckResult : Denied

If you want to try out the next way to limit permissions to a single exchange online mailbox please remove the mail-enabled security group, the assigned Microsoft Graph API Permissions to the app and the application access policy.

What are Roles Based Access Controls for Applications?

Again, I will just quote Microsoft directly:

RBAC for Applications in Exchange Online allows admins to grant permissions to an application that’s independently accessing data in Exchange Online. This grant can be paired with a scope of access (resource scope) to specify which mailboxes an app can access. This feature extends the current RBAC model in Exchange Online and it replaces Application Access Policies.

At the core of this system is the management role assignment configuration, which expresses an admin’s intent to allow a principal to access data. In this case, allowing an app to perform some role against a set of target resources. For example an admin might configure a room booking system with access to calendar data only in specific regions using a Management Scope.

Role Based Access Control for Applications in Exchange Online | Microsoft Learn

Most Exchange admins will get a bit scared the moment they read about management scopes and management roles in exchange, because they are a bit of a hassle, but a very powerful way to granular assign permissions.

In this case we just need three simple commands to create a Exchange Online Service Principal, a new management scope to lock it down to a single or a set of mailboxes and assign the permissions with a management role assignment.

To create a new Exchange Online Service Principal we need our application id and object id from our enterprise app blade. You can then choose a Displayname to better identify the new service principal.

New-ServicePrincipal -AppId "9eacb92b-xxxx-xxxx-xxxx-00e3c999471d0" -ObjectId "21e9d71a-xxxx-xxxx-xxxx-d8eab69d8987" -DisplayName "Role Based Access Control for Application XYZ in Exchange Online"

Next we will create the management scope. I did chose the guid of my shared mailbox and named the management scope according to my application id to better identify its later use. With the RecipientRestrictionFilter we are able to pin it down to exactly that single mailbox.

New-ManagementScope -Name "rbac_9eacb92b-xxxx-xxxx-xxxx-00e3c999471d" -RecipientRestrictionFilter "Guid -eq '24d72ef2-xxxx-xxxx-xxxx-1c66b70dbdfa'"

The third command then creates a new management role assignment and links it to the previously created managemend scope, the app registration and the needed role. So before you execute that command you have to take a look at the list of supported application roles and pick yours with the needed permissions. For my case here I did chose the role “Application Mail.ReadWrite” to get full access to the mailbox.

I you don’t chose a custom name for it it will default to “applicationrole-applicationid” like:

Application Mail.ReadWrite-9eacb92b-xxxx-xxxx-xxxx-00e3c999471d
New-ManagementRoleAssignment -App "9eacb92b-xxxx-xxxx-xxxx-00e3c999471d" -Role "Application Mail.ReadWrite" -CustomResourceScope "rbac_9eacb92b-xxxx-xxxx-xxxx-00e3c999471d"

Thats it. We can now connect and access the desired mailbox via Graph API. To test this we can use the Microsoft Graph PowerShell SDK.

$ClientSecretCredential = Get-Credential -Credential "9eacb92b-xxxx-xxxx-xxxx-00e3c999471d"
Connect-MgGraph -TenantId $TenantID -ClientSecretCredential $ClientSecretCredential

After the successful connect we can try to access the content of our shared mailbox which should look something like this.

Invoke-mgGraphRequest -Method GET 'https://graph.microsoft.com/v1.0/users/sharedmailbox@cloudkreise.de/messages'

Name Value
---- -----
@odata.nextLink https://graph.microsoft.com/v1.0/users/sharedmailbox@cloudkreise.de/messages?%24top=10&%24skip=10
value {AAMkADlmODRLTEwYzMtNDJkZC05ODQwLTRmNzY1ODQwYjlhMwBGAAAAAAAed1oI4qzqRp6e4OV4evKYBwB8Lnr_, AAMkADlmODRRLTEwYzMtNDJkZC05ODQwLTRmNzY1ODQwYjlhMwBGAAAAAAAed1oI4qzqRp6e4OV4evKYBwB8Lnr_…
@odata.context https://graph.microsoft.com/v1.0/$metadata#users('sharedmailbox%40cloudkreise.de')/messages

And if we try to access a mailbox that is not in our scope defined with the RecipientRestrictionFilter we get an access is denied.

Invoke-mgGraphRequest -Method GET 'https://graph.microsoft.com/v1.0/users/OtherMailbox@cloudkreise.de/messages' 
Invoke-MgGraphRequest: GET https://graph.microsoft.com/v1.0/users/OtherMailbox@cloudkreise.de/messages
HTTP/2.0 403 Forbidden
Cache-Control: private
Vary: Accept-Encoding
Strict-Transport-Security: max-age=31536000
request-id: 27a036c3-4ece-4923-95f5-df2431c8a464
client-request-id: 209c5516-caf0-4b44-bd94-87971f149821
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"Germany West Central","Slice":"E","Ring":"4","ScaleUnit":"003","RoleInstance":"FR1PEPF000010A1"}}
Date: Tue, 23 Apr 2024 11:24:28 GMT
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false; charset=utf-8
Content-Encoding: gzip

{"error":{"code":"ErrorAccessDenied","message":"Access is denied. Check credentials and try again."}}

If you migrate from Application Access Policies to Roles Based Access Controls for Applications in Exchange Online please remove the Microsoft Graph API permissions from your app as stated in the FAQ by Microsoft.

If you want to try out the next way to limit permissions to a single exchange online mailbox please remove the created management scope, the Exchange Online Service Principal and the management role assignment.

Use Exchange Service Principals with legacy protocols

Often existing software is not able to use the REST API or Graph API to access mailboxes and still needs protocols like POP3, IMAP and SMTP.

Well … we can use Exchange Online Service Principals to enable applications to access mailboxes via client credentials flow and the use of legacy protocols like SMTP, POP3 and IMAP.

You can use the following Office 365 Exchange Online API permissions:

  • POP.AccessAsApp
  • IMAP.AccessAsApp
  • SMTP.SendAsApp

All you need is to assign these to your app …

create an Exchange Online Service Principal …

New-ServicePrincipal -AppId "9eacb92b-xxxx-xxxx-xxxx-00e3c999471d" -ServiceId "21e9d71a-xxxx-xxxx-xxxx-d8eab69d8987" -DisplayName "Service Principal for Application XYZ in Exchange Online"

ExchangeOnlineServicePrincipal = Get-ServicePrincipal | Where-Object {$_.AppId -eq "9eacb92b-xxxx-xxxx-xxxx-00e3c999471d"}

and add it to the mailbox permissions of your shared mailbox.

Add-MailboxPermission -Identity "sharedmailbox@cloudkreise.de" -User $ExchangeOnlineServicePrincipal.Id -AccessRights FullAccess

Now you can connect your application to the shared mailbox using IMAP or any other mentioned protocol that you gave it the permissions for.

Conclusion

Microsoft offers you more than just one way to authenticate and authorise an application to access mailboxes hosted with exchange online and limit the access to only the needed scope. I hope this summary helps you as it has helped me to fulfil the requests of various stakeholders while complying with safety requirements.

If you don’t need the things we have done here please remove these for security reasons from your tenant.