More on service principal permissions in Exchange Online

Few days back I briefly covered the advancements in Exchange Online’s support for service principal objects. In the context of said article, we only talked about Full access permissions, but since service principals are now supported objects in Exchange Online, the question remains which other functionalities support them. So let’s take a deeper look.

First, let’s briefly talk again about creating and managing service principal objects in Exchange Online. Since no process exists to synchronize Azure AD service principal objects to ExODS, you’ll need to create a matching representation of the SP object yourself. In order to do that, you can use the New-ServicePrincipal cmdlet, which by default is available to users assigned the Role Management role. You will need to specify the client ID (application ID) value, via the –AppId parameter, as well as the object ID, via the –ServiceId parameter. Here’s an example:

New-ServicePrincipal -AppId xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -ServiceId xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

If you do not know the values for said parameters, you can obtain them from either the Azure AD blade > App registrations > Overview page, or via PowerShell (Get-MsolServicePrincipal, Get-AzureADServicePrincipal or Get-MgServicePrincipal cmdlet, depending on which module you prefer). Do note that the New-ServicePrincipal does NOT validate the values, so make sure you double- and triple-check them.

To list the current set of service principal objects recognized by ExODS, use the Get-ServicePrincipal cmdlet. Interestingly enough, the RecipientType value for such objects is User, whereas the RecipientTypeDetails is ServicePrinciple (note the spelling!). You cannot however use the Get-User cmdlet to list them, and Get-Recipient with the corresponding filter doesn’t work either. The Set-ServicePrincipal cmdlet can be used to update an existing object, although the only property you can change is the DisplayName. Lastly, to delete an existing service principal object, use the Remove-ServicePrincipal cmdlet.

Now that we covered the basics of managing service principals in Exchange Online, let’s turn into delegating permissions. We already covered the Full access permission in the previous article, but as a refresher you can grant permission via the Add-MailboxPermission cmdlet, by specifying the service principal identifier (display name, appId and serviceId will all work) as value for the -User parameter:

Add-MailboxPermission sharednew -User 2a63aee1-db17-489d-a8ab-d40971066292 -AccessRights FullAccess -AutoMapping $false

You can list, update and remove Full access permissions via the standard cmdlets. The EAC UI will display existing permission entries for service principal objects, but not allow you do add new ones. Interestingly, the new EAC will show you the GUID of the service principal, whereas the classic one returns its Display name. Another interesting thing is that the REST-based cmdlets cannot seem to resolve service principal objects by their DisplayName values, so you have to specify GUIDs instead:

Checking service principal permissions via PowerShellNow that we’ve covered Full access permissions, the question remains is there any other permission types for which service principal objects are supported? Turns out, you can actually grant Send As permissions as well, by leveraging the Add-RecipientPermission cmdlet and providing the service principal identifier as input for the -User parameter:

Add-RecipientPermission sharednew -Trustee 2a63aee1-db17-489d-a8ab-d40971066292 -AccessRights SendAs

Identity : sharednew
Trustee : 2a63aee1-db17-489d-a8ab-d40971066292
AccessControlType : Allow
AccessRights : {SendAs}
IsInherited : False
InheritanceType : None
TrusteeSidString : S-1-5-21-3675944716-2045640655-299186705-56056852

As with Full access permissions, the EAC UI will show you any existing entries, but does not allow you to grant the permissions, so you have to stick to PowerShell. Even PowerShell however does not support granting Send on Behalf of or folder-level permissions, at least currently. So it seems currently only Full Access/Send As permissions are supported. Granting permissions aside, just how one would leverage them is not clear either, the only example of using Full access permissions granted to service principal can be found in this EHLO blog and the script linked therein.

We still have one last set of permissions to cover though, namely granting administrative permissions to the service principal object by assigning RBAC roles, or adding it as a member of a Role Group. Both operations seem to be possible (via PowerShell), but there are some very important caveats to consider. In a nutshell, only specific roles are supported, as we will see below.

