Private/UserLookupHelpers.ps1

function Get-NSPADForestDomainInfo {
    # Read every domain NC in the selected forest, excluding external references, application
    # partitions and pre-created (not yet active) domains. No GC partial attribute set is used.
    param([Parameter(Mandatory)][string]$Server, [int]$PageSize = 500)
    $seed = Get-NSPADDomainInfo -Server $Server
    if (-not $seed.ConfigurationDN) { throw 'The seed domain has no ConfigurationDN; forest discovery cannot continue.' }
    $refs = @(Read-NSPADLdap -Server $seed.Server -SearchBase "CN=Partitions,$($seed.ConfigurationDN)" -Scope OneLevel -Filter '(&(objectClass=crossRef)(systemFlags:1.2.840.113556.1.4.803:=3)(!(enabled=FALSE)))' -Property nCName,dnsRoot -PageSize $PageSize)
    if (-not $refs.Count) { throw 'No forest domain crossRefs are visible; refusing an empty forest inventory.' }
    $domains = @{}
    foreach ($ref in $refs) {
        $dn = [string]$ref['ncname']
        $dns = [string]$ref['dnsroot']
        if (-not $dn -or -not $dns) { throw 'A forest domain crossRef is missing nCName or dnsRoot.' }
        if ($domains.ContainsKey($dn)) { throw 'Multiple forest domain crossRefs have the same naming context.' }
        $dc = Find-NSPADDomainController -DomainName $dns
        $domain = Get-NSPADDomainInfo -Server $dc
        if ($domain.DistinguishedName -ne $dn -or $domain.DNSRoot -ne $dns -or $domain.ConfigurationDN -ne $seed.ConfigurationDN) {
            throw "Forest domain discovery resolved a different domain or forest for $dns."
        }
        $domains[$dn] = $domain
    }
    if (-not $domains.ContainsKey([string]$seed.DistinguishedName)) { throw 'The seed domain is absent from forest discovery.' }
    # Keep discovery order for predictable inventory output.
    foreach ($ref in $refs) { $domains[[string]$ref['ncname']] }
}

function ConvertTo-NSPADUser {
    param([Parameter(Mandatory)][hashtable]$Entry, [Parameter(Mandatory)]$DomainInfo)
    foreach ($required in 'distinguishedname', 'objectguid', 'objectsid', 'useraccountcontrol') {
        if (-not $Entry.ContainsKey($required) -or $null -eq $Entry[$required]) {
            throw "LDAP user result is missing $required; identity and enabled state must be readable."
        }
    }
    [pscustomobject]@{
        PSTypeName           = 'NSP.ActiveDirectory.User'
        DistinguishedName    = [string]$Entry['distinguishedname']
        SamAccountName       = [string]$Entry['samaccountname']
        UserPrincipalName    = [string]$Entry['userprincipalname']
        DisplayName          = [string]$Entry['displayname']
        GivenName            = [string]$Entry['givenname']
        Surname              = [string]$Entry['sn']
        Mail                 = [string]$Entry['mail']
        ProxyAddresses       = [string[]]@($Entry['proxyaddresses'] | Where-Object { $null -ne $_ })
        ObjectGUID           = [guid]::new([byte[]]$Entry['objectguid'])
        ConsistencyGuidBytes = if ($Entry['ms-ds-consistencyguid']) { , [byte[]]$Entry['ms-ds-consistencyguid'] } else { $null }
        ObjectSID            = [Security.Principal.SecurityIdentifier]::new([byte[]]$Entry['objectsid'], 0).Value
        Enabled              = [bool](-not ([int]$Entry['useraccountcontrol'] -band 2))
        DomainDNSRoot        = [string]$DomainInfo.DNSRoot
        Server               = [string]$DomainInfo.Server
    }
}