Src/Private/Get-WinADDuplicateSPN.ps1

function Get-WinADDuplicateSPN {
    <#
    .SYNOPSIS
    Detects and lists duplicate Service Principal Names (SPNs) in the Active Directory Domain.
    .DESCRIPTION
    Detects and lists duplicate Service Principal Names (SPNs) in the Active Directory Domain.
    .PARAMETER All
    Returns all duplicate and non-duplicate SPNs. Default is to only return duplicate SPNs.
    .PARAMETER Exclude
    Provides ability to exclude specific SPNs from the duplicate detection. By default it excludes kadmin/changepw as with multiple forests it will happen for sure.
    .PARAMETER Forest
    Target different Forest, by default current forest is used
    .PARAMETER ExcludeDomains
    Exclude domain from search, by default whole forest is scanned
    .PARAMETER IncludeDomains
    Include only specific domains, by default whole forest is scanned
    .PARAMETER ExtendedForestInformation
    Ability to provide Forest Information from another command to speed up processing
    .EXAMPLE
    Get-WinADDuplicateSPN | Format-Table
    .EXAMPLE
    Get-WinADDuplicateSPN -All | Format-Table
    .NOTES
        Version: 0.1.0
        Author: Przemysław Kłys
    #>

    [CmdletBinding()]
    param(
        [switch] $All,
        [string[]] $Exclude,
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [Parameter(ParameterSetName = 'Forest')][System.Collections.IDictionary] $ExtendedForestInformation,
        [pscredential] $Credential
    )
    $Excluded = @(
        # 'kadmin/changepw'
        foreach ($Item in $Exclude) {
            $iTEM
        }
    )

    $SPNCache = [ordered] @{}
    $ForestInformation = Get-WinADForestDetail -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation -Credential $Credential
    foreach ($Domain in $ForestInformation.Domains) {
        Write-PScriboMessage -Message "Get-WinADDuplicateSPN - Processing $Domain"
        Write-PScriboMessage -Message "Get-WinADDuplicateSPN - Found $($Users.Count) objects. Processing..."
        foreach ($Object in $Users) {
            foreach ($SPN in $Object.ServicePrincipalName) {
                if (-not $SPNCache[$SPN]) {
                    $SPNCache[$SPN] = [PSCustomObject] @{
                        Name = $SPN
                        Duplicate = $false
                        Count = 0
                        Excluded = $false
                        List = [System.Collections.Generic.List[Object]]::new()
                    }
                }
                if ($SPN -in $Excluded) {
                    $SPNCache[$SPN].Excluded = $true
                }
                $SPNCache[$SPN].List.Add($Object)
                $SPNCache[$SPN].Count++
            }
        }
    }
    Write-PScriboMessage -Message "Get-WinADDuplicateSPN - Finalizing output. Processing..."
    foreach ($SPN in $SPNCache.Values) {
        if ($SPN.Count -gt 1 -and $SPN.Excluded -ne $true) {
            $SPN.Duplicate = $true
        }
        if ($All) {
            $SPN
        } else {
            if ($SPN.Duplicate) {
                $SPN
            }
        }
    }
}