This is a fairly major update to a previous script I included in a previous post (PowerShell Code: Find Duplicate SPNs in an Active Directory Forest).
This script provides a list of objects configured with a provided Service Principal Name (SPN) or search for all duplicate SPNs in the Active Directory Forest.
The script searches the Active Directory forest for this SPN value and displays the result.
Example: Find-SPNs.ps1 -SPNName “http/www.domain.com”
The script searches the Active Directory forest for this UPN value and displays the result.
Example: Find-SPNs.ps1 -UPNName “username@domain.com”
The script searches the Active Directory forest for this SPN value and displays the result.
Example: Find-SPNs.ps1 -FindDuplicateSPNs
The script searches the Active Directory forest for this UPN value and displays the result.
Example: Find-SPNs.ps1 -FindDuplicateUPNs
Here is the updated code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | <# NAME: Find-DuplicateSPNs.ps1 AUTHOR: Sean Metcalf AUTHOR EMAIL: SeanMetcalf@MetcorpConsulting.com CREATION DATE: 03/12/2012 LAST MODIFIED DATE: 01/18/2013 LAST MODIFIED BY: Sean Metcalf INTERNAL VERSION: 01.13.01.18.20 RELEASE VERSION: 0.2.0 #> ############################### # Set Environmental Variables # ############################### # COMMON write-output "Setting environmental variables... `r " $DomainDNS = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name #Get AD Domain (lightweight & fast method) $ADDomain = $DomainDNS Write-Debug "Variable $DomainDNS & ADDomain is set to $DomainDNS `r " $TimeVal = get-date -uformat "%Y-%m-%d-%H-%M" Write-Debug "Variable TimeVal is set to $TimeVal `r " $LogDir = "C:\temp\Logs\" #Standard location for script logs Write-Debug "Variable LogDir is set to $LogDir `r " $DateTime = Get-Date #Get date/time Write-Debug "Variable DateTime is set to $DateTime `r " $Separator = "#" #Create separation line $Sepline = $Separator * 75 #Create separation line IF (!(Test-Path $LogDir)) {new-item -type Directory -path $LogDir} # Script Specific # Script Logging $CSVReportFileName = "DuplicateSPNs-$DomainDNS-$TimeVal.csv" $CSVReportFile = $LogDir + $CSVReportFileName Write-Debug "Variable CSVReportFile is set to $CSVReportFile `r " ################################################ # Import Active Directory Powershell Elements # ################################################ write-Verbose "Configuring Powershell environment... `r " Write-Verbose "Importing Active Directory Powershell module `r " import-module ActiveDirectory ############################################# # Get Active Directory Forest & Domain Info # 20120201-15 ############################################# # Get Forest Info write-output "Gathering Active Directory Forest Information..." `r Write-Verbose "Running Get-ADForest Powershell command `r" $ADForestInfo = Get-ADForest $ADForestApplicationPartitions = $ADForestInfo.ApplicationPartitions $ADForestCrossForestReferences = $ADForestInfo.CrossForestReferences $ADForestDomainNamingMaster = $ADForestInfo.DomainNamingMaster $ADForestDomains = $ADForestInfo.Domains $ADForestForestMode = $ADForestInfo.ForestMode $ADForestGlobalCatalogs = $ADForestInfo.GlobalCatalogs $ADForestName = $ADForestInfo.Name $ADForestPartitionsContainer = $ADForestInfo.PartitionsContainer $ADForestRootDomain = $ADForestInfo.RootDomain $ADForestSchemaMaster = $ADForestInfo.SchemaMaster $ADForestSites = $ADForestInfo.Sites $ADForestSPNSuffixes = $ADForestInfo.SPNSuffixes $ADForestUPNSuffixes = $ADForestInfo.UPNSuffixes # Get Domain Info write-output "Gathering Active Directory Domain Information..." `r Write-Verbose "Performing Get-ADDomain powershell command `r" $ADDomainInfo = Get-ADDomain $ADDomainAllowedDNSSuffixes = $ADDomainInfo.ADDomainAllowedDNSSuffixes $ADDomainChildDomains = $ADDomainInfo.ChildDomains $ADDomainComputersContainer = $ADDomainInfo.ComputersContainer $ADDomainDeletedObjectsContainer = $ADDomainInfo.DeletedObjectsContainer $ADDomainDistinguishedName = $ADDomainInfo.DistinguishedName $ADDomainDNSRoot = $ADDomainInfo.DNSRoot $ADDomainDomainControllersContainer = $ADDomainInfo.DomainControllersContainer $ADDomainDomainMode = $ADDomainInfo.DomainMode $ADDomainDomainSID = $ADDomainInfo.DomainSID $ADDomainForeignSecurityPrincipalsContainer = $ADDomainInfo.ForeignSecurityPrincipalsContainer $ADDomainForest = $ADDomainInfo.Forest $ADDomainInfrastructureMaster = $ADDomainInfo.InfrastructureMaster $ADDomainLastLogonReplicationInterval = $ADDomainInfo.LastLogonReplicationInterval $ADDomainLinkedGroupPolicyObjects = $ADDomainInfo.LinkedGroupPolicyObjects $ADDomainLostAndFoundContainer = $ADDomainInfo.LostAndFoundContainer $ADDomainName = $ADDomainInfo.Name $ADDomainNetBIOSName = $ADDomainInfo.NetBIOSName $ADDomainObjectClass = $ADDomainInfo.ObjectClass $ADDomainObjectGUID = $ADDomainInfo.ObjectGUID $ADDomainParentDomain = $ADDomainInfo.ParentDomain $ADDomainPDCEmulator = $ADDomainInfo.PDCEmulator $ADDomainQuotasContainer = $ADDomainInfo.QuotasContainer $ADDomainReadOnlyReplicaDirectoryServers = $ADDomainInfo.ReadOnlyReplicaDirectoryServers $ADDomainReplicaDirectoryServers = $ADDomainInfo.ReplicaDirectoryServers $ADDomainRIDMaster = $ADDomainInfo.RIDMaster $ADDomainSubordinateReferences = $ADDomainInfo.SubordinateReferences $ADDomainSystemsContainer = $ADDomainInfo.SystemsContainer $ADDomainUsersContainer = $ADDomainInfo.UsersContainer $DomainDNS = $ADDomainDNSRoot ########################### # Discover Duplicate SPNs # ########################### IF ($AllSPNList) { Clear-Variable AllSPNList ; Clear-Variable DuplicateSPNList } Write-Output "Discover Local GC `r " Write-Output "Discover Local GC running ADWS `r " $LocalSite = (Get-ADDomainController -Discover).Site $NewTargetGC = Get-ADDomainController -Discover -Service 6 -SiteName $LocalSite IF (!$NewTargetGC) { $NewTargetGC = Get-ADDomainController -Discover -Service 6 -NextClosestSite } $NewTargetGCHostName = $NewTargetGC.HostName $LocalGC = "$NewTargetGCHostName" + ":3268" Write-Output "Identify User and Computer Objects with configured Service Principal Names `r " $Time = (Measure-Command ` { $ObjectList = Get-ADObject -Server "$LocalGC" -filter { (ObjectClass -eq "User") -OR (ObjectClass -eq "Computer") } ` -property name,distinguishedname,ServicePrincipalName | Where { $_.ServicePrincipalName -ne $NULL } }).Seconds $ObjectListCount = $ObjectList.Count Write-Output "Discovered $ObjectListCount User and Computer Objects with SPNs in $Time Seconds `r " Write-Output "Build a list of all SPNs `r " $Time = (Measure-Command ` { ForEach ($Item in $ObjectList) { ## OPEN ForEach Item in ObjectList ForEach ($Object in $Item.ServicePrincipalName) { ## OPEN ForEach Object in Item.ServicePrincipalName [array]$AllSPNList += $Object } ## CLOSE ForEach Object in Item.ServicePrincipalName } ## CLOSE ForEach Item in ObjectList }).Seconds Write-Output "SPN List created in $Time Seconds `r " Write-Output "Find duplicates in the SPN list `r " $Time = (Measure-Command ` { [array]$AllSPNList = $AllSPNList | sort-object [array]$UniqueSPNs = $AllSPNList | Select -unique [array]$DuplicateSPNs = Compare-Object -ReferenceObject $UniqueSPNs -DifferenceObject $AllSPNList }).Seconds [int]$UniqueSPNSCount = $UniqueSPNS.Count ForEach ($Dup in $DuplicateSPNs) { ## OPEN ForEach Dup in DuplicateSPNs [array]$DuplicateSPNList += $Dup.InputObject } ## CLOSE ForEach Dup in DuplicateSPNs [int]$DuplicateSPNsCount = $DuplicateSPNList.Count Write-Output "Discovered $UniqueSPNSCount Unique SPNs in $Time Seconds `r " Write-Output "Discovered $DuplicateSPNsCount Duplicate SPNs in $Time Seconds `r " Write-Output " `r " Write-Output "Identifying objects containing the duplicate SPNs... `r " ForEach ($SPN in $DuplicateSPNList) { ## OPEN ForEach SPN in DuplicateSPNs $DupSPNObjects = $ObjectList | Where { $_.ServicePrincipalName -eq $SPN } Write-Output " `r " Write-Output "The SPN $SPN is configured on the following objects: `r " ForEach ($Obj in $DupSPNObjects) { ## OPEN ForEach Obj in DupSPNObjects [string]$SPNObjectSPN = $SPN # $Obj.ServicePrincipalName $SPNObjectName = $Obj.Name $SPNObjectClass = $Obj.ObjectClass $SPNObjectDN = $Obj.DistinguishedName Write-Output " * $SPNObjectName ($SPNObjectClass) has the associated SPN: $SPN [$SPNObjectDN] `r " Write-Verbose "Creating Inventory Object for $FilePath..." $InventoryObject = New-Object -TypeName PSObject $InventoryObject | Add-Member -MemberType NoteProperty -Name SPN -Value ($SPN) $InventoryObject | Add-Member -MemberType NoteProperty -Name ObjectName -Value $SPNObjectName $InventoryObject | Add-Member -MemberType NoteProperty -Name SPNObjectClass -Value $SPNObjectClass $InventoryObject | Add-Member -MemberType NoteProperty -Name ObjectDN -Value $SPNObjectDN [array]$AllInventory += $InventoryObject } ## CLOSE ForEach Obj in DupSPNObjects } ## CLOSE ForEach SPN in DuplicateSPNs # Create Inventory Object [int]$AllInventoryCount = $AllInventory.Count Write-Output "Exporting File Information ($AllInventoryCount records) to CSV Report file ($CSVReportFile)..." $AllInventory | Export-CSV $CSVReportFile -NoType |




