functions/test-d365entraintegration.ps1


<#
    .SYNOPSIS
        Test the Entra Id integration
         
    .DESCRIPTION
        Validates the configuration of the web.config file and the certificate for the environment
         
        If any of the configuration is missing or in someway incorrect, it will prompt and stating corrective actions needed
         
    .EXAMPLE
        PS C:\> Test-D365EntraIntegration
         
        This will validate the settings inside the web.config file.
        It will search for Aad.Realm, Infrastructure.S2SCertThumbprint, GraphApi.GraphAPIServicePrincipalCert
        It will search for the certificate that matches the thumbprint.
         
        A result set example:
         
        EntraAppId Thumbprint Subject Expiration
        ---------- ---------- ------- ----------
        e068e004-8bec-48c3-a36f-2ab4982ee738 0768175DF3DFDEA3FA78925ADC1E588707649335 CN=CHEAuth 2/5/2026 8:09:28 AM
         
    .NOTES
        Based on: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/dev-tools/secure-developer-vm#external-integrations
         
        Author: Mötz Jensen (@Splaxi)
#>

function Test-D365EntraIntegration {
    param (
    )

    if (-not ($Script:IsAdminRuntime)) {
        Write-PSFMessage -Level Critical -Message "It seems that you ran this cmdlet <c='em'>non-elevated</c>. Testing the Entra integration requires you to run this cmdlet from an elevated console. Please exit the current console and start a new with `"Run As Administrator`""
        Stop-PSFFunction -Message "Stopping because the function is not run elevated"
        return
    }

    # Check web.config
    $webConfigFile = Join-Path -Path $Script:AOSPath $Script:WebConfig
    
    if (-not (Test-PathExists -Path $webConfigFile -Type Leaf -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)) {
        Write-PSFMessage -Level Host -Message "Unable to find the web.config file."
        Stop-PSFFunction -Message "Stopping because the web.config file could not be found"
    }
    
    $config = @{}

    [xml]$xml = Get-Content $webConfigFile
    $nodes = ($xml.configuration.appSettings).ChildNodes

    $config.AadRealm = $nodes | Where-Object -Property Key -eq "Aad.Realm" | Select-Object -First 1 -ExpandProperty value
    $config.S2SCertThumbprint = $nodes | Where-Object -Property Key -eq "Infrastructure.S2SCertThumbprint" | Select-Object -First 1 -ExpandProperty value
    $config.GraphAPIServicePrincipalCert = $nodes | Where-Object -Property Key -eq "GraphApi.GraphAPIServicePrincipalCert" | Select-Object -First 1 -ExpandProperty value
    
    if ([System.String]::IsNullOrWhiteSpace($config.AadRealm)) {
        Write-PSFMessage -Level Host -Message "The <c='em'>'Aad.Realm'</c> value is empty. This indicates that you need to run the <c='em'>'New-D365EntraIntegration'</c> cmdlet."
        Stop-PSFFunction -Message "Stopping because the 'Aad.Realm' value is empty"
    }
    
    if ([System.String]::IsNullOrWhiteSpace($config.S2SCertThumbprint)) {
        Write-PSFMessage -Level Host -Message "The <c='em'>'Infrastructure.S2SCertThumbprint'</c> value is empty. This indicates that you need to run the <c='em'>'New-D365EntraIntegration'</c> cmdlet."
        Stop-PSFFunction -Message "Stopping because the 'Infrastructure.S2SCertThumbprint' value is empty"
    }

    if ([System.String]::IsNullOrWhiteSpace($config.GraphAPIServicePrincipalCert)) {
        Write-PSFMessage -Level Host -Message "The <c='em'>'GraphApi.GraphAPIServicePrincipalCert'</c> value is empty. This indicates that you need to run the <c='em'>'New-D365EntraIntegration'</c> cmdlet."
        Stop-PSFFunction -Message "Stopping because the 'GraphApi.GraphAPIServicePrincipalCert' value is empty"
    }

    if ((-not [System.String]::IsNullOrWhiteSpace($config.S2SCertThumbprint)) -and $config.S2SCertThumbprint -ne $config.GraphAPIServicePrincipalCert) {
        Write-PSFMessage -Level Host -Message "The <c='em'>'Infrastructure.S2SCertThumbprint'</c> and the <c='em'>'GraphApi.GraphAPIServicePrincipalCert'</c> value do not match each other. This indicates that you have a <c='em'>corrupted</c> configuration. Running the <c='em'>'New-D365EntraIntegration'</c> cmdlet could assist with fixing the configuration."
        Stop-PSFFunction -Message "Stopping because the 'Infrastructure.S2SCertThumbprint' and 'GraphApi.GraphAPIServicePrincipalCert' values do not match"
    }

    if (Test-PSFFunctionInterrupt) { return }

    # Check wif.config
    $wifConfigFile = Join-Path -Path $Script:AOSPath $Script:WifConfig
    
    if (-not (Test-PathExists -Path $wifConfigFile -Type Leaf -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)) {
        Write-PSFMessage -Level Host -Message "Unable to find the wif.config file."
        Stop-PSFFunction -Message "Stopping because the wif.config file could not be found"
    }
    
    [xml]$xml = Get-Content $wifConfigFile
    $nodes = ($xml.'system.identityModel'.identityConfiguration.securityTokenHandlers.securityTokenHandlerConfiguration.audienceUris).ChildNodes
    $config.AudienceUri = $nodes | Where-Object { $_.value -like "*$($config.AadRealm)*" } | Select-Object -First 1 -ExpandProperty value

    if ([System.String]::IsNullOrWhiteSpace($config.AudienceUri)) {
        Write-PSFMessage -Level Host -Message "The <c='em'>'AudienceUri'</c> value is empty. This may not be needed, but in case of issues, try running the <c='em'>'New-D365EntraIntegration'</c> cmdlet with the -AddAppRegistrationToWifConfig switch."
    }
    elseif ($config.AadRealm -ne $config.AudienceUri) {
        Write-PSFMessage -Level Host -Message "The <c='em'>'Aad.Realm'</c> and the <c='em'>'AudienceUri'</c> value do not match each other. This indicates that you have a <c='em'>corrupted</c> configuration. Running the <c='em'>'New-D365EntraIntegration'</c> cmdlet with the -AddAppRegistrationToWifConfig switch could assist with fixing the configuration."
        Stop-PSFFunction -Message "Stopping because the 'Aad.Realm' and 'AudienceUri' values do not match"
    }

    if (Test-PSFFunctionInterrupt) { return }

    # Check certificate
    $certStoreLocation = "Cert:\LocalMachine\My"
    
    $certEntra = Get-ChildItem -Path $certStoreLocation -ErrorAction SilentlyContinue | Where-Object { $_.Thumbprint -eq $config.S2SCertThumbprint } | Select-Object -First 1
    
    if ($null -eq $certEntra) {
        Write-PSFMessage -Level Host -Message "Unable to find any certificate in the certificate store <c='em'>'$certStoreLocation'</c> that matches the thumbprint <c='em'>'$($config.S2SCertThumbprint)'</c>."
        Stop-PSFFunction -Message "Stopping because no certificate matching the thumbprint was found"
        return
    }

    [PSCustomObject][ordered]@{
        EntraAppId = $config.AadRealm.replace("spn:", "")
        Thumbprint = $config.S2SCertThumbprint
        Subject    = $certEntra.Subject
        Expiration = $certEntra.NotAfter
    }
}