Assigning Azure AD Graph API Permissions to a Managed Service Identity (MSI)

On a recent support case a customer wished to assign Azure AD Graph API permissions to his Managed Service Identity (MSI). If this was a standard Application Registration, assigning API permissions is quite easy from the portal by following the steps outlined in Azure AD API Permissions. However, today Managed Service Identities are not represented by an Azure AD app registration so granting API permissions is not possible in the Azure AD portal for MSIs.

Luckily, this is possible with the Azure AD and Azure PowerShell modules as well as Azure CLI shown via my colleague Liam Smith’s code samples below:

UPDATED 2020-11-30: Updated to assign graph.microsoft.com app roles instead of the legacy graph.windows.net. Reference https://docs.microsoft.com/en-us/graph/permissions-reference#microsoft-graph-permission-names for list of app roles

Assigning via PowerShell

#First define your environment variables
$TenantID="91ceb514-5ead-468c-a6ae-048e103d57f0"
$subscriptionID="ed6a63cc-c71c-4bfa-8bf7-c1510b559c72"
$DisplayNameOfMSI="AADDS-Client03"
$ResourceGroup="AADDS"
$VMResourceGroup="AADDS"
$VM="AADDS-Client03"

#If your User Assigned Identity doesnt exist yet, create it now
New-AzUserAssignedIdentity -ResourceGroupName $ResourceGroup -Name $DisplayNameOfMSI

#Now use the AzureAD Powershell module to grant the role
Connect-AzureAD -TenantId $TenantID #Connected as GA
$MSI = (Get-AzureADServicePrincipal -Filter "displayName eq '$DisplayNameOfMSI'")
Start-Sleep -Seconds 10
$GraphAppId = "00000002-0000-0000-c000-000000000000" #Windows Azure Active Directory aka graph.windows.net, this is legacy AAD graph and slated to be deprecated
$MSGraphAppId = "00000003-0000-0000-c000-000000000000" #Microsoft Graph aka graph.microsoft.com, this is the one you want more than likely.
$GraphServicePrincipal = Get-AzureADServicePrincipal -Filter "appId eq '$MSGraphAppId'"
$PermissionName = "Directory.Read.All"
$AppRole = $GraphServicePrincipal.AppRoles | Where-Object {$_.Value -eq $PermissionName -and $_.AllowedMemberTypes -contains "Application"}
New-AzureAdServiceAppRoleAssignment -ObjectId $MSI.ObjectId -PrincipalId $MSI.ObjectId -ResourceId $GraphServicePrincipal.ObjectId -Id $AppRole.Id  

#NOTE: The above assignment may indicate bad request or indicate failure but it has been noted that the permission assignment still succeeds and you can verify with the following command
Get-AzureADServiceAppRoleAssignment -ObjectId $GraphServicePrincipal.ObjectId | Where-Object {$_.PrincipalDisplayName -eq $DisplayNameOfMSI} | fl

At this point you should have been able to verify that your identity’s service principal has the correct app roles as shown below.

Get-AzureADServiceAppRoleAssignment showing that MSI principal has assigned AppRoleAssignment

You can now perform some tests to verify permissions via the following code on your Azure Virtual Machine that has the service identity assigned to it:

# First grab a bearer token for the Graph API using IMDS endpoint on Azure VM
$response = Invoke-WebRequest -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://graph.microsoft.com' -Method GET -Headers @{Metadata="true"}
$content = $response.Content | ConvertFrom-Json 
$token = $content.access_token 

You can copy\paste the value of $token to https://jwt.io to verify that your token is showing the Directory.Read.All permission properly.

Output of pasting $token contents to https://jwt.io to verify Directory.Read.All role

If for some reason your $token does not show the Directory.Read.All permission, try rebooting your Azure Virtual Machine as it is possible a previous failed request for a bearer token was cached on your VM

Now, continue testing on your Azure VM by using this $token to make a call to Azure AD Graph API :

$output = (Invoke-WebRequest -Uri "https://graph.windows.net/myorganization/users?api-version=1.6" -Method GET -Headers @{Authorization="Bearer $token"}).content
$json = ConvertFrom-Json $output
$json.value

