ExO RBAC improvements #2: Support for administrative units

Administrative units in Azure AD are the rough equivalent of an OU, and have been around for years. They are a cornerstone of Azure AD’s RBAC model, and as such we’ve covered them extensively over the years. As Microsoft is slowly ramping up support for AUs across various workloads and endpoints, their role continues to grow in importance. In this article, we will examine how Exchange Online supports Administrative units and highlight some scenarios they can be useful for.

As Administrative units are an Azure AD concept, their creation and management remains an Azure AD operation (nowadays you can use either the Azure AD blade, PowerShell or the Graph API). In order to facilitate their usage within Exchange however, Microsoft has introduced the Get-AdministrativeUnit cmdlet, which you can use to list all available AUs within the tenant. Here’s an example:


Name DisplayName
---- -----------
18560689-d221-4f88-a713-17e973cf6498 New AU
3cc9eefb-c0ab-4eb3-aeee-95f7da2ba493 Domain
4702b0dc-3ca5-4046-bf46-363a50ecb743 Domain-based
6ee4baa4-f864-4467-9cd2-f0832a323546 Shared mailboxes only
b42de8c9-7062-4cc0-8c85-f6ae79bdb839 Bulgaria users

As evident from the output above, the Name (as well as Identity/Id) value for the corresponding object, which is of the Deserialized.Microsoft.Exchange.Data.Directory.SystemConfiguration.ADAdministrativeUnit type, matches its ExternalDirectoryObjectId value. Not much else is revealed even after examining the full set of properties for the object. After all, AUs are simply a container for objects, so you will only ever need their identifier and the list of members. Speaking of which, Exchange Online does not expose any direct cmdlet to list members of an AU, however you can use a -RecipientPreviewFilter query to get the list. As usual, you will need to obtain the DistinguishedName value first, and then pair it with the AdministrativeUnits keyword. Here’s an example:

$dn = (Get-AdministrativeUnit -Identity 18560689-d221-4f88-a713-17e973cf6498).DistinguishedName

Get-Recipient -RecipientPreviewFilter "AdministrativeUnits -eq '$dn'"

Name RecipientType
---- -------------
shared UserMailbox

If we check the AU membership on Azure AD side, we will see the exact same list of members returned. Technically, you can also use the same method to report on membership of AUs with dynamic membership rules. Things of course get more interesting when we have other object types added as members of the AU, as Exchange Online cannot recognize devices and such. And as we’ve discussed in several articles already, even regular “user” objects are not returned by default. Overall, if you want a proper list, it’s best to check on Azure AD side, as the Exchange methods will only return a list of objects supported by Exchange. Then again, those are the objects you will be configuring permissions for, so that’s still a valuable information.

Now that we know how to list AUs in Exchange, as well as their members, how do we go about leveraging them for the purposes of RBAC? Basically, we have two options. The first, and least recommended one, is to create a management scope based on an AdministrativeUnits query, much like in the examples we used above. Here’s an example of how to do that:

$dn = (Get-AdministrativeUnit -Identity 18560689-d221-4f88-a713-17e973cf6498).DistinguishedName

New-ManagementScope -Name AUbased -RecipientRestrictionFilter "AdministrativeUnits -eq '$dn'"

The second method, and the one actually recommended by Microsoft, is to leverage the newly introduced -RecipientAdministrativeUnitScope parameter for the New-ManagementRoleAssignment cmdlet. As the name suggests, you can use said parameter to directly scope the newly created management role assignment to just recipients that are members of a given AU, where you can reference the AU in question by its name or objectID (as in, no need to use the DistinguishedName value). At least in theory, as in my tests, using the AU’s display name as input resulted in errors and I had to use the objectID instead. Still, the objectID is easier to handle than the DistinguishedName 🙂

With this information at hand, here’s how to configure an AU-scoped role assignment. In the examples below, we will grant the “User options” management role to a specific user (“gosho”), and scope the management role assignment to just members of the “Bulgaria users” Administrative unit. The following cmdlets can be used:

#Referencing the AU by display name seems to result in an error
New-ManagementRoleAssignment -User gosho -Role "User options" -RecipientAdministrativeUnitScope "Bulgaria users"

#You can pass the object however
New-ManagementRoleAssignment -User gosho -Role "User options" -RecipientAdministrativeUnitScope (Get-AdministrativeUnit "Bulgaria users")

#Or just use the objectID
New-ManagementRoleAssignment -User gosho -Role "User options" -RecipientAdministrativeUnitScope "b42de8c9-7062-4cc0-8c85-f6ae79bdb839"

As detailed in our previous article, you can also use Administrative units to create scoped management role assignments for applications. The syntax is the same, though you will have to use the -App parameter instead of -User, and specify one of the supported SP-assignable management roles. Here’s an example:

