Introduction

Most ADFS admins would probably know the Claims X-Ray web application from Microsoft, which can be used to troubleshoot SAML token issuance:

Claims X-Ray UI Screenshot

Although not officially supported, it is also possible to use Claims X-Ray with Azure Active Directory:

Claims X-Ray Application Registration Screenshot

As Microsoft is pushing Azure AD customers to migrate applications from ADFS to AAD, this utility might become more useful than ever.

Claims X-Ray app registration through the Azure AD Portal is pretty straightforward. But what is more challenging, is  doing the entire configuration with Microsoft Graph PowerShell SDK. As it took me an entire day to figure out some details, while struggling with several bugs in the PowerShell module, I have decided to publish my solution to this task. With only minor modifications, this guide can be used to register almost any SAML-based application to Azure AD using PowerShell.

App Registration

We will first need to install the Microsoft.Graph.Applications and Microsoft.Graph.Identity.SignIns PowerShell modules, including their dependencies:

Install-Module -Name Microsoft.Graph.Applications,Microsoft.Graph.Identity.SignIns -Scope AllUsers -Force

We can then connect to Azure Active Directory while specifying all permissions required by the registration process:

Connect-MgGraph -Scopes @(
   'Application.ReadWrite.All',
   'AppRoleAssignment.ReadWrite.All',
   'DelegatedPermissionGrant.ReadWrite.All',
   'Policy.Read.All',
   'Policy.ReadWrite.ApplicationConfiguration'
)

We are now ready to register the Claims X-Ray application in Azure AD:

[string] $appName = 'Claims X-Ray'
[string] $appDescription = 'Use the Claims X-ray service to debug and troubleshoot problems with claims issuance.'
[string] $redirectUrl = 'https://adfshelp.microsoft.com/ClaimsXray/TokenResponse'
[hashtable] $infoUrls = @{
    MarketingUrl =        'https://adfshelp.microsoft.com/Tools/ShowTools'
    PrivacyStatementUrl = 'https://privacy.microsoft.com/en-us/privacystatement'
    TermsOfServiceUrl   = 'https://learn.microsoft.com/en-us/legal/mdsa'
    SupportUrl          = 'https://adfshelp.microsoft.com/Feedback/ProvideFeedback'
}

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphApplication] $registeredApp =
   New-MgApplication -DisplayName $appName `
                     -Description $appDescription `
                     -Web @{ RedirectUris = $redirectUrl } `
                     -DefaultRedirectUri $redirectUrl `
                     -GroupMembershipClaims All `
                     -Info $infoUrls

The previous command would always fail when used with the -SignInAudience and -IdentifierUris parameters. These application properties thus need to be configured separately, making the application single-tenant:

Update-MgApplication -ApplicationId $registeredApp.Id `
                     -SignInAudience 'AzureADMyOrg' `
                     -IdentifierUris 'urn:microsoft:adfs:claimsxray'

It is time to configure the application logo. As the Claims X-Ray website only contains a logo in the SVG format, which is not supported by AAD, I had to first convert it to PNG:

Claims X-Ray Logo

The logo must be downloaded locally before it can be uploaded to AAD:

[string] $logoUrl = 'https://www.dsinternals.com/assets/images/claims-xray-logo.png'
[string] $tempLogoPath = New-TemporaryFile
Invoke-WebRequest -Uri $logoUrl -OutFile $tempLogoPath -UseBasicParsing

Due to a bug in Microsoft Graph PowerShell, the following command would fail:

Set-MgApplicationLogo -ApplicationId $registeredApp.Id -InFile $tempLogoPath

We thus need to upload the image to Azure AD by calling the raw Graph API:

Invoke-GraphRequest -Method PUT -Uri "https://graph.microsoft.com/v1.0/applications/$($registeredApp.Id)/logo" `
                    -InputFilePath $tempLogoPath `
                    -ContentType 'image/*'

Upon success, the temporary local copy of the logo can be deleted:

Remove-Item -Path $tempLogoPath

Service Principal

Now that the application itself is registered, we can now register the corresponding service principal, which will appear in the Enterprise Applications section of the Azure AD Portal:

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphServicePrincipal] $servicePrincipal =
   New-MgServicePrincipal -DisplayName $appName `
                          -AppId $registeredApp.AppId `
                          -AccountEnabled `
                          -ServicePrincipalType Application `
                          -PreferredSingleSignOnMode saml `
                          -ReplyUrls $redirectUrl `
                          -Notes $appDescription `
                          -Tags 'WindowsAzureActiveDirectoryIntegratedApp','WindowsAzureActiveDirectoryCustomSingleSignOnApplication'