For now, let’s see what happens if we try to create a direct role assignment to a service principal object. We can select a random role, such as say “Federated Sharing”, and try to assign it to the SP via the New-ManagementRoleAssignment cmdlet. Doing so will result in the following error message:

New-ManagementRoleAssignment -Role "Federated Sharing" -User 2a63aee1-db17-489d-a8ab-d40971066292
Write-ErrorMessage : |System.ArgumentException|Couldn't assign role "Federated Sharing" to the service principal recipient "2a63aee1-db17-489d-a8ab-d40971066292", please specify a service principal role.

So while the process fails, the error message itself gives us a clue. Apparently, few months back Microsoft introduced a new type of role, a Service principal role, and a corresponding parameter was added to all existing roles, namely IsServicePrincipalRole. This is illustrated by the following cmdlet

Get-ManagementRole | select Name,IsServicePrincipalRole,WhenCreatedUtc | sort Name

Name IsServicePrincipalRole WhenCreatedUTC
---- ---------------------- --------------
Address Lists False 19/10/2013 08:58:08
Application Calendars.Read True 21/03/2022 22:52:15
Application Calendars.ReadWrite True 21/03/2022 22:52:15
Application Contacts.Read True 21/03/2022 22:52:15
Application Contacts.ReadWrite True 21/03/2022 22:52:15
Application EWS.AccessAsApp True 21/03/2022 22:52:15
Application Exchange Full Access True 21/03/2022 22:52:15
Application Mail Full Access True 21/03/2022 22:52:15
Application Mail.Read True 21/03/2022 22:52:15
Application Mail.ReadBasic True 21/03/2022 22:52:15
Application Mail.ReadWrite True 21/03/2022 22:52:15
Application Mail.Send True 21/03/2022 22:52:15
Application MailboxSettings.Read True 21/03/2022 22:52:15
Application MailboxSettings.ReadWrite True 21/03/2022 22:52:15
ApplicationImpersonation False 19/10/2013 08:58:08

I’ve truncated the output above, but since all the SP roles (the ones with IsServicePrincipalRole = True) have names starting with “Application”, we now have the full list of such roles. Interestingly, most of the roles seem to correspond to specific Graph permissions, such as Mail.Read. Another interesting observation is that a new Type property has been introduced for management role entries, with the corresponding parameter for the Get-ManagementRoleEntry cmdlet. In other words, you can also list all “ApplicationPermission” management role entries via the Get-ManagementRoleEntry cmdlet, as follows:

Get-ManagementRoleEntry "*\*" -Type ApplicationPermission | select Name, Role, Type, Parameters,WhenCreatedUTC

Name : Impersonate-ExchangeUser
Role : ApplicationImpersonation
Type : ApplicationPermission
Parameters : {}
WhenCreatedUTC : 19/10/2013 08:58:08

Name : Mail.Read
Role : Application Mail.Read
Type : ApplicationPermission
Parameters : {}
WhenCreatedUTC : 21/03/2022 22:52:15

But I digress. Back to assigning admin permissions to service principal objects now. As we established above, direct role assignment is only possible for specific roles, such as the “Application Mail.ReadBasic” one:

New-ManagementRoleAssignment -Role "Application Mail.ReadBasic" -User 2a63aee1-db17-489d-a8ab-d40971066292

For all non-SP roles, direct assignment will result in an error, as we already saw above. Turns out however that you can assign service principal objects as a member of a role group that contains non-SP roles. While the end result is disappointing, it’s still interesting to know that this is possible. Here’s how to assign a role group to a service principal object (as usual we have to use PowerShell):

Add-RoleGroupMember -Identity test -Member 2a63aee1-db17-489d-a8ab-d40971066292

where “test” is the identity of an already existing custom Role Group in my tenant. Usually, one can use the –GetEffectiveUsers switch of the Get-ManagementRoleAssignment cmdlet to “expand” role group based assignments and get a list of the corresponding user objects. This however does not seem to work with service principal objects, so you have to employ workarounds. Here are some examples:

C:\> Get-ManagementRoleAssignment "Mail Recipients-test" -GetEffectiveUsers | select User,EffectiveUserName,AssignmentMethod,RoleAssigneeType

User EffectiveUserName AssignmentMethod RoleAssigneeType
---- ----------------- ---------------- ----------------
test All Group Members Direct RoleGroup
bathroom bathroom RoleGroup RoleGroup
2a63aee1-db17-489d-a8ab-d40971066292 2a63aee1-db17-489d-a8ab-d40971066292 RoleGroup RoleGroup

C:\> Get-ManagementRoleAssignment -RoleAssignee 2a63aee1-db17-489d-a8ab-d40971066292 -GetEffectiveUsers
Write-ErrorMessage : |Microsoft.Exchange.Configuration.Tasks.ThrowTerminatingErrorException|A management role assignment policy, user, or security group couldn't be found with the identity
"2a63aee1-db17-489d-a8ab-d40971066292".

C:\> Get-ManagementRoleAssignment -GetEffectiveUsers | ? {$_.User -eq "2a63aee1-db17-489d-a8ab-d40971066292"}

DataObject : Mail Recipients-test
User : 2a63aee1-db17-489d-a8ab-d40971066292
AssignmentMethod : RoleGroup
EffectiveUserName : 2a63aee1-db17-489d-a8ab-d40971066292
AssignmentChain : {test}
RoleAssigneeType : RoleGroup
RoleAssignee : test
Role : Mail Recipients

Unfortunately, while the above examples show that it is indeed possible to add service principal objects as members of Role Groups, the permissions corresponding to any of the roles contained within said assignments are not available for use with the service principal login. One would hope that this method would finally allow us to use Role Groups for CBA scenarios for ExO PowerShell. However, this will result in an error as shown below:

The role assigned to application xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx isn’t supported in this scenario. Please check online documentation for assigning correct Directory Roles to Azure AD Application for EXO App-Only Authentication.

So in summary, Exchange Online now supports service principal objects. Those are objects of RecipientType User, with RecipientTypeDetails value set to ServicePrinciple. A new set of cmdlets has been introduced to manage them, namely *-ServicePrincipal. Service principal objects can be used to delegate Full Access and Send As permissions in app-only authentication scenarios, such as OAuth authentication via POP/IMAP. Folder level, delegate and send on behalf of permissions are not supported.

When it comes to admin permissions, service principal objects can of course be granted Azure AD admin roles. Within ExODS however, only a subset of the management roles are available for direct assignment – those designated with IsServicePrincipalRole value of True. And while you can add service principal objects as members of a group, including Role Groups, do not expect the corresponding cmdlets and functions to become available to the service principal. Well, perhaps any of the “Application *” ones would work 🙂

Before closing, it’s worth mentioning that you can use yet another “hack” to look at the full properties of the service principal object. Whereas the Get-ServicePrincipal cmdlet exposes only a handful of properties via the Deserialized.Microsoft.Exchange.Data.Directory.Management.ServicePrincipal object, you can get a lot more by fetching the Deserialized.Microsoft.Exchange.Data.Directory.Management.ReducedRecipient object as follows.

C:\> Add-DistributionGroupMember secgrp -Member 2a63aee1-db17-489d-a8ab-d40971066292 -BypassSecurityGroupManagerCheck

C:\> $res = Get-DistributionGroupMember secgrp | ogv -PassThru

C:\> $res | select Name, ExternalDirectoryObjectId, DisplayName,HiddenFromAddressListsEnabled,RecipientType,RecipientTypeDetails,SamAccountName,IsValidSecurityPrincipal,EmailAddresses

Name : 2a63aee1-db17-489d-a8ab-d40971066292
ExternalDirectoryObjectId : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
DisplayName : Service Principal for ExOPS app
HiddenFromAddressListsEnabled : False
RecipientType : User
RecipientTypeDetails : ServicePrinciple
SamAccountName : $K0NEL1-6B0JEE9GI8PT
IsValidSecurityPrincipal : False
EmailAddresses : {}

