Private/Comparison/Find-VersionMismatches.ps1

function Find-VersionMismatches {
    <#
    .SYNOPSIS
    Finds policies with version differences between baseline and assignments.
     
    .DESCRIPTION
    Compares policy versions between baseline and assigned policies.
    Only reports on policies that exist in both (common policies).
    Supports both ID-based and name-based matching strategies.
     
    .PARAMETER AssignedPolicies
    Array of assigned policy objects with PolicyDefinitionId, PolicyDisplayName, and Version.
     
    .PARAMETER BaselineIndex
    Hashtable for fast lookup of baseline policies with version info.
     
    .PARAMETER MatchByNameOnly
    If true, matches by normalized name only. If false, matches by ID first, then name.
     
    .EXAMPLE
    $versionDiffs = Find-VersionMismatches -AssignedPolicies $assRefs `
                                           -BaselineIndex $baselineIndex `
                                           -MatchByNameOnly $false
     
    Returns array of policies with version mismatches.
     
    .OUTPUTS
    Array of PSCustomObject with PolicyDefinitionId, PolicyDisplayName, BaselineVersion,
    AssignmentVersion, and BaselineSources properties.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object[]]$AssignedPolicies,
        
        [Parameter(Mandatory)]
        [object]$BaselineIndex,
        
        [Parameter()]
        [bool]$MatchByNameOnly = $false
    )
    
    $versionDiffs = foreach ($r in $AssignedPolicies) {
        if ($MatchByNameOnly) {
            # Match by normalized name
            $k = Normalize-PolicyName $r.PolicyDisplayName
            if (-not $k) { continue }
            
            if ($BaselineIndex.ByNormName.ContainsKey($k)) {
                $base = $BaselineIndex.ByNormName[$k] | Select-Object -First 1
                if ($base.Version -ne $r.Version) {
                    [PSCustomObject]@{
                        PolicyDefinitionId = $r.PolicyDefinitionId
                        PolicyDisplayName  = $r.PolicyDisplayName
                        BaselineVersion    = $base.Version
                        AssignmentVersion  = $r.Version
                        BaselineSources    = $base.BaselineSources
                    }
                }
            }
        }
        else {
            # Match by ID first, fallback to name
            $base = $BaselineIndex.ById[$r.PolicyDefinitionId]
            
            if (-not $base -and $BaselineIndex.ByName.ContainsKey($r.PolicyDisplayName)) {
                $base = $BaselineIndex.ByName[$r.PolicyDisplayName]
            }
            
            if ($base -and $base.Version -ne $r.Version) {
                [PSCustomObject]@{
                    PolicyDefinitionId = $r.PolicyDefinitionId
                    PolicyDisplayName  = $r.PolicyDisplayName
                    BaselineVersion    = $base.Version
                    AssignmentVersion  = $r.Version
                    BaselineSources    = $base.BaselineSources
                }
            }
        }
    }
    
    return $versionDiffs
}