New-ManagementRoleAssignment -App 2a63aee1-db17-489d-a8ab-d40971066292 -Role "Application Mail.ReadBasic" -RecipientAdministrativeUnitScope (Get-AdministrativeUnit "Bulgaria users")

To list all existing management role assignments scoped to administrative units, use the -RecipientWriteScope parameter of the Get-ManagementRoleAssignment cmdlet:

Get-ManagementRoleAssignment -RecipientWriteScope AdministrativeUnit

Name Role RoleAssigneeName RoleAssigneeType AssignmentMethod EffectiveUserName
---- ---- ---------------- ---------------- ---------------- -----------------
User Options-gosho User Options gosho User Direct gosho

The Get-ManagementRoleAssignment cmdlet also supports a -RecipientAdministrativeUnitScope parameter, which in theory should allow us to filter the set of management role assignments by a specific AU. Unfortunately, using the parameter currently seems to always result in an error, regardless of the value provided for the AU identifier. An alternative approach to achieve the same task is to leverage client-side filters, based on the value of the CustomRecipientWriteScope property, which will contain the AU’s identity. Here’s an example:

Get-ManagementRoleAssignment -RecipientWriteScope AdministrativeUnit | ? { $_.CustomRecipientWriteScope -eq "b42de8c9-7062-4cc0-8c85-f6ae79bdb839" }

Name Role RoleAssigneeName RoleAssigneeType AssignmentMethod EffectiveUserName
---- ---- ---------------- ---------------- ---------------- -----------------
User Options-gosho User Options gosho User Direct gosho

Note that even though the Get-ManagementRoleAssignment cmdlet supports direct queries based on the CustomRecipientWriteScope value, they also seem to always throw an error, at least currently.

Lastly, if order to test a given AU-scoped management role assignment, all you need to do is login with the corresponding user and try to run one of the allowed cmdlets against an object within the AU scope. Compare the results with running the cmdlet against an object outside of the AU scope and you will know whether the AU-scoped management role assignment works as expected. And for application-based management role assignments, you can leverage the Test-ServicePrincipalAuthorization cmdlet, as detailed in our previous article.

In summary, the Exchange Online RBAC model now has support for scoping management role assignments based on membership of Azure AD administrative units. This is a welcome addition, which brings the ExO and Azure AD RBAC models closer together, and enables additional scenarios. Going forward, customers can use an unified AU-based scoping approach for restricting both Azure AD and Exchange Online operations. That said, good old management scopes in Exchange Online remain supported and can still help you address some corner scenarios, such as where you want to scope the objects based on the value of some Exchange-specific attribute (which is not exposed in Azure AD).

Going forward, we’re likely see administrative units incorporated in the Exchange Admin Center, and hopefully, an UI-based approach for managing (or at the very least viewing) management role assignments. Stay tuned!

Posted in Azure AD, Exchange Online, Microsoft 365, Office 365, PowerShell | Leave a comment

ExO RBAC improvements #1: Limiting application access

Exchange has long been the prime example of a workload with granular and robust permissions model. Thus, it comes as no surprise that the Azure AD role-based access control (RBAC) model follows most of the principles introduced by Exchange. With all its customizability however, the RBAC model had some issues addressing the application permissions model introduced by the Graph API, where the service principal, under which the various operations were being executed, gains unscoped, directory-wide permissions.

To address such scenarios, Exchange Online introduced support for application access policies back in 2019. Later on, the EWS application permissions scenario was also addressed. Other workloads added their own implementations of similar controls, such as the Sites.Selected method for SharePoint Online and the resource-scoped consent model for Teams, to an extent. Yet, such implementations are not without their own issues, and in the case of Exchange certain limitations were quickly identified. So instead of making further investments in application access policies, Microsoft decided to bring the application experience as part of the existing Exchange RBAC model.

Meet Role Based Access Control for Applications in Exchange Online, currently in public preview. In a nutshell, the feature allows you to treat application access in a manner similar to how you assign user permissions. To that end, you need to decide on the set of permissions needed (the Management Role), the principal to grant them to (a Service principal object in this case) and the set of objects against said permissions will be valid (the Management Scope). The new bits here are support for service principal objects, i.e. the object representing an Azure AD integrated app within your directory, and the introduction of new, service principal specific management roles.

Incidentally, we’ve already covered all those new bits individually. At the time I wrote said article, management role assignment was not yet available for SP objects, but I speculated this will be coming in the future. And so, the time has come. For the time being, you will still need to manually create a matching service principal object for any application you plan to restrict access on, by means of using the New-ServicePrincipal cmdlet. Going forward, Microsoft is promising that this step will no longer be needed. Here’s an example on how to use the cmdlet. 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:

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

