Private/AD/Core/Resolve-ADSid.ps1

# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0
# https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/
# AI/LLM use: see AI-USAGE.md for required attribution
function Resolve-ADSid {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$SidString,

        [System.DirectoryServices.DirectoryEntry]$SearchRoot
    )

    # Check cache first
    if ($script:SidCache.ContainsKey($SidString)) {
        return $script:SidCache[$SidString]
    }

    # Check well-known SIDs
    if ($script:WellKnownSids.ContainsKey($SidString)) {
        $name = $script:WellKnownSids[$SidString]
        $script:SidCache[$SidString] = $name
        return $name
    }

    # Check domain-relative well-known RIDs
    $sidParts = $SidString -split '-'
    if ($sidParts.Count -ge 5) {
        $rid = [int]$sidParts[-1]
        if ($script:WellKnownRids.ContainsKey($rid)) {
            $name = $script:WellKnownRids[$rid]
            $script:SidCache[$SidString] = $name
            return $name
        }
    }

    # Try .NET translation
    try {
        $sid = New-Object System.Security.Principal.SecurityIdentifier($SidString)
        $account = $sid.Translate([System.Security.Principal.NTAccount])
        $name = $account.Value
        $script:SidCache[$SidString] = $name
        return $name
    } catch {
        # Fall through to LDAP lookup
    }

    # Try LDAP lookup
    if ($SearchRoot) {
        try {
            $sidBytes = (New-Object System.Security.Principal.SecurityIdentifier($SidString)).GetSidBytes()
            $escapedSid = ($sidBytes | ForEach-Object { '\' + $_.ToString('x2') }) -join ''

            $results = @(Invoke-LdapQuery -SearchRoot $SearchRoot `
                -Filter "(objectSid=$escapedSid)" `
                -Properties @('samaccountname', 'distinguishedname', 'objectclass') `
                -SizeLimit 1)

            if ($results.Count -gt 0) {
                $name = $results[0].samaccountname ?? $results[0].distinguishedname ?? $SidString
                $script:SidCache[$SidString] = $name
                return $name
            }
        } catch {
            Write-Verbose "LDAP SID lookup failed for $SidString`: $_"
        }
    }

    # Cache the unresolved SID to avoid repeated lookups
    $script:SidCache[$SidString] = $SidString
    return $SidString
}

function Clear-ADSidCache {
    [CmdletBinding()]
    param()
    $script:SidCache = @{}
}