Private/Compare-NSPConditionalAccessCanonicalPolicy.ps1

function Compare-NSPConditionalAccessCanonicalPolicy {
    <#
    .SYNOPSIS
        Field-by-field comparison of two canonical policies: the differing fields plus a similarity score.
    .DESCRIPTION
        Score is the share of fields, across both sides, whose values are equal (0 to 1). It ranks
        candidates when adopting existing policies. It is never a reason to act without review.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][System.Collections.IDictionary]$Live,
        [Parameter(Mandatory)][System.Collections.IDictionary]$Desired
    )

    $paths = @(@($Live.Keys) + @($Desired.Keys) | Sort-Object -Unique)
    $differences = foreach ($path in $paths) {
        $liveValue = if ($Live.Contains($path)) { $Live[$path] } else { $null }
        $desiredValue = if ($Desired.Contains($path)) { $Desired[$path] } else { $null }
        $liveText = ConvertTo-Json -InputObject $liveValue -Compress
        $desiredText = ConvertTo-Json -InputObject $desiredValue -Compress
        if ($liveText -cne $desiredText) {
            [pscustomobject]@{ Field = $path; Live = $liveValue; Desired = $desiredValue }
        }
    }
    $differences = @($differences)
    [pscustomobject]@{
        Differences = $differences
        Score = if ($paths.Count) { [math]::Round(($paths.Count - $differences.Count) / $paths.Count, 3) } else { 1 }
    }
}