Token-Signing Certificate

One of the requirements for a functional relying party trust is a token-signing certificate. For the sake of simplicity, we can generate a self-signed one, that will be valid for 3 years:

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphSelfSignedCertificate] $tokenSigningCertificate =
   Add-MgServicePrincipalTokenSigningCertificate -ServicePrincipalId $servicePrincipal.Id `
                                                 -DisplayName "CN=$appName AAD Token Signing" `
                                                 -EndDateTime (Get-Date).AddYears(3)

The result will look like this in Azure AD Portal:

SAML Signing Certificate Screenshot

Application Permissions

As we want the Claims X-Ray app to receive information about signed-in users, we need to delegate the User.Read permission:

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphServicePrincipal] $microsoftGraph =
    Get-MgServicePrincipal -Filter "DisplayName eq 'Microsoft Graph'"

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphPermissionScope] $userReadScope =
    $microsoftGraph.Oauth2PermissionScopes | Where-Object Value -eq 'User.Read'

Update-MgApplication -ApplicationId $registeredApp.Id -RequiredResourceAccess @{
    ResourceAppId = $microsoftGraph.AppId
    ResourceAccess = @(@{
        id = $userReadScope.Id
        type = 'Scope'
    })
}

It would make sense to hide the corresponding consent prompt from end-users accessing the app:

 Screenshot

