List all SPNs used in your Active Directory

There are a lot of hints & tips out there for troubleshooting SPNs, or Service Principal Names. Listing duplicate SPNs is fairly easy, just use setspn -X on your command-line and you’ll find out. But how do you find out which SPNs are used for which users and computers are used for this?

An SPN or Service Principal Name is a unique identity for a service, mapped with a specific account (mostly service account). Using an SPN, you can create multiple aliases for a service mapped with an Active Directory domain account.

SetSPN command-line

To set, list or delete the SPN, we use an in-built command line tool SETSPN (setspn.exe) provided by Microsoft.

Quite some scripts assume you’re looking for a specific SPN (HTTP/…), a specific user, or a specific computer. For example, you can use setspn to find (query) Service Principal Names (SPNs) linked to a certain computer:

setspn.exe -L <ServerName>Code language: PowerShell (powershell)

Or you can use setspn to find (query) SPNs linked to a certain user account:

setspn.exe -L <domain\user>Code language: PowerShell (powershell)

And now you need a general script to list all SPNs, for all users and all computers…

You delete arbitrary SPNs, or Service Principal Names, using the -D switch:

setspn.exe -D <spn> accountnameCode language: HTML, XML (xml)

List SPNs using Powershell

Nice to know fact, Service Principal Names (SPNs) are set as an attribute on the user or computer accounts. That makes it fairly ease to query for that attribute. And modern admins do PowerShell, right?

So… Save the following code into a new PowerShell .ps1 file and run it in your domain. It will query and list the Service Principal Names – SPNs.

# Source / credit:
# https://social.technet.microsoft.com/wiki/contents/articles/18996.active-directory-powershell-script-to-list-all-spns-used.aspx

cls
$search = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$search.filter = "(servicePrincipalName=*)"

## You can use this to filter for OU's:
## $results = $search.Findall() | ?{ $_.path -like '*OU=whatever,DC=whatever,DC=whatever*' }
$results = $search.Findall()

foreach( $result in $results ) {
	$userEntry = $result.GetDirectoryEntry()
	Write-host "Object Name	=	"	$userEntry.name -backgroundcolor "yellow" -foregroundcolor "black"
	Write-host "DN	=	"	$userEntry.distinguishedName
	Write-host "Object Cat.	=	" $userEntry.objectCategory
	Write-host "servicePrincipalNames"

	$i=1
	foreach( $SPN in $userEntry.servicePrincipalName ) {
		Write-host "SPN ${i} =$SPN"
		$i+=1
	}
	Write-host ""
}Code language: PowerShell (powershell)

Or use dsquery on your CMD.exe command-line:

dsquery * "ou=domain controllers,dc=yourdomain,dc=com" -filter "(&(objectcategory=computer)
(servicePrincipalName=*))" -attr distinguishedName servicePrincipalName > spns.txtCode language: PowerShell (powershell)

This is a valuable script and information reference for your own documentation.

Protip: Donate $10, 20 or 30 through Paypal (or see my donate page) and support this site. Thank you <3

Jan Reilink

Hi, my name is Jan. I am not a hacker, coder, developer or guru. I am merely an application manager / systems administrator, doing my daily thing at Embrace - The Human Cloud. In the past I worked for clidn and Vevida. With over 20 years of experience, my specialties include Windows Server, IIS, Linux (CentOS, Debian), security, PHP, websites & optimization. I blog at https://www.saotn.org.

10 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Philippe
28/04/2021 16:50

A few enhancement:

class Spn {
    static [int]$counter
    [int]$id
    [string]$serviceClass
    [string]$name
    [string]$spnChain 

    Spn([string]$spnChainIs){
        
        $this.id = [Spn]::counter++
        $this.spnChain = $spnChainIs
        $temp = $spnChainIs.Split("/")
        $this.serviceClass = $temp[0]
        $this.name = $temp[1]
        
    }

    Spn([string]$portIs,[string]$objectIs){
        
        $this.id = [Spn]::counter++
        $this.serviceClass = $portIs
        $this.name = $objectIs
        $this.spnChain = ($this.serviceClass + "/" + $this.name)
    }

    [string] ToString(){
        return ("$($this.spnChain)")
    }

}

class Spns {
    hidden[string]$objectCategory
    hidden[string]$dN
    [string]$name
    [Spn[]]$spnss

}

Clear-Host
$search = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$search.filter = "(servicePrincipalName=*)"

## You can use this to filter for OU's:
## $results = $search.Findall() | ?{ $_.path -like '*OU=whatever,DC=whatever,DC=whatever*' }
$results = $search.Findall()

$report = @()

foreach( $result in $results ) {
	$userEntry = $result.GetDirectoryEntry()
    [Spns]$userSPNs = [Spns]::new()
    $userSPNs.dN             = $userEntry.distinguishedName
    $userSPNs.name           = $userEntry.name
    $userSPNs.objectCategory = $userEntry.objectCategory

    [Spn]::counter = 0
	foreach( $SPN in $userEntry.servicePrincipalName ) {
        [Spn]$oneSpn = [Spn]::new($SPN)
        $userSPNs.spnss += $oneSpn #.ToString()
	}
    $report += $userSPNs
}

$report | Measure-Object
$report | Where-Object {($_.spnss).spnChain -like "*changepw*"}
Purclot
05/04/2021 12:26

if the tusk is just to list all the SPNs in your domain, why just not simply:
Get-ADUser -filter * -prop * | where{$_.serviceprincipalname -ne $null} | select name, serviceprincipalname
?

John Sullivan
24/09/2020 18:58

How can the above PowerShell script be modified to show just one server? I have tried different things but cannot get it to work.

Thanks

FrankJ
20/09/2019 21:00

If using dsquery this filter will return user and computer objects
-filter “(&(|(objectcategory=computer)(objectcategory=user))(servicePrincipalName=*))”

John Doe
07/12/2018 21:54

it is better to

setspn -Q */*

Jan
09/04/2018 09:31

Hé this is a great way to list all SPN’s used in my active directory network, thanks!