The other part of the puzzle is the set of Management Roles you can assign. Microsoft has created a set of such roles, corresponding to the (most used) Graph API permissions. You can obtain a list of all supported roles by filtering them via the IsServicePrincipalRole value of True. Alternatively, you can look for all roles with the “Application” prefix. Do note that the list does not include all application permissions scopes available in the Graph!

Get-ManagementRole | ? {$_.IsServicePrincipalRole} | select Name,IsServicePrincipalRole,Description | sort Name

Name IsServicePrincipalRole Description
---- ---------------------- -----------
Application Calendars.Read True Allows the app to read events of all calendars without a signed-in user
Application Calendars.ReadWrite True Allows the app to create, read, update, and delete events of all calendars without a signed-in user
Application Contacts.Read True Allows the app to read all contacts in all mailboxes without a signed-in user
Application Contacts.ReadWrite True Allows the app to create, read, update, and delete all contacts in all mailboxes without a signed-in user
Application EWS.AccessAsApp True Allows the app to use Exchange Web Services with full access to all mailboxes
Application Exchange Full Access True Without a signed-in user: Allows the app to create, read, update, and delete email in all mailboxes as well as send mail as any user. A...
Application Mail Full Access True Allows the app to create, read, update, and delete email in all mailboxes as well as send mail as any user without a signed-in user
Application Mail.Read True Allows the app to read email in all mailboxes without a signed-in user
Application Mail.ReadBasic True Allows the app to read email except the body, previewBody, attachments, and any extended properties in all mailboxes without a signed-i...
Application Mail.ReadWrite True Allows the app to create, read, update, and delete email in all mailboxes without a signed-in user. Does not include permission to send...
Application Mail.Send True Allows the app to send mail as any user without a signed-in user
Application MailboxSettings.Read True Allows the app to read user's mailbox settings in all mailboxes without a signed-in user
Application MailboxSettings.ReadWrite True Allows the app to create, read, update, and delete user's mailbox settings in all mailboxes without a signed-in user

After this introduction/refresher, how do we go about replacing an existing application access policy with a matching role assignment? The task boils down to determining the set of objects the application access policy was scoped to, creating a matching management scope and creating a management role assignment for the corresponding service principal. If you are not using application access policies but only interested in creating a management role assignment for a SP, just skip the next paragraph. The process remains the same, we simply need to provide the required information, instead of “borrowing” it from the properties of an existing application access policy.

Let’s start by looking at the application access policy details. The Identity property gives us the ClientID of the application, as well as the object to which it’s scoped. The only other bit of information we need is the type of policy, determined by the value of the AccessRight property.


ScopeName : USG
ScopeIdentity : USG
Identity : 923712ba-352a-4eda-bece-09d0684d0cfb\ae23ad05-9b02-4946-a906-78b7a0757f5f:S-1-5-21-3675944716-2045640655-299186705-9086853;9e629d33-d655-440c-89af-15738e59e667
ScopeIdentityRaw : S-1-5-21-3675944716-2045640655-299186705-9086853;9e629d33-d655-440c-89af-15738e59e667
Description : Test for app permissions
AccessRight : DenyAccess

We can use the New-ServicePrincipal cmdlet to create a matching object within ExODS (see example above).

Next, we need a management scope. Few notes are due here. With application access policies, the scope was either a single mailbox/mail user object or a set of objects, designated by their membership of a mail-enabled security group. Using “standard” Exchange management scopes, we have a lot more flexibility as you can use a variety of properties and operators to build the recipient filter. If you simply want to match the scope of the application access policy, use the MemberOfGroup property as follows:

$dn = (Get-Recipient 9e629d33-d655-440c-89af-15738e59e667).DistinguishedName

Get-Recipient -RecipientPreviewFilter "MemberOfGroup -eq '$dn'"

Name RecipientType
---- -------------
vasil UserMailbox
HuKu UserMailbox

where we’ve used the GUID obtained from the application access policy configuration. The Get-Recipient cmdlet can be used to “resolve” said GUID to the matching object within the directory, and after that we can leverage its DistinguishedName value to prepare the Recipient filter.

You might have noticed however that the application access policy was configured in “deny” mode, i.e. it was preventing the application from performing any operations against members of the specified group, while all other objects within the directory were valid targets. Thus, we need to amend our management scope query to exclude members of this group, i.e. use the “opposite” filter query:

Get-Recipient -RecipientPreviewFilter "MemberOfGroup -ne '$dn'"

Once you are satisfied with the set of object returned by the query, proceed with creating the matching management scope:

New-ManagementScope -Name "Exclude members of USG" -RecipientRestrictionFilter "MemberOfGroup -ne '$dn'"

Name ScopeRestrictionType Exclusive RecipientRoot RecipientFilter
---- -------------------- --------- ------------- ---------------
Exclude members of USG RecipientScope False MemberOfGroup -ne 'CN=USG,OU=michev.onmicrosoft.com,OU=Microsoft Exchange Hosted Organizations,DC=EURPR03A001,DC=prod,DC=outlook...

