Identify applications using Exchange Online OutlookRESTv2 or Exchange Web Services (EWS) APIs

  • Thread starter Thread starter The_Exchange_Team
  • Start date Start date
T

The_Exchange_Team

This post is a part of our EWS (Exchange Web Services) to Microsoft Graph Migration Guide Series.

Earlier this month the Exchange Team announced two major changes that impact the API’s available to connect to Exchange Online:

  • The Outlook REST v2 API is scheduled to be decommissioned on March 31st, 2024, for more information see the article here
  • The Exchange Web Services (EWS) API will be retired on October 1, 2026, for more information see the article here

Several customers are trying to proactively identify the applications in their tenant that use these API’s and have opened cases with Microsoft Support requesting some guidance and/or assistance.

The goal of this article is to discuss what options are available and provide a set of detailed instructions.

Depending on the API used, there are two locations where the API permissions to connect to Exchange Online can be granted:

  • Azure AD using App Registrations: used for all Exchange Online APIs including OutlookRESTv2, Exchange Web Services (EWS), Microsoft Graph, IMAP, POP, SMTP
  • Exchange Online using RBAC for Applications: used only for Microsoft Graph and EWS applications.

In Azure AD you can either use the ‘Microsoft Entra Admin center’ or Windows PowerShell to view which API permissions have been granted to each individual application.

Manually reviewing each App Registration using the ‘Microsoft Entra Admin center’ can be a quite cumbersome and time-consuming task, especially if your tenant has several hundred application registrations.

Exporting the API Permissions from Azure AD using Windows PowerShell can also be an intimidating task, so the goal of this article is to provide detailed explanation of the steps involved.
Note: A sample script to export the API permissions from Azure AD is provided at the end of this article.

As mentioned, in addition to the API permissions assigned in Azure AD, for both the Microsoft Graph and EWS APIs, permissions can be assigned through RBAC roles in Exchange Online as documented in this article.

Right now, the only option to review the API permissions assigned using RBAC for Applications is by using the Exchange Online Management v3 PowerShell module. Detailed instructions to review these permissions are provided together with a sample script that automates this task.

Exporting API Permissions assigned in Azure Active Directory


As mentioned, you have two options to review the API permissions assigned in Azure AD.

Using the Microsoft Entra Admin center GUI

  • Sign in to the Microsoft Entra admin center as a global administrator.
  • Expand the Identity menu > Select Applications > App registrations.
  • In the App registrations window, under the All applications tab, select each app registration for which you wish to review, this opens the app registration's Overview pane.
  • From the left pane of the window, under the Manage menu group, select API permissions.

PowerShell: using either the AzureAD or Microsoft.Graph modules

You can use either the ‘AzureAD’ module or the ‘Microsoft.Graph’ module, both use a similar high-level approach.

If you are using the Microsoft.Graph module, make sure that an App Registration for Microsoft Graph module has been setup correctly in your tenant that has the required 'Application.Read.All' API permissions assigned.

For more information on these steps please see Authentication module cmdlets in Microsoft Graph PowerShell.

A few sample scripts that can help automate steps mentioned in this article are linked to at the end of this blog post.

Using the ‘AzureAD’ PowerShell module


IMPORTANT: Please note that the AzureAD PowerShell module is scheduled to be retired on March 30, 2024. For more information see Important: Azure AD Graph Retirement and PowerShell Module Deprecation and Upgrade from Azure AD PowerShell to Microsoft Graph PowerShell.

Detailed instructions:

Validate that you have the latest version of the AzureAD module installed. Please note that the AzureAD module is not compatible with PowerShell version 7.

PS C:\> Get-InstalledModule ‘AzureAD’ | Select-Object Name, Version
Name Version
---- -------
AzureAD 2.0.2.182

Connect to Azure Active Directory with a global administrator account by running the cmdlet connect-AzureAD.

PS C:\> connect-AzureAD

Retrieve all the App registrations by running: Get-AzureADApplication -All $true
Note: In the example below, I’m using a single App Registration that has both the EWS Delegated and Application permissions assigned.

PS C:\> $AppReg = Get-AzureADApplication -ObjectId 7cb7d375-b0e9-40e3-8abe-4c4fcfe4e1de
PS C:\> $AppReg | Select-Object DisplayName, AppId
DisplayName AppId
----------- -----
My EWS Application 5951643e-5013-442f-963c-1ee875bbe74a

