RBAC for Exchange Online Mailboxes
Tags: azure, m365, security
Published: 2023-07-14 09:41:19

If you are writing solutions that target M365 services, you need to plan for permissions that your application will have to the M365 resources. 

It doesn't really matter if you are using Azure Services with Managed Identity, or a custom application with Service Principal; you have to set API permissions.

## Architecture design

Let's imagine an application that needs to synchronize information between "Events" SharePoint Online list and an Exchange Online mailbox belonging to a group responsible for organizing these events. Invitations are sent to members of the group and replies need to be recorder in the list. 

This means that the application needs to `Read` and `Write` to both: the SharePoint list and the Exchange calendar:

![Logical Design](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hyxr9k5kvyj2b5k1hkr.png)

The business logic is implemented using **Azure Function**, with **System-Managed Identity**. 
Some tests are performed using **Postman**, with a **Service Principal**. 

### API Permissions

I'm a staunch believer in a minimum-required permissions and would never implement an application that has indiscriminate access to all resources using Graph API (unless specifically required, that is).  

SharePoint offers `Sites.Selected` **Application API permissions** that together with `Grant-PnPAzureADAppSitePermission` allow granting `Read/Write` or `FullControl` (with `Set-PnPAzureADAppSitePermission `) permissions to the application identity. 

But what about the **mailboxes**? The **API permissions** for **Calendars** grant access to all the mailboxes. That's a bit of overreach, _innit_?

![API permissions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wa4j0zpsg7wuw9vp8wta.png)

Luckily there's a way to set granular access rights on the Exchange Online mailboxes as well!

There are two ways to do it: 
- Access Policy for Applications in Exchange Online: [Limiting application permissions to specific Exchange Online mailboxes - Microsoft Graph](https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access)
- RBAC: [Role Based Access Control for Applications in Exchange Online (Preview)](https://learn.microsoft.com/en-us/exchange/permissions-exo/application-rbac)

> **However**, _"App Access Policies will soon be replaced by Role Based Access Control for Applications."_ [New-ApplicationAccessPolicy (ExchangePowerShell)](https://learn.microsoft.com/en-us/powershell/module/exchange/new-applicationaccesspolicy?view=exchange-ps)

## Setting RBAC

> The procedure requires [Exchange Online PowerShell](https://learn.microsoft.com/en-us/powershell/exchange/connect-to-exchange-online-powershell?view=exchange-ps) 

Let's use the **Service Principal** created for Postman.

In order to retrieve the information required for setting RBAC, you need to find out the **Object and Application IDs** belonging to the **Enterprise Application** and not the App Registration. Click on the App name under "**Managed application in local directory**"

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0izg6k8cpj6cjcz7gwkt.png)

You will be redirected to "Enterprise Application" page displaying the IDs you need to use:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hgeucc6b4vhhikbo0axf.png)

### Exchange Online Service Principal

Next, we need to create a Service Principal. Why?
> In **Exchange Online**, **service principals are references to** the service principals in **Azure AD**. To assign Exchange Online role-based access control (RBAC) roles to service principals in Azure AD, you use the service principal references in Exchange Online. 

```powershell
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -UserPrincipalName

New-ServicePrincipal `
    -AppId f3776ca2-2d7c-48f8-9e3c-7xxxxxxxxxxx `
    -ObjectId b7cb2398-1e91-4a8b-95b8-xxxxxxxxxxxxx `
    -DisplayName "Postman"
```

### Exchange Online Management Scope

The [Role Based Access Control for Applications in Exchange Online (Preview)](https://learn.microsoft.com/en-us/exchange/permissions-exo/application-rbac) proposes two resource scopes for defining who has the RBAC: 

- Management Scopes and 
- Administrative Units

You have to go with **Management Scopes** to use the **Managed Identity/Service Principals**. Administrative units don't allow these.

```powershell
New-ManagementScope `
    -Name "My Scope" `
    -RecipientRestrictionFilter "Alias -eq 'mailboxname' "
```

###  Exchange Online Management Role Assignment 

And now we can grant the Service Principal the RBAC to the mailbox:

```powershell
New-ManagementRoleAssignment `
    -App "f3776ca2-2d7c-48f8-9e3c-7xxxxxxxxxxx" `
    -Role "Application Calendars.ReadWrite" `
    -CustomResourceScope  "My Scope"
```

### Did it work?

```powershell
Test-ServicePrincipalAuthorization `
    -Identity "f3776ca2-2d7c-48f8-9e3c-7xxxxxxxxxxx" `
    -Resource mailboxname@tenantname.onmicrosoft.com
```
> Did you know? You don't have to build the Graph API requests in Postman yourself. You can fork the [Microsoft Graph](https://www.postman.com/microsoftgraph/workspace/microsoft-graph/collection/455214-085f7047-1bec-4570-9ed0-3a7253be148c/fork) collection and focus on the fun stuff. 
[Use Postman with the Microsoft Graph API](https://learn.microsoft.com/en-us/graph/use-postman)

### Limitations

- Applications can't become member of a Role Group.
- **Application roles can only be assigned to Service Principals.**
- Application roles can't be copied or derived.
- Exclusive management scopes don't restrict app access.
- **Changes to app permissions are subject** to cache maintenance that varies** between 30 minutes and 2 hours **depending on the app's recent usage. When testing configurations, the test command bypasses this cache. An app with no inbound calls to APIs will have its cache reset in 30 minutes, whereas an actively used app will keep its cache alive for up to 2 hours.

### Full script

```powershell
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -UserPrincipalName

$scopeName=""
$mailboxName=""
$tenantName=""
$appName=""
$appId=""
$objId=""

New-ServicePrincipal -AppId $appId -ObjectId $objId -DisplayName $appName
New-ManagementScope -Name $scopeName -RecipientRestrictionFilter "Alias -eq $mailboxName "
New-ManagementRoleAssignment -App $appId -Role "Application Calendars.ReadWrite" -CustomResourceScope $scopeName

Test-ServicePrincipalAuthorization -Identity $appId  -Resource "$mailboxName@$tenantName.onmicrosoft.com"

```

[Find the permissions required to run any Exchange cmdlet](https://learn.microsoft.com/en-us/powershell/exchange/find-exchange-cmdlet-permissions?view=exchange-ps)