It’s worth mentioning that only direct members of the group will fall under the scope of the filter (or out of it, when we use the -ne operator). In other words, nested groups are not supported. You can however use regular distribution groups and Microsoft 365 Groups in addition to mail-enabled security groups. In addition, you can create a scope based on membership of an administrative unit, but we will cover this in another article.

With that, we have all the building blocks to create a management role assignment that will restrict access for the given application. We have an object representing the application and the set of objects against which access will be granted. The last thing to decide on is what permissions the app will get, in other words which Management role(s) to assign to it. While in the case of application access policies this was configured entirely on Azure AD side, with the extensions to the Exchange RBAC model you can control the set of permissions independently, by assigning one (or more) of the service principal roles we listed above.

The management role assignment is done by executing the New-ManagementRoleAssignment cmdlet. Use the newly introduced –App parameter to specify the service principal object and the –Role parameter to assign the corresponding management role. The –CustomResourceScope parameter is optional one and serves to designate the management scope, if you plan to use one. Without it, the newly created management role assignment will allow the application to act upon any object within the tenant. Combining those, we can now create the new role assignment:

New-ManagementRoleAssignment -App 2a63aee1-db17-489d-a8ab-d40971066292 -Role "Application Mail.ReadBasic" -CustomResourceScope "Exclude members of USG"

In effect, we have mirrored the behavior of the existing application access policy, which restricted the given app to act on every object within the directory, excluding members of the specified group. In addition we’ve ensured that only specific Graph API operations can be executed, independent of any permissions the app has been granted on Azure AD side. If we want to provide the app with unrestricted access, we can create a management role assignment for the Application Exchange Full Access role instead and remove the management scope, if needed.

It’s worth clarifying that the Exchange RBAC permissions do not override application access policies, thus you need to carefully consider the settings you configure. What Microsoft suggests (and I concur with) is to remove the application access policy once a matching role assignment has been created. If both an application access policy and management role assignment are in effect for the same app, the resulting experience can be a bit confusing. In a nutshell, both sets of restrictions are applied in a logical OR configuration, as detailed in the official documentation.

Once the new role assignment is created, it’s worth running some additional checks to ensure everything is configured as it should be. The best approach would be to test the application itself, but Microsoft has also provided a new cmdlet that should help diagnose some issues, namely Test-ServicePrincipalAuthorization. You run the cmdlet by providing the id of the service principal you want to test, and optionally a resource to test against, such as a mailbox name. Here are some examples:

#Resource not in scope
Test-ServicePrincipalAuthorization -Identity 2a63aee1-db17-489d-a8ab-d40971066292 -Resource vasil

RoleName GrantedPermissions AllowedResourceScope ScopeType InScope
-------- ------------------ -------------------- --------- -------
Application Mail.ReadBasic Mail.ReadBasic Exclude members of USG CustomRecipientScope False

#Resource in scope
Test-ServicePrincipalAuthorization -Identity 2a63aee1-db17-489d-a8ab-d40971066292 -Resource shared

RoleName GrantedPermissions AllowedResourceScope ScopeType InScope
-------- ------------------ -------------------- --------- -------
Application Mail.ReadBasic Mail.ReadBasic Exclude members of USG CustomRecipientScope True

In summary, the Exchange Online’s RBAC model has been extended to cover some application-specific scenarios, by means of adding support for delegating management roles to service principal objects. Not only this approach unifies the application scenario with the existing RBAC controls, it also addresses the shortcomings of the application access policies feature, which was our only option to restrict application access until now, and adds some additional possibilities on top of it. While the feature is still in preview and there are some rough edges, Microsoft is already planning to address them, and most importantly provide an UI. And, unlike some of the recent announcements on Microsoft side, we’re getting all this for free, without requiring any add-on licenses or new SKUs. Amen.

One last thing before closing – you cannot use exclusive scopes for app permissions. Just in case I’ve neglected to answer some question, here’s a link to the official announcement, as well as the preview documentation. Next, we will look into assigning “regular” management roles to service principal objects, which allows us to limit CBA scenarios, as well as the added support for management scopes based on administrative units. Stay tuned!

Posted in Exchange Online, Microsoft 365, Office 365, PowerShell | 2 Comments

Manage directory synchronization settings via the Graph API

After years of unsuccessful attempts to deprecate the good old MSOnline PowerShell module and subsequent postponements for its end of life, Microsoft seems to finally be making steps in the right direction and providing actual replacements for missing functionalities. In this case, we’re talking about the set of cmdlets used to manage directory synchronization settings, most of which are now covered by a Graph API endpoint. Let’s take a look.