For each App registration get the the RequiredResourceAccess collection. For each item in the RequiredResourceAccess collection, retrieve both the ResourceAppId property (GUID) and the ResourceAccess collection. The example below only retrieves the ResourceAppId and ResourceAccess collection for the first object in the RequiredResourceAccess collection.

PS C:\> $ReqResAccess = $AppReg.RequiredResourceAccess[0]
PS C:\> $ReqResAccess[0].ResourceAppId
00000002-0000-0ff1-ce00-000000000000
PS C:\> $ReqResAccess[0].ResourceAccess
Id Type
-- ----
3b5f3d61-589b-4a3c-a359-5dd4b5ee5bd5 Scope
dc890d15-9560-4a4c-9b7f-a736ec74ec40 Role

Using the ResourceAppId (GUID) property retrieved in Step 4, get the SecurityPrincipal object from Azure AD representing this ResourceAccess group object by running cmdlet Get-AzureADServicePrincipal.

PS C:\> $ServicePrincipal = Get-AzureADServicePrincipal -All $true | Where-Object {$_.AppId -eq $($ReqResAccess[0].ResourceAppId)}
PS C:\> $ServicePrincipal | Select-Object AppId, DisplayName
AppId DisplayName
----- -----------
00000002-0000-0ff1-ce00-000000000000 Office 365 Exchange Online

Loop through the ResourceAccess collection retrieved in Step 4 above:

If the ResourceAccess Scope property object equals ‘Scope’ than the permission is a Delegated API permission. Match the ResourceAccess ID property to an entry in the of the AzureAD ServicePrincipal Oauth2Permissions collection. In my example, the Delegated API Permissions is the First object in the ResourceAccess collection, so index 0:

PS C:\> $ReqResAccess[0].ResourceAccess[0]
Id Type
-- ----
3b5f3d61-589b-4a3c-a359-5dd4b5ee5bd5 Scope
PS C:\> $AppScope = $ServicePrincipal.Oauth2Permissions | Where-Object {$_.Id -eq "$($ReqResAccess[0].ResourceAccess[0].Id)"}
PS C:\> $AppScope | Select-Object Type, Value
Type Value
---- -----
User EWS.AccessAsUser.All

If the ResourceAccess Scope property object equals ‘Role’ than the permission is an Application API permission. Match the ResourceAccess ID property to an entry in the of the AzureADServicePrincipal AppRoles collection. In my example, the Application API Permissions is the Second object in the ResourceAccess collection, so index 1:

PS C:\> $ReqResAccess[0].ResourceAccess[1]
Id Type
-- ----
dc890d15-9560-4a4c-9b7f-a736ec74ec40 Role
PS C:\> $AppRole = $ServicePrincipal.AppRoles | Where-Object {$_.Id -eq "$($ReqResAccess[0].ResourceAccess[1].Id)"}
PS C:\> $AppRole | Select-Object AllowedMemberTypes, Value
AllowedMemberTypes Value
------------------ -----
{Application} full_access_as_app
Using the ‘Microsoft.Graph’ PowerShell module


Detailed instructions:

Validate that you have the latest version of the Microsoft.Graph module installed.

PS C:\> Get-InstalledModule ‘Microsoft.Graph’ | Select-Object Name, Version
Name Version
---- -------
Microsoft.Graph 2.6.1

Connect to Microsoft Graph with a global administrator account by running the cmdlet connect-MgGraph, as a best practice use only the minimum required permissions, in our scenario this Application.Read.All:

Connect-MgGraph -Scopes Application.Read.All

Retrieve all the App registrations by running: Get-MgApplication -All
Note: In the example below, I’m using a single App Registration that has the Microsoft Graph Delegated ‘User.Read’ and Application ‘User.Read.All’ API permissions assigned.

PS C:\> $AppReg = Get-MgApplication | Where {$_.AppId -eq '283a0ec7-65c3-45a8-9739-1322b5696045'}
PS C:\> $AppReg | Select-Object DisplayName, AppId
DisplayName AppId
----------- -----
My Graph Application 283a0ec7-65c3-45a8-9739-1322b5696045

