Private/ServiceAccountHelpers.ps1

# gMSA helpers for Get/Test/New-NSPADServiceAccount. LDAP only (through Read-NSPADLdap).

function ConvertFrom-NSPADGmsaMembership {
    # msDS-GroupMSAMembership is a security descriptor: its Allow entries are the principals that may
    # retrieve the managed password. Returns one @{ Sid; Name } per allowed principal.
    param([byte[]]$Descriptor)
    if (-not $Descriptor) { return }
    $sd = New-Object System.DirectoryServices.ActiveDirectorySecurity
    $sd.SetSecurityDescriptorBinaryForm($Descriptor)
    foreach ($rule in $sd.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier])) {
        if ($rule.AccessControlType -ne 'Allow') { continue }
        $sid = $rule.IdentityReference.Value
        $name = try { $rule.IdentityReference.Translate([System.Security.Principal.NTAccount]).Value } catch { $sid }
        [pscustomobject]@{ Sid = $sid; Name = $name }
    }
}

function Get-NSPADKdsRootKeyStatus {
    # Whether the forest has a KDS root key gMSAs can use (msKds-ProvRootKey objects under
    # CN=Master Root Keys). Reading them needs Domain Admin-level rights: none visible can also mean
    # "not allowed to see", which the caller reports as such.
    param([Parameter(Mandatory)]$DomainInfo)
    # The forest's configuration partition (from RootDSE), not one built from the domain DN: a child domain's differs.
    $configDn = $DomainInfo.ConfigurationDN
    $keys = @(Read-NSPADLdap -Server $DomainInfo.Server -SearchBase "CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,$configDn" `
            -Filter '(objectClass=msKds-ProvRootKey)' -Property 'cn', 'msKds-UseStartTime' -Scope OneLevel)
    $usable = @($keys | Where-Object { $_['mskds-usestarttime'] -and [DateTime]::FromFileTimeUtc([int64]$_['mskds-usestarttime']) -le [DateTime]::UtcNow })
    [pscustomobject]@{ Visible = $keys.Count; Usable = $usable.Count }
}