First, a quick refresher on the set of cmdlets involved. Apart from the Get-MsolCompanyInformation cmdlet, which can be used to check the status of directory synchronization and password sync, the last sync timestamps and the service account used, the MSOnline module features a set of cmdlets to get or update some of the settings controlling the dirsync process. Those include:

  • Get-MsolDirSyncConfiguration – gives you the status of the Accidental deletion feature.
  • Get-MsolDirSyncFeatures – gives you the status of the rest of the features, such as device writeback or soft-match based on UPN.
  • Set-MsolDirSyncConfiguration – updates the Accidental deletion feature settings.
  • Set-MsolDirSyncEnabled – toggles the status of the directory synchronization feature itself.
  • Set-MsolDirSyncFeature – controls the rest of the features.

The list of corresponding replacements is now available under the /beta endpoint of the Graph API, under /directory/onPremisesSynchronization/. Only delegate permissions are currently supported, and you will need the OnPremDirectorySynchronization.Read.All scope to read the current settings values. To make changes, the OnPremDirectorySynchronization.ReadWrite.All scope is needed. Let’s take a look at few examples.

First, to get the current configuration, use a GET query against the endpoint:

GET https://graph.microsoft.com/beta/directory/onPremisesSynchronization

As you can see from the screenshot above, the settings are grouped into two sections. Configuration lists the accidental deletion feature settings as well as the sync interval settings, all bundled into a onPremisesDirectorySynchronizationConfiguration resource type. Features lists the settings for each feature, as part of the onPremisesDirectorySynchronizationFeature resource type. Do note that the individual feature names differ between the output of the MSOnline cmdlets and the Graph API. Lastly, we have the id value, representing the tenant’s objectID, which we will need in order to update the settings.

To update the directory synchronization settings, one must issue a PATCH request against the /directory/onPremisesSynchronization/{id} endpoint and provide a JSON body with the corresponding resource settings values. The OnPremDirectorySynchronization.ReadWrite.All scope is required in order to make change, so make sure you have it granted and are running in the delegate permissions model. The list of “configuration” settings you can update includes:

  • accidentalDeletionPrevention – a representation of the onPremisesAccidentalDeletionPrevention resource, with two settings:
    • alertThreshold – integer value representing the maximum number of objects deleted before an alert is triggered. Alternatively, you can provide a percentage value (see below).
    • synchronizationPreventionType – string value (enum?), representing the status of the accidental deletion feature. Possible values include: disabled, enforcedForCount and enforcedForPercentage.
  • synchronizationInterval – the sync interval, represented as a duration value. The default value is “PT30M”. If directory synchronization is disabled, the value will be null.
  • customerRequestedSynchronizationInterval – the custom sync interval, as requested by your organization. Also represented as a duration value. If directory synchronization is disabled, the value will be null.

As an example, here’s how to update the alertThreshold value:

PATCH https://graph.microsoft.com/beta/directory/onPremisesSynchronization/923712ba-352a-4eda-bece-09d0684d0cfb

Next, we have the set of “feature” settings. Those include:

  • BlockCloudObjectTakeoverThroughHardMatch – controls whether “hard-match” is enabled.
  • blockSoftMatchEnabled – controls whether “soft-match” is enabled.
  • bypassDirSyncOverridesEnabled – controls the “dirsync overrides” feature as detailed here.
  • cloudPasswordPolicyForPasswordSyncedUsersEnabled – controls whether the cloud password policy applies to user with password sync enabled.
  • concurrentCredentialUpdateEnabled – controls concurrency for user credentials updates.
  • concurrentOrgIdProvisioningEnabled – controls concurrency for user provisioning.
  • deviceWritebackEnabled – controls the device writeback feature.
  • directoryExtensionsEnabled – controls the  directory extensions feature.
  • fopeConflictResolutionEnabled – this setting is only relevant for FOPE clients, and no longer used?
  • groupWriteBackEnabled – controls the group writeback feature.
  • passwordSyncEnabled – controls the password sync feature.
  • passwordWritebackEnabled – controls the password writeback feature.
  • quarantineUponProxyAddressesConflictEnabled – controls the behavior of the duplicate attribute resiliency feature.
  • quarantineUponUpnConflictEnabled – as above.
  • softMatchOnUpnEnabled – controls whether soft-match should happen also on UPN values.
  • synchronizeUpnForManagedUsersEnabled – controls whether UPN updates are syncrhonized from on-premises (read here).
  • unifiedGroupWritebackEnabled – controls the M365 Group writeback feature.
  • userForcePasswordChangeOnLogonEnabled – controls the force password change on next logon feature.
  • userWritebackEnabled – controls the User writeback feature (deprecated?).

As an example, here’s how to toggle the group writeback and M365 group writeback features:

PATCH https://graph.microsoft.com/beta/directory/onPremisesSynchronization/923712ba-352a-4eda-bece-09d0684d0cfb

