- Published on
Use PowerShell to query Dynamics CRM Web API
- Authors
- Name
- Jonathan Devere-Ellery
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.