Private/Get-HydrationDeleteCandidates.ps1

function Get-HydrationDeleteCandidates {
    <#
    .SYNOPSIS
        Finds Graph objects that are safe delete candidates for hydration cleanup.
    .DESCRIPTION
        Enumerates objects from Graph list endpoints, keeps only objects created by the
        Intune Hydration Kit, and optionally restricts matches to known template names.
        If a list response omits description or notes, the function performs a targeted
        GET for likely matches so the hydration marker can still be verified safely.
    .PARAMETER Endpoint
        One or more beta Graph endpoints to enumerate.
    .PARAMETER KnownTemplateNames
        Optional case-insensitive HashSet of current template names used to scope deletes.
    .PARAMETER RequireTemplateMatch
        When specified, only returns hydration kit objects whose names match a current template.
    .OUTPUTS
        hashtable[] containing Name, Id, and Url for batch delete operations.
    #>

    [CmdletBinding()]
    [OutputType([hashtable[]])]
    param(
        [Parameter(Mandatory)]
        [string[]]$Endpoint,

        [Parameter()]
        [System.Collections.Generic.HashSet[string]]$KnownTemplateNames,

        [Parameter()]
        [switch]$RequireTemplateMatch
    )

    $deleteCandidates = @()
    $importPrefix = if ([string]::IsNullOrEmpty($script:ImportPrefix)) { '[IHD] ' } else { $script:ImportPrefix }
    $escapedPrefix = [regex]::Escape($importPrefix)

    foreach ($currentEndpoint in $Endpoint) {
        try {
            $allPolicies = Get-GraphPagedResults -Uri $currentEndpoint
            foreach ($policy in $allPolicies) {
                $policyName = if ($policy.displayName) {
                    $policy.displayName
                } elseif ($policy.name) {
                    $policy.name
                } else {
                    'Unknown'
                }

                $unprefixedName = $policyName -replace "^$escapedPrefix", ''
                $matchesTemplateName = $false
                if ($KnownTemplateNames) {
                    $matchesTemplateName = $KnownTemplateNames.Contains($policyName) -or $KnownTemplateNames.Contains($unprefixedName)
                }

                $isHydrationKitObject = Test-HydrationKitObject -Description $policy.description -Notes $policy.notes -ObjectName $policyName
                $shouldVerifyWithGet = -not $isHydrationKitObject -and
                [string]::IsNullOrWhiteSpace($policy.description) -and
                [string]::IsNullOrWhiteSpace($policy.notes) -and
                ($policyName -match "^$escapedPrefix" -or $matchesTemplateName)

                if ($shouldVerifyWithGet) {
                    try {
                        $fullPolicy = Invoke-MgGraphRequest -Method GET -Uri "$currentEndpoint/$($policy.id)" -ErrorAction Stop
                        $isHydrationKitObject = Test-HydrationKitObject -Description $fullPolicy.description -Notes $fullPolicy.notes -ObjectName $policyName
                    } catch {
                        Write-Verbose "Could not verify hydration marker for '$policyName' from $currentEndpoint/$($policy.id): $_"
                    }
                }

                if (-not $isHydrationKitObject) {
                    Write-Verbose "Skipping '$policyName' - not created by Intune Hydration Kit"
                    continue
                }

                if ($RequireTemplateMatch -and $KnownTemplateNames -and -not $matchesTemplateName) {
                    continue
                }

                if (-not $RequireTemplateMatch -and $KnownTemplateNames -and -not $matchesTemplateName) {
                    Write-Verbose "Policy '$policyName' not in current templates (may be from an older baseline version) - deleting based on hydration kit marker"
                }

                $deleteCandidates += @{
                    Name = $policyName
                    Id   = $policy.id
                    Url  = "/$($currentEndpoint -replace '^beta/', '')/$($policy.id)"
                }
            }
        } catch {
            Write-Warning "Failed to list policies from $currentEndpoint : $_"
        }
    }

    return $deleteCandidates
}