You can of course mix and match settings between the configuration and features sections as needed. Do note however that not all settings can be changed. And most importantly, none of the settings above allows you to actually enable/disable directory synchronization currently, as you might have already noticed. This is controlled via the onPremisesSyncEnabled value, which is currently NOT exposed via the Graph API. I’ll make sure to update the article once that happens.

Posted in Azure AD, Graph API, Microsoft 365, Office 365, PowerShell | Leave a comment

Connecting to Exchange Online PowerShell by passing an access token

December 2022 has certainly been an eventful month for Exchange Online PowerShell, with bunch of new improvements to cover, and the news of the planned deprecation of RPS-based modules and methods hitting last week. We will leave the Remote PowerShell deprecation news for another article, and will focus on covering one small, but impactful addition introduced with the latest (preview) version of the “V3” Exchange Online PowerShell module – namely the addition of the -AccessToken parameter and support for passing access tokens in general.

To start of, you will need the latest preview version of the module installed. At the time of writing, this is the 3.1.0-Preview1 version, which you can download over at the PowerShell Gallery. As you can see from the release notes, not much has changed in this release:

v3.1.0-Preview1 :
1. Support for providing an Access Token with Connect-ExchangeOnline.
2.  Bug fixes in Connect-ExchangeOnline and Get-ConnectionInformation.

Unfortunately, that’s pretty much all the information you can find about the new -AccessToken parameter, as no documentation has been published on how to use it. The only clue we can get is from the Connect-ExchangeOnline cmdlet help:


Note: This parameter is available in version 3.1.0-Preview1 or later of the module.

The AccessToken parameter specifies the OAuth JSON Web Token (JWT) that’s used to connect to ExchangeOnline.

Depending on the type of access token, you need to use this parameter with the Organization, DelegatedOrganization, or UserPrincipalName parameter.

In this article, I will describe the steps required to connect to Exchange Online via this new method. Those of you that have already played with certificate-based authentication or used the unsupported variations (such as the methods detailed here or here) will find the process quite familiar, as the introduction of the –AccessToken parameter basically makes such methods officially supported. For example, you can now use the -AccessToken parameter together with the -Organization one in order to connect via a client secret, instead of using the unsupported methods outlined in the second article above.

You can use any existing method in order to obtain the access token, such as the ADAL/MSAL-based snippets used in the articles above. The important thing is that once the token is obtained, you can now pass it directly to the Connect-ExchangeOnline cmdlet, instead of using the unsupported, and likely to be deprecated, method based on the New-PSSession cmdlet used in said articles. And, depending on the flow used, you will either have to provide some additional details, such as the -Organization identifier when connecting as a service principal using the application permissions model, or the -UserPrincipalName when connecting in the context of a user within the delegate permissions model.

But I digress. The idea behind this article was to show you how to enable the latter scenario, namely connecting in the context of a given user while passing an access token. To start with, you will need an Azure AD application with the necessary permissions granted. When connecting in the context of a user, you can and you should be leveraging the built-in Microsoft application (clientID of fb78d390-0c51-40cd-8e17-fdbfab77341b), unless you have a valid reason to use your own. If you are reading this article, I will assume you do, so let’s continue with creating the Azure AD app registration.

Open the Azure AD blade, go to App registrations and hit the New registration button. Enter a Name for you app, and for Supported account types, select the first option, Accounts in this organizational directory only. Other values can also work, but that’s not relevant for the current scenario. Lastly, under Redirect URI, select Public client/native (mobile & desktop) from the dropdown menu and enter a value for the URI. Again, some caveats apply here, but for the purposes of our scenario we can enter something like https://blabla. Hit the Register button to complete the process.

The app registration will now be created and you will be redirected to the Overview page for the new object, where you can see some important details we will need later on. Take a note of the Application (client) ID value and copy it. You can also copy the Directory (tenant) ID value, if you don’t already know it. At this point we can already fetch an Access token for our newly registered app, however, the permissions that will allow us to access Exchange Online PowerShell have not been granted yet. In order to ensure proper access, go to API permissions, hit the Add a permission button, select APIs my organization uses, then search for and select the Office 365 Exchange Online entry. Next, select Delegated permissions, expand the Exchange node and select Exchange.Manage entry. Finally, hit the Update permissions button.

The screenshot above illustrates how the API permissions should look like for our newly registered app. This is an important step – without the Exchange.Manage permission you will be presented with an UnAuthorized error when trying to connect to Exchange Online PowerShell. Also note that the Exchange.Manage permission does not require any sort of admin consent, so every user will be able to leverage our new app to obtain a valid access token for Exchange Online PowerShell. The usual RBAC and protocol-level controls apply though, so this is not a security issue. In addition, you can also restrict who gets access to our new app, but toggling the Assignment Required property for the corresponding service principal object, and adding Users and groups as needed.