Your $json.value output should be the successful response of your Azure AD Graph API call. Hope this helps someone!

Assigning via Azure CLI

You can also perform the same steps using Azure CLI and CURL if this is your preferred management environment. See below for Liam’s steps via Azure CLI

#1) Get accesstoken:
accessToken=$(az account get-access-token --resource=https://graph.windows.net --query accessToken --output tsv)

#2) Define a variable for your tenantID
TenantID=mytenant.onmicrosoft.com

#3) Confirm access to graph.windows.net:
curl "https://graph.windows.net/$TenantID/users?api-version=1.6" -H "Authorization: Bearer $accessToken"

#4) Find your managed identity's object ID and assign to a variable (MSIObjectID)
az ad sp list --filter "startswith(displayName, 'MyUAI')" | grep objectId

MSIObjectID=d910d43b-8886-4bff-90bf-25ee0acb2314

#5) Find the service principal objectID (GraphObjectID) of the "Windows Azure Active Directory" principal in your directory,  also confirm the id of the Directory.Read.All oauth2Permission role is 5778995a-e1bf-45b8-affa-663a9f3f4d04 (DirectoryReadAll)
az ad sp list --filter "startswith(displayName, 'Windows Azure Active Directory')"

GraphObjectID=72405622-433d-4943-a5ad-11a4401d0bd3
DirectoryReadAll=5778995a-e1bf-45b8-affa-663a9f3f4d04

#6) Define your JSON payload
json={'"'id'"':'"'$DirectoryReadAll'"','"'principalId'"':'"'$MSIObjectID'"','"'resourceId'"':'"'$GraphObjectID'"'}

#7) Give permissions of 'Directory.Read.All' to the service prinicpal:
curl "https://graph.windows.net/$TenantID/servicePrincipals/$MSIObjectID/appRoleAssignments?api-version=1.6" -X POST -d "$json" -H "Content-Type: application/json" -H "Authorization: Bearer $accessToken"

#8) Verify with read operation
curl "https://graph.windows.net/$TenantID/servicePrincipals/$MSIObjectID/appRoleAssignments?api-version=1.6" -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $accessToken"

#9) Now you can test on your resource which has managed identity to verify access
curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://graph.windows.net' -H Metadata:true
accessToken=<accessToken>
curl 'https://graph.windows.net/mytenant.onmicrosoft.com/users?api-version=1.6' -H "Authorization: Bearer $accessToken"

Oauth2 and OpenID Protocol Review

The following presentations by John Craddock and Pamela Dingle at Microsoft Ignite are the best explanations I have found for understanding and troubleshooting the Oauth2.0 and OpenID protocols as they related to Azure Active Directory in increasing order of complexity:

Introduction to identity standards – BRK3238 – Pamela Dingle
An IT pros guide to Open ID Connect Oauth 2.0 with the V1 and V2 Azure AD – BRK3234 – John Craddock
Troubleshooting OpenID Connect and Oauth 2.0 protocols – John Craddock

What is Office 365 Shell WCSS-Client?

On a recent support case a customer noted that an application named “Office 365 Shell WCSS-Client” was found in his Office 365 and Azure AD sign in security logs.  This customer was concerned that this may be some type of malware.  After searching public documentation we could not find any information on what this application was, so we asked the product engineering teams to see if they could explain.  Our Office 365 UX team provided this very helpful description of what this application is and that it should not be viewed as malware:

Office 365 Shell WCSS-Client is the browser code that runs whenever a user navigates to (most) Office365 applications in the browser.  The shell, also known as the suite header, is shared code that loads as part of almost all Office365 workloads, including SharePoint, OneDrive, Outlook, Yammer, and many more.


The suite header needs authentication to do the following:


* Get information about the user’s licensing state, so that we know what apps to show in the app launcher


* Connect to services that provide information about most recently used documents, so that we can show those in the app launcher


* Connect to Exchange, so that we can provide mail and calendar notifications


* Authenticate against the Microsoft / O365 graph, so that we can get and set user preferences for things like language, user theme and other O365 settings

There are different providers for those different things, necessitating different auth exchanges.  These exchanges happen without direct user intervention, when a page hosting the shell code is loaded.  The shell code, workload-specific code (e.g. SharePoint) and the browser all cache different parts of this information in different ways, so that pattern might not always line up for each user in each workload, but multiple auth exchanges here are the norm. A typical user navigating through different Office365 workloads can expect to see several different requests such as shown in the logs”

O365 Product Engineer

Hopefully this helps someone understand what this application is in the future when performing a similar audit of their security logs in Office 365 or Azure AD.

Capturing a Fiddler trace

A Fiddler trace can be used to view any Http\Https web requests and responses made from your web browser. Capturing this data can be very useful for analyzing errors encountered. To capture a Fiddler trace, follow the steps below.

How to capture a Fiddler trace

  1. Download Fiddler from here and install it.
  2. Run Fiddler and go to Tools -> Fiddler Options.
  3. On the HTTPS page, verify that Capture HTTPS Connects is enabled.
  4. Verify that Decrypt HTTPS traffic is enabled with the …from all processes option.
  1. NOTE: If you are in a Federated enviornment, please also check Rules -> Automatically Authenticate.
  2. In the very bottom left of the Fiddler application, ensure that you see “Capturing” displayed indicating that Fiddler is capturing traffic.
  1. If Fiddler is not capturing. Choose File -> Capture Traffic (Or press F12)
  1. Minimize Fiddler to tray.
  2. Replicate the reported issue.
  3. Once Complete, you can stop capturing by selecting File -> Capture Traffic again or pressing F12
  4. In Fiddler, go to File -> Save -> All Sessions and save the archive to disk.
  1. Save .SAZ file locally and then provide this .SAZ to engineer troubleshooting your issue
  2. Once complete, Fiddler can be uninstalled from Windows Control Panel \ Programs and Features \ Uninstall “Progress Telerik Fiddler”

Capturing a HTTP Archive (HAR) trace

A HTTP Archive (HAR) trace can be used to view any Http\Https web requests and responses made from your web browser. HAR traces are useful when you do not want to install a full application like Fiddler, as HAR files can be captured naively using most Web Browsers like Edge, Chrome, or Internet Explorer. Capturing this data can be very useful for analyzing errors encountered. To capture a HAR trace, follow the steps below

To generate the HAR file for Internet Explorer
  1. Open Internet Explorer and go to the page where the issue is occurring.
  2. Press F12 on your keyboard(or click the gear icon > F12 Developer Tools)
  3. Click the Network tab.
  4. Reproduce the issue that you were experiencing before, while the network requests are being recorded.
  5. Once done click the Save button to export as HAR (or press Ctrl+S).
  1. Give the trace a filename and click the Save button which will save it as a .har file .
  2. Upload your HAR file to your ticket or attach it to your email so that we may analyze it.
To generate the HAR file for Chrome
  1. Open Google Chrome and go to the page where the issue is occurring.
  2. From the Chrome menu bar select View > Developer > Developer Tools .
  3. From the panel opened at the bottom of your screen, select the Network tab.
  4. Look for a round red Record button ( )) in the upper left corner of the Network tab, and make sure it is red. If it is grey, click it once to start recording.
  5. Check the box next to Preserve log .
  6. Click the Clear button ( )) to clear out any existing logs from the Network tab.
  7. Now try to reproduce the issue that you were experiencing before, while the network requests are being recorded.
  8. Once you have reproduced the issue, right click anywhere on the grid of network requests, select Save as HAR with Content , and save the file to your computer.
  1. Upload your HAR file to your ticket or attach it to your email so that we may analyze it.
To generate the HAR file for Edge
  1. Browse to the URL where you are seeing the issue.
  2. Navigate to Developer tools (use F12 as a shortcut) and select the “Network” tab.
  3. Refresh the page to start capturing the traffic between the browser to the server, or click on a link with which you are seeing the issue. The aim is to reproduce the issue and capture the output.
  4. Click on “Export as HAR” followed by Save As… to save the HAR file.
HAR trace Analysis with Fiddler

A support technician can then analyze this HAR trace using an application like Fiddler.

The steps for that are using the File -> Import Sessions option in Fiddler and then choosing HTTPArchive as shown below.