We can therefore give the required consent on behalf of the entire AAD Tenant in advance:

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphOAuth2PermissionGrant] $adminConsent =
    New-MgOauth2PermissionGrant -ClientId $servicePrincipal.Id `
                                -ConsentType AllPrincipals `
                                -ResourceId $microsoftGraph.Id `
                                -Scope $userReadScope.Value

This is how the results should look in the AAD Portal:

Admin Consent Screenshot

User Assignment

For users to see the application in the My Apps portal, they need to be assigned to the application. This is how we can assign ourselves to the app:

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphDirectoryObject] $currentUser =
    Invoke-GraphRequest -Method GET -Uri 'https://graph.microsoft.com/v1.0/me'

[string] $defaultAppAccessRole = [Guid]::Empty
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphAppRoleAssignment] $appAssignment =
   New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $servicePrincipal.Id `
                                           -ResourceId $servicePrincipal.Id `
                                           -AppRoleId $defaultAppAccessRole `
                                           -PrincipalType User `
                                           -PrincipalId $currentUser.Id

Note that we have not declared any custom roles for the application, so we had to reference the default app role ID of 00000000-0000-0000-0000-000000000000.

The result can again be verified through the AAD Portal:

User Assignment Screenshot

SAML Token Configuration

The built-in acct and groups claims are optional, so we need to explicitly enable them:

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphOptionalClaims] $optionalClaims = [Microsoft.Graph.PowerShell.Models.MicrosoftGraphOptionalClaims]::DeserializeFromDictionary(@{
   Saml2Token = @(
      @{ Name = 'acct' },
      @{ Name = 'groups' }
   )
})

Update-MgApplication -ApplicationId $registeredApp.Id -OptionalClaims $optionalClaims

Here is how the change will show up in the UI:

Optional Claims Screenshot

Contrary to what the documentation says, the email and upn do not need to be configured here to appear in SAML tokens. Even the groups claim does not need to be specified if the default group identifier settings are sufficient.

It is also possible to define custom SAML claims for an application:

Custom Claims Screenshot

I have decided to map the AAD attributes to SAML claims as follows:

Claim Type Value
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier user.userprincipalname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name user.userprincipalname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn user.userprincipalname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress user.mail
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname user.givenname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname user.surname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress user.streetaddress
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality user.city
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode user.postalcode
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince user.state
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country user.country
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone user.mobilephone
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone user.telephonenumber
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone user.facsimiletelephonenumber
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/employeeid user.employeeid
http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID user.onpremisesimmutableid
http://schemas.microsoft.com/2012/01/requestcontext/claims/relyingpartytrustid application.objectid

Unfortunately, I have not found a way to configure these rules through the Graph API. Please let me know in case you were more successful than me.

As a workaround, we can override the application-specific claim issuance configuration by creating a Claims Mapping Policy and assigning it to the Claims X-Ray application:

[string] $allClaimsMapping = @'
{
   "ClaimsMappingPolicy": {
       "Version": 1,
       "IncludeBasicClaimSet": "true",
       "ClaimsSchema": [
           {
               "Source": "user",
               "ID": "userprincipalname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"
           },
           {
               "Source": "user",
               "ID": "userprincipalname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
           },
           {
               "Source": "user",
               "ID": "userprincipalname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"
           },
           {
               "Source": "user",
               "ID": "mail",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
           },
           {
               "Source": "user",
               "ID": "givenname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
           },
           {
               "Source": "user",
               "ID": "surname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
           },
           {
               "Source": "user",
               "ID": "streetaddress",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress"
           },
           {
               "Source": "user",
               "ID": "city",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality"
           },
           {
               "Source": "user",
               "ID": "postalcode",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode"
           },
           {
               "Source": "user",
               "ID": "state",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince"
           },
           {
               "Source": "user",
               "ID": "country",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country"
           },
           {
               "Source": "user",
               "ID": "mobilephone",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone"
           },
           {
               "Source": "user",
               "ID": "telephonenumber",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone"
           },
           {
               "Source": "user",
               "ID": "facsimiletelephonenumber",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone"
           },
           {
               "Source": "user",
               "ID": "employeeid",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/employeeid"
           },
           {
               "Source": "user",
               "ID": "onpremisesimmutableid",
               "SamlClaimType": "http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID"
           },
           {
               "Source": "application",
               "ID": "objectid",
               "SamlClaimType": "http://schemas.microsoft.com/2012/01/requestcontext/claims/relyingpartytrustid"
           }
       ],
       "ClaimsTransformation": []
   }
}
'@

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphClaimsMappingPolicy] $allClaimsPolicy =
   New-MgPolicyClaimMappingPolicy -DisplayName 'Issue All Claims' -Definition $allClaimsMapping

New-MgServicePrincipalClaimMappingPolicyByRef -ServicePrincipalId $servicePrincipal.Id -OdataId "https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies/$($allClaimsPolicy.Id)"

Unfortunately, there is currently no user interface for viewing/editing the policies:

Claims Mapping Policy Screenshot

Testing the Sign-In

We are finally ready to log into the Claims X-Ray application and test the SAML claim issuance. This can be done by visiting the My Apps portal:

My Apps Portal Screenshot

Or we can simply run this PowerShell command, which will automatically open the Claims X-Ray application in the default browser:

Start-Process ('https://myapps.microsoft.com/signin/{0}?tenantId={1}' -f $servicePrincipal.AppId,$servicePrincipal.AppOwnerOrganizationId)

Limitations

  • Because the Claims X-Ray app uses the urn:microsoft:adfs:claimsxray identifier, it can only be registered as a single-tenant app.
  • As the Claims X-Ray app is hardcoded with ADFS-specific token request relative URL, only the Identity Provider-Initiated Single Sign-On can be used.
  • Unlike production applications, the Claims X-Ray does not validate the token-signing certificates.
  • This article does not cover the assignment of a Conditional Access Policy, which could enforce MFA.

Fetching the New Objects

This is how we can list all Azure AD objects created by the PowerShell commands above:

Get-MgApplication -Filter "DisplayName eq 'Claims X-Ray'" | Format-List
Get-MgServicePrincipal -Filter "DisplayName eq 'Claims X-Ray'" | Format-List
Get-MgPolicyClaimMappingPolicy -Filter "DisplayName eq 'Issue All Claims'" | Format-List

End-to-End Script

To wrap things up, here is the full PowerShell script, concatenated from the code snippets above:

#Requires -Version 5
#Requires -Modules Microsoft.Graph.Applications,Microsoft.Graph.Identity.SignIns

# Note: The required modules can be installed using the following command:
# Install-Module -Name Microsoft.Graph.Applications,Microsoft.Graph.Identity.SignIns -Scope AllUsers -Force

# Connect to AzureAD
# Note: The -TenantId parameter is also required when using a Microsoft Account.
Connect-MgGraph -Scopes @(
   'Application.ReadWrite.All',
   'AppRoleAssignment.ReadWrite.All',
   'DelegatedPermissionGrant.ReadWrite.All',
   'Policy.Read.All',
   'Policy.ReadWrite.ApplicationConfiguration'
)

# Register the application
[string] $appName = 'Claims X-Ray'
[string] $appDescription = 'Use the Claims X-ray service to debug and troubleshoot problems with claims issuance.'
[string] $redirectUrl = 'https://adfshelp.microsoft.com/ClaimsXray/TokenResponse'
[hashtable] $infoUrls = @{
    MarketingUrl =        'https://adfshelp.microsoft.com/Tools/ShowTools'
    PrivacyStatementUrl = 'https://privacy.microsoft.com/en-us/privacystatement'
    TermsOfServiceUrl   = 'https://learn.microsoft.com/en-us/legal/mdsa'
    SupportUrl          = 'https://adfshelp.microsoft.com/Feedback/ProvideFeedback'
}

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphApplication] $registeredApp =
   New-MgApplication -DisplayName $appName `
                     -Description $appDescription `
                     -Web @{ RedirectUris = $redirectUrl } `
                     -DefaultRedirectUri $redirectUrl `
                     -GroupMembershipClaims All `
                     -Info $infoUrls