So, we now have an application we can leverage to obtain an access token for Exchange Online management. All that is left now is actually fetching the token and then passing it to the Connect-ExchangeOnline cmdlet. To obtain the token, you can use a variety of methods, such as the MSAL- and ADAL-based snippets shown below, the various SDKs, direct HTTPS requests, even “borrowing” a token obtained from the module itself. The method doesn’t really matter, as long as the token is a valid one and contains the Exchange.Manage permission.

#Obtain an access token via MSAL

Add-Type -Path "C:\Program Files\WindowsPowerShell\Modules\MSAL\Microsoft.Identity.Client.dll"

$app2 = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create("59bd1626-xxxx-xxxx-xxxx-4b5ec0b5532b").WithTenantId("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").WithRedirectUri("https://ExOPSApp").WithBroker().Build #Azure Android app

$Scopes = New-Object System.Collections.Generic.List[string]
$Scope = "https://outlook.office365.com/.default"

$token = $app2.Invoke().AcquireTokenInteractive($Scopes).WithLoginHint("user@tenant.onmicrosoft.com").ExecuteAsync().Result

#Obtain an access token via ADAL
Add-Type -Path 'C:\Program Files\WindowsPowerShell\Modules\AzureAD\\Microsoft.IdentityModel.Clients.ActiveDirectory.dll' #-PassThru

$authContext3 = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList "https://login.windows.net/tenant.onmicrosoft.com"
$plat = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters -ArgumentList "Auto"
$authenticationResult = $authContext3.AcquireTokenAsync("https://outlook.office365.com", "59bd1626-xxxx-xxxx-xxxx-4b5ec0b5532b", "https://ExOPSApp",$plat);

$token = $authenticationResult.Result.AccessToken

For the sake of completeness, here’s how a decoded token should look like. Note the presence of the Exchange.Manage permission under the scp claim. And since we’re obtaining a token in the context of a given user, you can also find the relevant user claims, including the list of admin roles assigned to the user.

As mentioned above, no admin consent is required for the Exchange.Manage permission. Still, the first time a given user tries to obtain a token via our app, a consent screen will popup, screenshot of which I’m also adding for the sake of completeness:

Now that we have a valid token, all that’s left is to pass it to the Connect-ExchangeOnline cmdlet, by providing it as value for the -AccessToken parameter. And since we obtained a token in the context of a user, we will also use the -UserPrincipalName parameter. And that’s it:

From here on, it’s business as usual – the user will get access to the set of cmdlets granted to him based on the roles/admin permissions assigned. It is important to understand however that this method DOES NOT cater to token expiration, session renewal and so on. Lots of the goodness added with the V2/V3 versions of the Exchange Online PowerShell module revolves around making things easier, more reliable and performant, and by leveraging this semi-automated method, you will be impacting your experience, in a negative way. While it’s a good thing to have the added flexibility of using this method, I wouldn’t recommend doing so, unless you have a valid reason/requirements.

And of course, we can pass a parameter obtained in the context of an application, combined with the –Organization parameter, to emulate the CBA-based experience, with a token obtained outside of the module (such as one leveraging the client credentials flow). The process is the same – obtain an access token with sufficient permissions, but this time in the application context, then pass it to the –AccessToken parameter. Refer to the article above for all the details on how to set up an app and obtain a token for this scenario.

Before closing the article, a small warning – pay attention to the value you provide for the -UserPrincipalName parameter. Why Microsoft does not validate the value, or leverage the corresponding claim directly from the access token, if beyond me. But a mistake therein can have quite interesting repercussions… more on that in another article 🙂

Posted in Exchange Online, Microsoft 365, Office 365, PowerShell | 7 Comments

Role management improvements in the Microsoft 365 Admin Center

Last month, we covered the introduction of custom Azure AD roles for user management. As with most things RBAC, the new bits are available only within the Azure AD blade. But the addition of new user-centric custom roles prompted me to examine how the Microsoft 365 Admin center has changed since the last time I took a deeper look, and in this article we will cover some of the new things I found therein. In a nutshell, those include improved support for Administrative units and AU-scoped roles, a “compare” functionality for roles and the “Run As” feature.

Let’s start with the Role assignments page, which you can access via the Microsoft 365 Admin Center > Roles > Role assignments or directly via this link. While this experience has been available for a while, it was recently improved and now shows AU-scoped role assignments as well. For example, in my tenant I have few users that have been assigned the User Administrator role, scoped down to a specific AU. If I select the User administrator role entry under the Role assignments page and click the Assigned tab, I will now be presented with both directory-wide and AU-scoped assignments:

In addition, the number of assignments as displayed under the General tab includes AU-scoped ones. On the negative side, information about AU-scoped role assignments is still not shown under the Roles experience in the user’s card.

