functions/get-d365odatatokeninteractive.ps1
<# .SYNOPSIS Get OAuth 2.0 token to be used against OData or Custom Service, via an interactive sign-in flow .DESCRIPTION Get an OAuth 2.0 bearer token to be used against the OData or Custom Service endpoints of the Dynamics 365 Finance & Operations It will be running as an interactive sign-in flow, based on what is know as the device authentication flow Your clipboard will be set with a device code, and your default browser will navigate to "https://microsoft.com/devicelogin" You will have to paste in the device code, and complete an ordinary sign-in with your credentials When your sign-in is complete, it will pick up the OAuth 2.0 bearer token from the Azure AD .PARAMETER Tenant Azure Active Directory (AAD) tenant id (Guid) that the D365FO environment is connected to, that you want to access through OData .PARAMETER Url URL / URI for the D365FO environment you want to be working against If you are working against a D365FO instance, it will be the URL / URI for the instance itself If you are working against a D365 Talent / HR instance, this will have to be "http://hr.talent.dynamics.com" .PARAMETER Timeout Instruct the cmdlet how long time you need to be able to complete the interactive logon The default value is: 300 seconds Note: The parameter doesn't show up in the intellisense when tabbing through all available parameters, as it shouldn't be necessary to change the value .PARAMETER EnableException This parameters disables user-friendly warnings and enables the throwing of exceptions This is less user friendly, but allows catching exceptions in calling scripts .PARAMETER RawOutput Instructs the cmdlet to output the raw token object and all its properties .EXAMPLE PS C:\> Get-D365ODataTokenInteractive This will start an interactive sign-in process to the Azure AD. It will utilize the active OData configuration for the Tenant(Id) and the Url (Resource). It will copy the device code into your clipboard. It will start the default browser and nagivate to "https://microsoft.com/devicelogin". It will wait the default amount of seconds for you to complete the interactive sign-in. The output will be a formal formatted bearer token, ready to be used right away. It will use the default OData configuration details that are stored in the configuration store. .EXAMPLE PS C:\> Get-D365ODataTokenInteractive -RawOutput This will start an interactive sign-in process to the Azure AD. It will utilize the active OData configuration for the Tenant(Id) and the Url (Resource). It will copy the device code into your clipboard. It will start the default browser and nagivate to "https://microsoft.com/devicelogin". It will wait the default amount of seconds for you to complete the interactive sign-in. It will output all properties of the token. It will use the default OData configuration details that are stored in the configuration store. .EXAMPLE PS C:\> Get-D365ODataTokenInteractive -Timeout 100 This will start an interactive sign-in process to the Azure AD. It will utilize the active OData configuration for the Tenant(Id) and the Url (Resource). It will copy the device code into your clipboard. It will start the default browser and nagivate to "https://microsoft.com/devicelogin". It will wait 100 seconds for you to complete the interactive sign-in. The output will be a formal formatted bearer token, ready to be used right away. It will use the default OData configuration details that are stored in the configuration store. .EXAMPLE PS C:\> Get-D365ODataTokenInteractive | Set-D365ODataTokenInSession This sets the Token parameter value for all cmdlets, for the remaining of the session. It gets a token from the Get-D365ODataTokenInteractive cmdlet and pipes it into Set-D365ODataTokenInSession. .LINK Add-D365ODataConfig .LINK Get-D365ActiveODataConfig .LINK Set-D365ActiveODataConfig .NOTES Tags: OData, OAuth, Token, JWT, DeviceAuth, Device, DeviceCode Inspiration: https://blog.simonw.se/getting-an-access-token-for-azuread-using-powershell-and-device-login-flow/ Author: Mötz Jensen (@Splaxi) #> function Get-D365ODataTokenInteractive { [CmdletBinding(DefaultParameterSetName = "Default")] [OutputType()] param ( [Alias('$AadGuid')] [string] $Tenant = $Script:ODataTenant, [Alias('Uri')] [Alias('Resource')] [string] $Url = $Script:ODataUrl, [Parameter(DontShow = $true)] [int] $Timeout = 300, [switch] $EnableException, [switch] $RawOutput ) begin { if ([System.String]::IsNullOrEmpty($Url)) { $messageString = "It seems that you didn't supply a valid value for the Url parameter. You need specify the Url parameter or add a configuration with the <c='em'>Add-D365ODataConfig</c> cmdlet." Write-PSFMessage -Level Host -Message $messageString -Exception $PSItem.Exception -Target $entityName Stop-PSFFunction -Message "Stopping because of errors." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) -ErrorRecord $_ return } if ($Url.Substring($Url.Length - 1) -eq "/") { Write-PSFMessage -Level Verbose -Message "The Url parameter had a tailing slash, which shouldn't be there. Removing the tailling slash." -Target $Url $Url = $Url.Substring(0, $Url.Length - 1) } # Known ClientId for PowerShell in Azure AD $clientID = '1950a258-227b-4e31-a9cf-717495945fc2' } process { $azureUriDeviceCode = $Script:AzureTenantOauthDevicecode $azureUriToken = $Script:AzureTenantOauthToken $DeviceCodeRequestParams = @{ Method = 'POST' Body = @{ resource = $Url client_id = $ClientId } } $DeviceCodeRequestParams.Uri = $azureUriDeviceCode -f $Tenant $DeviceCodeRequest = Invoke-RestMethod @DeviceCodeRequestParams $DeviceCodeRequest.user_code | Set-Clipboard Write-PSFMessage -Level Host -Message "The device code <c='em'>$($DeviceCodeRequest.user_code)</c> has been copied into your clipboard." Start-Sleep -Seconds 2 Write-PSFMessage -Level Host -Message "Will start the default browser and have it open the <c='em'>$($DeviceCodeRequest.verification_url)</c> page, where you need to <c='em'>paste</c> the code in and complete sign-in with your credentials." Start-Sleep -Seconds 2 Start-Process $DeviceCodeRequest.verification_url $TokenRequestParams = @{ Method = 'POST' Body = @{ grant_type = "urn:ietf:params:oauth:grant-type:device_code" code = $DeviceCodeRequest.device_code client_id = $ClientId } } $TokenRequestParams.Uri = $azureUriToken -f $Tenant $TimeoutTimer = [System.Diagnostics.Stopwatch]::StartNew() while ([string]::IsNullOrEmpty($tokenObj.access_token)) { if (Test-PSFFunctionInterrupt) { return } if ($TimeoutTimer.Elapsed.TotalSeconds -gt $Timeout) { $messageString = "The login session <c='em'>timed out</c>. You have <c='em'>$Timeout seconds</c> to complete the sign-in operation." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because login took to long." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } $tokenObj = try { Invoke-RestMethod @TokenRequestParams -ErrorAction Stop } catch { $Message = $_.ErrorDetails.Message | ConvertFrom-Json if ($Message.error -ne "authorization_pending") { throw } } Start-Sleep -Seconds 1 } if ($RawOutput) { $tokenObj } else { $tokenObj | Get-BearerToken } } } |