Update-MgApplication -ApplicationId $registeredApp.Id `
                     -SignInAudience 'AzureADMyOrg' `
                     -IdentifierUris 'urn:microsoft:adfs:claimsxray'

# Configure application logo
[string] $logoUrl = 'https://www.dsinternals.com/assets/images/claims-xray-logo.png'
[string] $tempLogoPath = New-TemporaryFile
Invoke-WebRequest -Uri $logoUrl -OutFile $tempLogoPath -UseBasicParsing

Invoke-GraphRequest -Method PUT -Uri "https://graph.microsoft.com/v1.0/applications/$($registeredApp.Id)/logo" `
                    -InputFilePath $tempLogoPath `
                    -ContentType 'image/*'

Remove-Item -Path $tempLogoPath

# Create the service principal
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphServicePrincipal] $servicePrincipal =
   New-MgServicePrincipal -DisplayName $appName `
                          -AppId $registeredApp.AppId `
                          -AccountEnabled `
                          -ServicePrincipalType Application `
                          -PreferredSingleSignOnMode saml `
                          -ReplyUrls $redirectUrl `
                          -Notes $appDescription `
                          -Tags 'WindowsAzureActiveDirectoryIntegratedApp','WindowsAzureActiveDirectoryCustomSingleSignOnApplication'

# Generate a new token-signing certificate
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphSelfSignedCertificate] $tokenSigningCertificate =
   Add-MgServicePrincipalTokenSigningCertificate -ServicePrincipalId $servicePrincipal.Id `
                                                 -DisplayName "CN=$appName AAD Token Signing" `
                                                 -EndDateTime (Get-Date).AddYears(3)

# Delegate the User.Read permission
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphServicePrincipal] $microsoftGraph =
    Get-MgServicePrincipal -Filter "DisplayName eq 'Microsoft Graph'"

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphPermissionScope] $userReadScope =
    $microsoftGraph.Oauth2PermissionScopes | Where-Object Value -eq 'User.Read'

Update-MgApplication -ApplicationId $registeredApp.Id -RequiredResourceAccess @{
    ResourceAppId = $microsoftGraph.AppId
    ResourceAccess = @(@{
        id = $userReadScope.Id
        type = 'Scope'
    })
}

# Approve the User.Read permission on behalf of all tenant users
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphOAuth2PermissionGrant] $adminConsent =
    New-MgOauth2PermissionGrant -ClientId $servicePrincipal.Id `
                                -ConsentType AllPrincipals `
                                -ResourceId $microsoftGraph.Id `
                                -Scope $userReadScope.Value