For each App registration get the the RequiredResourceAccess collection. For each item in the RequiredResourceAccess collection, retrieve both the ResourceAppId property (GUID) and the ResourceAccess collection. The example below only retrieves the ResourceAppId and ResourceAccess collection for the first object in the RequiredResourceAccess collection.

PS C:\> $ReqResAccess = $AppReg.RequiredResourceAccess[0]
PS C:\> $ReqResAccess.ResourceAppId
00000002-0000-0ff1-ce00-000000000000
PS C:\> $ReqResAccess.ResourceAccess
Id Type
-- ----
e1fe6dd8-ba31-4d61-89e7-88639da4683d Scope
df021288-bdef-4463-88db-98f22de89214 Role

Using the ResourceAppId (GUID) property retrieved in Step 4, get the SecurityPrincipal object from Graph representing this ResourceAccess group object by running cmdlet Get-MgServicePrincipal.

PS C:\> $ServicePrincipal = Get-MgServicePrincipal -All | Where-Object {$_.AppId -eq $($ReqResAccess.ResourceAppId)}
PS C:\> $ServicePrincipal | Select-Object AppId, DisplayName
AppId DisplayName
----- -----------
00000003-0000-0000-c000-000000000000 Microsoft Graph

Loop through the ResourceAccess collection retrieved in Step 4 above.

If the ResourceAccess Scope property object equals ‘Scope’ than the permission is a Delegated API permission. Match the ResourceAccess ID property to an entry in the of the AzureAD ServicePrincipal Oauth2PermissionScopes collection. In my example, the Delegated API Permissions is the First object in the ResourceAccess collection, so index 0:

PS C:\> $ReqResAccess.ResourceAccess[0]
Id Type
-- ----
e1fe6dd8-ba31-4d61-89e7-88639da4683d Scope
PS C:\> $AppScope = $ServicePrincipal. Oauth2PermissionScopes | Where-Object {$_.Id -eq "$($ReqResAccess.ResourceAccess[0].Id)"}
PS C:\> $AppScope | Select-Object Type, Value
Type Value
---- -----
User User.Read

If the ResourceAccess Scope property object equals ‘Role’ than the permission is an Application API permission. Match the ResourceAccess ID property to an entry in the of the AzureADServicePrincipal AppRoles collection. In my example, the Application API Permissions is the Second object in the ResourceAccess collection, so index 1:

PS C:\> $ReqResAccess.ResourceAccess[1]
Id Type
-- ----
df021288-bdef-4463-88db-98f22de89214 Role
PS C:\> $AppRole = $ServicePrincipal.AppRoles | Where-Object {$_.Id -eq "$($ReqResAccess.ResourceAccess[1].Id)"}
PS C:\> $AppRole | Select-Object AllowedMemberTypes, Value
AllowedMemberTypes Value
------------------ -----
{Application} User.Read.All
Exporting the API permissions assigned via RBAC for Application in Exchange Online


Note: An alternate method to the one described below, you can also Identify the API permissions assigned via RBAC by running the Test-ServicePrincipalAuthorization cmdlet.

Detailed instructions:

Validate that you have the latest version of the ExchangeOnlineManagement v3 module installed.

PS C:\> Get-InstalledModule ‘ExchangeOnlineManagement’ | Select-Object Name, Version
Name Version
---- -------
ExchangeOnlineManagement 3.3.0

Connect to Exchange Online with an Exchange administrator account by running the cmdlet connect-ExchangeOnline:

PS C:\> connect-ExchangeOnline

Retrieve all the ServicePrincipal objects in Exchange Online by running: Get-ServicePrincipal. In the example below, I’m using a single App Registration that has the ‘Application EWS.AccessAsApp’ Role assigned via RBAC for Applications.

PS C:\> Get-ServicePrincipal -Identity 9672ee63-6e28-4ced-885e-fb595158a843
PS C:\> $SP | Select-Object DisplayName, AppId
DisplayName AppId
----------- -----
SP for My EWS Application 5951643e-5013-442f-963c-1ee875bbe74a

For each Service Principal object get all the ManagementRoleAssignment by running Get-ManagementRoleAssignment. Loop through the ManagementRoleAssignment collection and display the Role, RoleAssignee, CustomerResourceScope.

