title | description | author | ms.author | ms.topic | ms.date | ms.reviewer | audience | ms.search.region |
---|---|---|---|---|---|---|---|---|
Cloud-hosted environments can't use business events or virtual entities and receive a 400 error |
Learn about how you can troubleshoot business events in cloud-hosted environments, including a PowerShell script. |
ramasri |
ramasri |
article |
06/26/2023 |
johnmichalak |
IT Pro |
Global for most topics. Set Country/Region name for localizations |
When you set up business events or virtual entities in a cloud-hosted environment, you might receive the following error message:
Response Status code does not indicate success : 400 ({"error":"invalid_client","error_description";"Expected aud
https://securityservice.operations365.dynamics.com
but found."})
The error occurs because cloud-hosted environments and on-premises environments (also known as local business data [LBD] environments) don't use Security Service. Follow these steps to manually complete the setup.
-
Create a new app registration in Microsoft Entra ID. Save the Application client ID value.
-
In the new app registration, create a new secret. Save the Secret value.
-
In Power Platform admin center, go to Environments > Settings > Users + Permissions > Application users > New App User, create an app user for the application client ID that you saved in step 1, and assign the appropriate role.
-
Go to Dynamics 365 Finance and Operations System Administration > Microsoft Entra Application, and add the application client ID that you saved in step 1.
-
In the Admin PowerShell console, run the following PowerShell script to refresh the integration of Dataverse via PowerShell in remote desktop in the cloud-hosted or LBD environment.
param( [Parameter(Mandatory = $false)] [switch]$Relaunched ) $isRelaunched = $false if ($PSBoundParameters.ContainsKey("Relaunched")) { $isRelaunched = $Relaunched.IsPresent } if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { # Relaunch as an elevated process: Start-Process powershell.exe "-File", ('"{0}"' -f $MyInvocation.MyCommand.Path), "-Relaunched" -Verb RunAs exit } $aosWebsiteName = "AOSService" function Get-AosWebSitePhysicalPath() { if (Get-Service W3SVC | Where-Object status -ne 'Running') { #IIS service is not running, starting IIS Service. Start-Service W3SVC } $webSitePhysicalPath = (Get-Website | Where-Object { $_.Name -eq $aosWebsiteName }).PhysicalPath return $webSitePhysicalPath } function Get-WebConfigValue($Key) { $webroot = Get-AosWebSitePhysicalPath $webConfigPath = Join-Path $webroot "web.config" if (-not (Test-Path $webConfigPath)) { Throw "Unable to find web.config file at '$($webConfigPath)'..." } [xml]$webConfigDocument = Get-Content $webConfigPath -ErrorAction stop $appSettingNode = $webConfigDocument.SelectSingleNode("/configuration/appSettings/add[@key='$($Key)']") if ($appSettingNode) { return $appSettingNode.Value } return $null } function Set-WebConfigValue($Key, [string]$Value) { $webroot = Get-AosWebSitePhysicalPath $webConfigPath = Join-Path $webroot "web.config" if (-not (Test-Path $webConfigPath)) { Throw "Unable to find web.config file at '$($webConfigPath)'..." } [xml]$webConfigDocument = Get-Content $webConfigPath -ErrorAction stop $appSettingNode = $webConfigDocument.SelectSingleNode("/configuration/appSettings/add[@key='$($Key)']") if ($null -ne $appSettingNode) { Write-Host "Updating key '$($Key)' to value '$($Value)'..." $appSettingNode.Value = [string]$Value } else { Write-Host "Inserting new key '$($Key)' with value '$($Value)'..." $ns = New-Object System.Xml.XmlNamespaceManager($webConfigDocument.NameTable) $ns.AddNamespace("ns", $webConfigDocument.DocumentElement.NamespaceURI) $addElement = $webConfigDocument.CreateElement("add") $addElement.SetAttribute("key", $Key) $addElement.SetAttribute("value", $Value) $appSettings = $webConfigDocument.SelectSingleNode("//ns:appSettings", $ns) $appSettings.AppendChild($addElement) | Out-Null } $webConfigDocument.Save($webConfigPath) Write-Host } function Confirm-ValueOfType($Value, $Type) { if ($Type -eq "Uri") { try { New-Object System.Uri $Value | Out-Null } catch { Throw "Cannot parse '$($Value)' as a URL: $($_)" } } elseif ($Type -eq "Guid") { try { [Guid]::Parse($Value) | Out-Null } catch { Throw "Cannot parse '$($Value)' as a guid: $($_)" } } elseif ($Type -eq "String") { if ([string]::IsNullOrEmpty($Value)) { Throw "String value cannot be empty." } } } function Update-WebConfigValueFromHost($Key, $Prompt, $Type) { $shouldUpdate = $true $currentValue = Get-WebConfigValue -Key $Key if ($currentValue) { if ($Type -eq "Secret") { $currentValue = "<redacted>" } while ($true) { $yesNoResponse = Read-Host -Prompt "Value for '$($Prompt)' is already set to '$($currentValue)'. Do you want to overwrite it? (y/n)" if ($yesNoResponse -eq "y" -or $yesNoResponse -eq "yes") { $shouldUpdate = $true break } elseif ($yesNoResponse -eq "n" -or $yesNoResponse -eq "no") { $shouldUpdate = $false break } else { Write-Host "Did not recognize input value '$($yesNoResponse)' - please try again." } } } if ($shouldUpdate) { $value = Read-Host -Prompt "Enter $($Prompt)" Confirm-ValueOfType -Value $value -Type $Type if ($Type -eq "Secret") { # If value is blank, assume we are trying to clear it $secretValue = "" if (-not [string]::IsNullOrEmpty($value)) { $webroot = Get-AosWebSitePhysicalPath -ErrorAction stop $webrootBinPath = Join-Path $webroot "bin" $b2bInvitationHelperDllPath = Join-Path $webrootBinPath "Microsoft.Dynamics.AX.Security.B2BInvitationHelper.dll" Add-Type -Path $b2bInvitationHelperDllPath $encryptionEngine = [Microsoft.Dynamics.AX.Security.B2BInvitationHelper.Cryptor]::GetEncryptionEngine() $secretValue = [System.Convert]::ToBase64String($encryptionEngine.Encrypt($value)) } $value = $secretValue } Set-WebConfigValue -Key $Key -Value $value } } function Enable-Flight($FlightName) { Write-Verbose "Enabling flight '$($FlightName)'..." $webroot = Get-AosWebSitePhysicalPath -ErrorAction stop $webrootBinPath = Join-Path $webroot "bin" $environmentDllPath = Join-Path $webrootBinPath 'Microsoft.Dynamics.ApplicationPlatform.Environment.dll' Add-Type -Path $environmentDllPath $config = [Microsoft.Dynamics.ApplicationPlatform.Environment.EnvironmentFactory]::GetApplicationEnvironment() $ServerName = $config.DataAccess.DbServer $DatabaseName = $config.DataAccess.Database $UserId = $config.DataAccess.SqlUser $Password = $config.DataAccess.SqlPwd $EnableFlightQuery = "DECLARE @flightName NVARCHAR(100) = '$($FlightName)'; IF NOT EXISTS (SELECT TOP 1 1 FROM SysFlighting WHERE flightName = @flightName) INSERT INTO SYSFLIGHTING(FLIGHTNAME,ENABLED, FLIGHTSERVICEID, PARTITION) SELECT @flightName, 1, 12719367, RECID FROM DBO.[PARTITIONS]; ELSE UPDATE SysFlighting SET enabled = 1, flightServiceId = 12719367 WHERE flightName = @flightName;" Invoke-Sqlcmd -ServerInstance $ServerName -Database $DatabaseName -Username $UserId -Password $Password -Query $EnableFlightQuery Write-Verbose "Flight '$($FlightName)' has been enabled." } function Test-Settings() { $cdsApiPath = "sdkmessages"; Write-Host "Testing setup by calling API '$($cdsApiPath)'..." $webroot = Get-AosWebSitePhysicalPath -ErrorAction stop $webrootBinPath = Join-Path $webroot "bin" $httpCommunicationDllPath = Join-Path $webrootBinPath "Microsoft.Dynamics.HttpCommunication.dll" Add-Type -Path $httpCommunicationDllPath try { $assembly = [System.Reflection.Assembly]::LoadFile($httpCommunicationDllPath) $loggerType = $assembly.GetType("Microsoft.Dynamics.HttpCommunication.Logging.InMemoryLogger") $bindingFlags = [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::Public $loggerConstructor = $loggerType.GetConstructor($bindingFlags, $null, [System.Type]::EmptyTypes, $null) $logger = $loggerConstructor.Invoke($null) $cdsWebApiClient = New-Object Microsoft.Dynamics.HttpCommunication.Cds.CdsWebApiClient $logger; $bindingFlags = [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic $method = [Microsoft.Dynamics.HttpCommunication.Cds.CdsWebApiClient].GetMethod("GetWithStringResponse", $bindingFlags, $null, @([string]), $null) $task = $method.Invoke($cdsWebApiClient, @($cdsApiPath)) $response = $task.GetAwaiter().GetResult() Write-Host $logger.LogContent.ToString() Write-Host "Received response with length: $($response.Length)" Write-Host "Test complete." } catch { Write-Verbose $logger.LogContent.ToString() Throw "Failed while testing the new settings: $($_)" } } try { Update-WebConfigValueFromHost -Key "Infrastructure.CdsOrganizationUrl" -Prompt "Dataverse Organization URL" -Type "Uri" Update-WebConfigValueFromHost -Key "Infrastructure.CdsOrganizationId" -Prompt "Dataverse Organization id" -Type "Guid" Update-WebConfigValueFromHost -Key "Infrastructure.DataverseCommunicationAadTenantId" -Prompt "Dataverse Microsoft Entra Tenant domain (e.g. Contoso.OnMicrosoft.com)" -Type "String" Update-WebConfigValueFromHost -Key "Infrastructure.DataverseCommunicationAppId" -Prompt "Dataverse Microsoft Entra App id" -Type "Guid" Update-WebConfigValueFromHost -Key "Infrastructure.DataverseCommunicationAppSecretEncrypted" -Prompt "Dataverse Microsoft Entra App secret" -Type "Secret" Enable-Flight -FlightName "BusinessEventsCDSIntegration" Write-Host "Restarting AOS..." Stop-Website -Name $aosWebSiteName Start-Website -Name $aosWebSiteName Write-Host "AOS has been restarted." Test-Settings } catch { Write-Error $_ } if ($isRelaunched) { Write-Host "Press any key to continue..." [System.Console]::ReadKey() | Out-Null }
[!INCLUDEfooter-include]