Private/Comparison/Find-MissingPolicies.ps1

function Find-MissingPolicies {
    <#
    .SYNOPSIS
    Finds baseline policies that are not assigned in Azure.
     
    .DESCRIPTION
    Compares the baseline policies against assigned policies to identify
    which baseline policies are missing (not deployed).
    Supports both ID-based and name-based matching strategies.
     
    .PARAMETER Baseline
    Array of baseline policy objects with PolicyDefinitionId and PolicyDisplayName.
     
    .PARAMETER AssignmentIndex
    Hashtable for fast lookup of assigned policies (ById or ByNormName).
     
    .PARAMETER MatchByNameOnly
    If true, matches by normalized name only. If false, matches by ID first, then name.
     
    .EXAMPLE
    $missing = Find-MissingPolicies -Baseline $baseline `
                                     -AssignmentIndex $assignedByNormName `
                                     -MatchByNameOnly $true
     
    Returns array of baseline policies not found in assignments.
     
    .OUTPUTS
    Array of PSCustomObject baseline policies that are missing from assignments.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object[]]$Baseline,
        
        [Parameter(Mandatory)]
        [hashtable]$AssignmentIndex,
        
        [Parameter()]
        [bool]$MatchByNameOnly = $false
    )
    
    $missing = foreach ($b in $Baseline) {
        if ($MatchByNameOnly) {
            # Match by normalized name only
            $k = Normalize-PolicyName $b.PolicyDisplayName
            if (-not $k) { continue }
            if (-not $AssignmentIndex.ContainsKey($k)) { $b }
        }
        else {
            # Match by ID first, fallback to name
            if (-not $AssignmentIndex.ContainsKey($b.PolicyDefinitionId)) {
                $k = Normalize-PolicyName $b.PolicyDisplayName
                if ($k -and -not $AssignmentIndex.ContainsKey($k)) { $b }
                elseif (-not $k) { $b }
            }
        }
    }
    
    return $missing
}