Another new feature is the ability to compare roles. To get to it, select up to three Azure AD roles, then press the Compare roles button on top. You will be taken to the Compare roles page, where you will get a detailed breakdown between the individual permissions included in each role’s definition. Of course, for a proper comparison you should select roles that have some sort of an overlap, for example the screenshot below shows the tally between Reports Reader and Usage Summary Reports Reader roles.

Even in it’s latest version however, the Microsoft 365 Admin Center’s Role assignments experience does not support custom Azure AD roles. Another important functionality missing is the integration with Privileged Identity Management. So while these improvements are certainly helpful, I’d still recommend sticking to the Azure AD blade, at least for the time being.

If you do want to use the Microsoft 365 Admin Center for role management, there is another new feature you might appreciate. The so-called Run As functionality allows you to “reload” the M365 AC UI in the context of a given role, that is the UI will simulate running under a user who has the given admin role assigned and toggle all the corresponding controls as necessary. This is a nice way to preview the effect of assigning a given role, at least when it comes to the availability of the relevant UI bits. For example, you can use this feature to double-check whether activating the Helpdesk administrator role will allow the user to open service requests.

To activate the Run As feature, navigate to the Roles > Role assignments page and select the role you’re interested in, then hit the Run As button on top of the right-hand pane (as seen on the first screenshot above). A new browser tab will open with the Microsoft 365 Admin Center UI toggled as per the permissions included in the selected role. The experience lacks some polish, but it still gives you an idea of what to expect when assigning a given role. In the example below, the Usage Summary Reports Viewer role has been selected, thus the M365 AC UI is rendered in a very restrictive mode, with just the Reports and Health sections visible. Note the message bar on top informing you about using the Run As mode, the role selected (bugged in this case), the fact that any changes made are actually committed, and the Exit Run As button on the far right.

You might note the URL in the address bar, which gives you another way to launch the Run As feature, provided you know the GUID of the desired Azure AD role:




Next, let’s turn to the Administrative units improvements. Not only you can find a list of all AUs under the Roles > Administrative units page, but you can now also manage them directly within the Microsoft 365 Admin center. Well, with some limitations, such as the lack of support for device objects (as the M365 AC still doesn’t do devices). Dynamic membership is also not supported, and if you select an existing AU with dynamic membership rules configured, you will be presented with a deep link pointing to the corresponding experience within the Azure AD blade.

To create a new Administrative unit, hit the Add unit button on top. You will be presented with the familiar “wizard” experience. On the initial page, you can enter a Name for the AU, and optional Description. Next, you will be taken to the Add members page, where you can either manually Add up to 20 users and groups, or provide a CSV file to provision up to 200 members, via the Upload users selection. As mentioned already, dynamic membership is not supported, and neither is adding device objects. Keep in mind that this part of the process is optional, so you can just skip to the next page.

Next, you can choose whether to add scoped role assignment, based on the current AU. This is also an optional step, configured under the Assign admins to scoped roles page of the wizard. You will be able to select from a list of 10 Azure AD roles, which unlike the Azure AD blade experience does not include custom-created roles. To create a scoped role assignment, select a given role, say User Administrator, then hit the Assign admins button on top. Keep in mind that the button only appears after you have selected a role. Hitting the button will force a new pane to appear on the right-side, allowing you to add individual users or groups. Again, this part of the process is optional and can be configured later on. Hitting the Next button will take you to the Review and finish page, where you can double-check the selection made and confirm the creation of the new AU.

To manage an existing AU, click it’s name in the list of AUs available within the tenant. This will take you to the Members page, where you can add/remove users and groups as needed (again, no devices). In addition, you will be able to Upload users or Export members, by hitting the corresponding button. If the AU is using dynamic membership rules, you will only be able to see the current list of members, and use the redirect link to the Azure AD experience, where you can make changes as needed. A basic Filter functionality is also available.

To edit the list of users who have been granted permissions to manage objects within a given AU, go to the Role Assignments tab. As with the AU creation process, you will be presented with a list of supported roles, but interestingly enough, this time we’re not shown a count of current active assignments. To create an AU-scoped role assignment, select the role in question, hit the Assign admins button on top and add users or groups as needed. On the same pane, you can remove existing role assignments.

Should you want to change the name and/or description of an AU, you can do so under the Administrative units landing page, by selecting the corresponding AU and hitting the Edit name and description button. Similarly, to remove an existing AU, select it and hit the Delete unit button.

So in summary, Microsoft has introduced a set of new features within the M365 Admin Center, which bring the Role management experience a step closer to parity with other endpoints. We now get information about AU-scoped role assignments at least in some parts of the UI, can manage AUs directly within the M365 AC, can compare Azure AD roles and even preview the effects of assigning a given role. Overall, a good set of additions, though there are still some missing pieces that require you to use the Azure AD blade, PowerShell or the Graph API instead. Hopefully, Microsoft will address these in the future.

Posted in Azure AD, Microsoft 365, Office 365 | 3 Comments