Published on

Use PowerShell to query Dynamics CRM Web API

Authors
  • avatar
    Name
    Jonathan Devere-Ellery
    Twitter

Recently I was asked how to get an Access Token from AzureAD which could then be used to make requests on a Dynamics CRM web API endpoint.

In my case I already had an Enterprise Application in my tenant that I wanted to use, but it was only configured with Application permissions so I was using a client secret and a ConfidentialClient login flow.

The Dynamics permission however is Delegated permission with the user_impersonation scope, so I need to add a PublicClient login flow to the existing application, to avoid a RedirectUri mismatch error when I try and get a token.

I can add this flow by using the Microsoft.Graph.Applications module and to modify the existing app to add the require Redirect URI:

Connect-MgGraph
Update-MgApplication -ApplicationId "b1befa1f-2776-4363-a6c3-4a09c0bfaa2d" -PublicClient @{'RedirectUris'='https://login.microsoftonline.com/common/oauth2/nativeclient'}

I also added the Dynamics CRM Delegated permissions to allow user impersonation (https://admin.services.crm.dynamics.com/user_impersonation) permission scope to my existing AAD App.

Now was to craft a valid Access Token using the MSAL library. I typically piggyback off the top of the ExchangeOnlineManagement module which is normally already installed on all of my machines, and I avoid installing anything extra.

$ExoModule = Get-Module -Name "ExchangeOnlineManagement" -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1
$MSAL = Join-Path $ExoModule.ModuleBase "NetFramework\Microsoft.Identity.Client.dll"
Try {Add-Type -LiteralPath $MSAL | Out-Null} Catch {}

Since every Dynamics instance is going to have its own unique name, I needed to query what the name of my instance and the Web API URL was:

Install-Module Microsoft.PowerApps.Administration.PowerShell
$APIUrl = (Get-AdminPowerAppEnvironment).Internal.Properties.linkedEnvironmentMetadata.instanceApiUrl

I need to populate which App in the tenant am I going to request an Access Token for, and then add scopes for Dynamics permissions:

$App = Get-AzureADApplication -All $True | Where-Object {$_.DisplayName -eq "Contoso Test App"}
$Authority = "https://login.microsoftonline.com/contosocom.onmicrosoft.com"

$Scopes = New-Object System.Collections.Generic.List[string]
$Scopes.Add("$APIUrl/.default")

Last but not least, I use the PublicClientApplicationBuilder class to finish building my token request, and then I start an interactive login to get my token.

$pubApp = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($App.AppId).WithRedirectUri('https://login.microsoftonline.com/common/oauth2/nativeclient').WithAuthority($Authority).Build()
$Token = $pubApp.AcquireTokenInteractive($Scopes).ExecuteAsync().GetAwaiter().GetResult()

Alternately you could also use the .WithDefaultRedirectUri() method for the Redirect Uri which would have the same result, but I prefer to explicitly define the URL I want.

Now we (should) have a valid access token stored in our $Token variable, which and we can use to make REST queries the Web API of our environment

(Invoke-RestMethod -Uri "$APIUrl/api/data/v9.2/organizations" -Headers @{'Authorization'="$($Token.TokenType) $($Token.AccessToken)"}).Value

Hope this is helpful.