Few things to note here. The ExternalDirectoryObjectId value will match the clientID/appID of the parent application. No email addresses will be present and no Alias either, so while HiddenFromAddressListsEnabled is set to False, don’t expect to see service principal objects in the GAL. Interestingly, IsValidSecurityPrincipal is set to False, which is strange for an object that can be used for delegating permissions. But hey, it’s still in the early days I suppose.

I’ll circle back to this article at a later point to update it with new information as it becomes available. In any case, some more details on granting permissions to service principal objects and the corresponding use cases can be found in the official documentation.

13 thoughts on “More on service principal permissions in Exchange Online

  1. Florian says:

    Adding Service Pricipals to (custom) RBAC roles should work, as documented in the Exchange Blog post.
    https://techcommunity.microsoft.com/t5/exchange-team-blog/notes-from-the-field-using-app-only-authentication-with/ba-p/3690083

    However, when following the steps, I also receive the error you got:
    The role assigned to application xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx isn’t supported in this scenario. Please check online documentation for assigning correct Directory Roles to Azure AD Application for EXO App-Only Authentication.

    Did you manage to resolve that?

    Reply
    1. Vasil Michev says:

      Seems to work fine here now, I can connect with just a Role Group assigned to the SP object, and the set of cmdlets I’m able to run is quite restricted.
      I’ll do a proper writeup in the coming days.

      Reply
  2. Roel van der Wegen says:

    Which EXO module version are you using for this?

    The New-ServicePrincipal cmdlet doesn’t appear to exist in version 3.0.1-Preview1 or 3.0.0.

    Reply
    1. Vasil Michev says:

      Check your permissions 🙂

      Get-ManagementRole -Cmdlet New-ServicePrincipal
      
      Name            RoleType
      ----            --------
      Role Management RoleManagement
      Reply
  3. lt says:

    Your article helped me a lot, but some questions confused me. When I try to assign roles(actually the role is Application EWS.AccessAsApp) to the application(I use New-ManagementRoleAssignment mentioned in the article), I use this application through EWS to get mail. However, the returned result is 403 server forbidden. It seems that the application does not have permission. What can I do to use these permissions?

    Reply
      1. lt says:

        Thanks for your reply! I have tried the method you mentioned before. What really confuses me is what use are these roles(like Application Mail.ReadBasic) that can only be assigned to applications in Exchange? Or is it still unavailable?

        Reply
        1. Vasil Michev says:

          Those roles are specific to Graph API operations.

      2. lt says:

        After I use New-ManagementRoleAssignment to assign roles(Application Mail.ReadBasic) for the Azure AD application, the application still cannot use the graph api. Do you mean that these roles are internal and used in the graph api to access exchange, but cannot be used by ordinary users?

        Reply
        1. Vasil Michev says:

          You cannot do this purely on Exchange side, the AAD application still needs to have the corresponding permission added and consented to.

  4. Alon Armoza says:

    Amazing article! thank you so much for taking the time to write this up, I’m sure it helps a lot of people.

    While reading this I had an idea and I’m wondering if you tried it. One of the application permissions in Exchange Online is the “ApplicationImpersonation” one. Do you think this can be used to grant a user the necessary permissions and then grant the SP impersonation rights to that user? while looking at this I could only find documentation for impersonation in c# (https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-add-appointments-by-using-exchange-impersonation). But doing this in Powershell might be amazing.

    What do you think?

    Reply
    1. Vasil Michev says:

      Sure, EWS impersonation can be restricted to specific mailboxes only. Depending on the type of permissions you are using, you can either:

      1) Application permissions model: Configure an Application access policy: https://practical365.com/new-application-access-policies-extend-support-for-more-scenarios/
      2) Delegate permissions model: Assign the user under whose identity your app is running to the ApplicationImpersonation role, with a custom management scope restricted to just the mailboxes you want him to be able to access

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

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