# Assign the application to the current user
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphDirectoryObject] $currentUser =
    Invoke-GraphRequest -Method GET -Uri 'https://graph.microsoft.com/v1.0/me'

[string] $defaultAppAccessRole = [Guid]::Empty
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphAppRoleAssignment] $appAssignment =
   New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $servicePrincipal.Id `
                                           -ResourceId $servicePrincipal.Id `
                                           -AppRoleId $defaultAppAccessRole `
                                           -PrincipalType User `
                                           -PrincipalId $currentUser.Id

# Configure optional claims
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphOptionalClaims] $optionalClaims = [Microsoft.Graph.PowerShell.Models.MicrosoftGraphOptionalClaims]::DeserializeFromDictionary(@{
   Saml2Token = @(
      @{ Name = 'acct' },
      @{ Name = 'groups' }
   )
})

Update-MgApplication -ApplicationId $registeredApp.Id -OptionalClaims $optionalClaims

# Create a new claims mapping policy
[string] $allClaimsMapping = @'
{
   "ClaimsMappingPolicy": {
       "Version": 1,
       "IncludeBasicClaimSet": "true",
       "ClaimsSchema": [
           {
               "Source": "user",
               "ID": "userprincipalname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"
           },
           {
               "Source": "user",
               "ID": "userprincipalname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
           },
           {
               "Source": "user",
               "ID": "userprincipalname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"
           },
           {
               "Source": "user",
               "ID": "mail",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
           },
           {
               "Source": "user",
               "ID": "givenname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
           },
           {
               "Source": "user",
               "ID": "surname",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
           },
           {
               "Source": "user",
               "ID": "streetaddress",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress"
           },
           {
               "Source": "user",
               "ID": "city",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality"
           },
           {
               "Source": "user",
               "ID": "postalcode",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode"
           },
           {
               "Source": "user",
               "ID": "state",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince"
           },
           {
               "Source": "user",
               "ID": "country",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country"
           },
           {
               "Source": "user",
               "ID": "mobilephone",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone"
           },
           {
               "Source": "user",
               "ID": "telephonenumber",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone"
           },
           {
               "Source": "user",
               "ID": "facsimiletelephonenumber",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone"
           },
           {
               "Source": "user",
               "ID": "employeeid",
               "SamlClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/employeeid"
           },
           {
               "Source": "user",
               "ID": "onpremisesimmutableid",
               "SamlClaimType": "http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID"
           },
           {
               "Source": "application",
               "ID": "objectid",
               "SamlClaimType": "http://schemas.microsoft.com/2012/01/requestcontext/claims/relyingpartytrustid"
           }
       ],
       "ClaimsTransformation": []
   }
}
'@

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphClaimsMappingPolicy] $allClaimsPolicy =
   New-MgPolicyClaimMappingPolicy -DisplayName 'Issue All Claims' -Definition $allClaimsMapping

# Assign the claims mapping policy to the application
New-MgServicePrincipalClaimMappingPolicyByRef -ServicePrincipalId $servicePrincipal.Id -OdataId "https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies/$($allClaimsPolicy.Id)"

# Open the Claims X-Ray app in a browser
# Note that it might take a minute for the application to become accessible.
Start-Process ('https://myapps.microsoft.com/signin/{0}?tenantId={1}' -f $servicePrincipal.AppId,$servicePrincipal.AppOwnerOrganizationId)