Public/Get-SqlSpnAccount.ps1

# =============================================================================
# Script : Get-SqlSpnAccount.ps1
# Author : Keith Ramsey
# =============================================================================
# Change Log
# -----------------------------------------------------------------------------
# 2026-05-09 Keith Ramsey Phase 2 release polish - DR-202 standard header applied.
# 2026-05-14 Keith Ramsey Fix Get-ADObject property name. ServicePrincipalNames
# (plural) is a Get-ADUser/Computer extended property;
# the generic Get-ADObject does not synthesize it and
# throws, which the catch masked as a misleading
# "Account not found". Use servicePrincipalName (the
# real LDAP attribute). Surfaced by real-domain lab test.
# =============================================================================
function Get-SqlSpnAccount {
    <#
    .SYNOPSIS
        Verifies and retrieves a standard Active Directory account.
    .DESCRIPTION
        First step of the SPN management pipeline. Ensures the supplied service
        account or gMSA exists in Active Directory and returns a normalized
        descriptor used by downstream functions (New-SqlSpnPlan, Invoke-SqlSpnExecutionEngine).
        On lookup failure, emits a non-terminating error so callers can decide
        whether to abort the pipeline or recover.
    .PARAMETER SamAccountName
        The SAM account name (e.g., svc_sql_prod or SQLFCI01$).
    .EXAMPLE
        Get-SqlSpnAccount -SamAccountName 'svc_sql_prod'
    .EXAMPLE
        $acct = Get-SqlSpnAccount -SamAccountName 'SQLFCI01$'
        if ($acct.ObjectClass -eq 'computer') { 'OK for FCI Engine SPN' }
    .OUTPUTS
        PSCustomObject with Name, DistinguishedName, ObjectClass, ExistingSpns.
    .NOTES
        Requires the ActiveDirectory PowerShell module and a reachable domain
        controller. On non-domain-joined hosts this command will fail at the
        Get-ADObject call.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory=$true)][string]$SamAccountName)
    try {
        $Account = Get-ADObject -Filter "SamAccountName -eq '$SamAccountName'" -Properties servicePrincipalName, DistinguishedName, ObjectClass -ErrorAction Stop
        return [PSCustomObject]@{
            Name              = $SamAccountName
            DistinguishedName = $Account.DistinguishedName
            ObjectClass       = $Account.ObjectClass
            ExistingSpns      = $Account.servicePrincipalName
        }
    }
    catch {
        Write-Error "Account [$SamAccountName] not found in Active Directory."
    }
}