PS C:\> $AllMgmtRoleAssigment = Get-ManagementRoleAssignment -RoleAssignee $($SP.Identity)
PS C:\> $AllMgmtRoleAssigment[0] | Select-Object RoleAssignee, Role, CustomResourceScope
RoleAssignee Role CustomResourceScope
------------ ---- -------------------
9672ee63-6e28-4ced-885e-fb595158a843 Application EWS.AccessAsApp User1 scope

If you want to retrieve more information regarding the RoleAssignee, run the cmdlet Get-ManagementRole:

PS C:\> Get-ServicePrincipal -identity $($AllMgmtRoleAssigment[0].RoleAssignee)
DisplayName ObjectId AppId
----------- -------- -----
SP for My EWS Application 9672ee63-6e28-4ced-885e-fb595158a843 5951643e-5013-442f-963c-1ee875bbe74a

If you want to retrieve more information regarding the Role that was assigned, run the cmdlet Get-ManagementRole:

PS C:\> Get-ManagementRole -Identity $($AllMgmtRoleAssigment[0].Role)
Name RoleType
---- --------
Application EWS.AccessAsApp ApplicationEWSAccessAsApp

If you want to retrieve more information regarding the CustomResourceScope, check the RecipientWriteScope property depending on the RecipientWriteScope object run one of the following:

If the RecipientWriteScope is a CustomRecipientScope run the cmdlet Get-ManagementScope

PS C:\> $AllMgmtRoleAssigment[0].RecipientWriteScope
CustomRecipientScope
PS C:\> Get-ManagementScope -Identity $($AllMgmtRoleAssigment[0].CustomResourceScope)
Name ScopeRestrictionType Exclusive RecipientRoot RecipientFilter ServerFilter
---- -------------------- --------- ------------- --------------- ------------
User1 scope RecipientScope False Alias -eq 'user1'

If the RecipientWriteScope is a AdministrativeUnit , run the cmdlet Get-AdministrativeUnit

PS C:\> $AllMgmtRoleAssigment[1].RecipientWriteScope
AdministrativeUnit
PS C:\> get-AdministrativeUnit -Identity $AllMgmtRoleAssigment[1].CustomResourceScope
Name DisplayName
---- -----------
60f565a0-9a30-40e6-a34e-4023db4616c2 Europe Admin Unit
Sample Script to exporting API Permissions assigned in Azure Active Directory


A sample script Export-AppReg_APIPermissions to export the API Permissions for each Application Registration in Azure AD is available on GitHub here.

IMPORTANT: The script performs only READ ONLY operations against Azure Active Directory.

The script uses 3 mandatory parameters; please see script documentation for more information.

A few examples:

.\Export-AppReg_APIPermissions_v1.0.ps1 -UsePSModule:Microsoft.Graph -ExportAPIPermissions:EWS -OutputPath:'C:\temp'

The script will use the 'Microsoft.Graph' PowerShell module to retrieve all the Application registrations from AzureAD and will export ALL the 'EWS' API Permissions to a file "Export-AppReg_APIPermissions_<timestamp>.csv" in the 'C:\temp' directory.

.\Export-AppReg_APIPermissions_v1.0.ps1 -UsePSModule:AzureAD -ExportAPIPermissions:EWS -OutputPath:'C:\temp'

The script will use the 'AzureAD' PowerShell module to retrieve all the Application registrations from AzureAD and will export ALL the 'EWS' API Permissions to a file "Export-AppReg_APIPermissions_<timestamp>.csv" in the 'C:\temp' directory.

.\Export-AppReg_APIPermissions_v1.0.ps1 -UsePSModule:Microsoft.Graph -ExportAPIPermissions:OutlookRESTv2 -OutputPath:'C:\temp'

The script will use the 'Microsoft.Graph' PowerShell module to retrieve all the Application registrations from AzureAD and will export ALL the Outlook REST v2 API Permissions to a file "Export-AppReg_APIPermissions_<timestamp>.csv" in the 'C:\temp' directory.

Sample Script to export the API permissions assigned using RBAC for Application in Exchange Online


A sample script Export-RBAC_APIPermisions to export the API Permissions for each Service Principal object in Exchange Online is available on GitHub here.

IMPORTANT: The script performs only READ ONLY operations against Exchange Online.

Let us know if you have feedback on this blog post!

Dirk Buntinx

Continue reading...
 
Back
Top