I built a Kerberos SPN tool because setspn.exe made me feel stupid
- powershell
- sql-server
- kerberos
- dba
Here's the thing about Kerberos authentication errors in SQL Server: they always happen on a Tuesday. Specifically a Tuesday afternoon. Specifically when you have somewhere else to be.
Users start getting "Cannot generate SSPI context." You start getting messages. You fire up setspn -L domain\sqlsvcaccount and stare at the output trying to remember which SPNs are supposed to be there and which ones are duplicates from that server rename six months ago that nobody documented. You fix something. It either gets better or gets worse. It takes you an hour to find out which.
I got tired of that particular experience. Hence: SqlSpnManager.
The funny part about how this happened
SqlSpnManager was the first of my PowerShell modules to land on PowerShell Gallery — which makes it sound like it was the first one I built. It wasn't even close. I had four or five modules going at home before this one was ready to ship. The others aren't done yet. SqlSpnManager made it out first because Kerberos kept winning, and eventually I got motivated enough to do something about it properly.
What setspn.exe actually gives you
setspn.exe is the built-in Windows tool for managing Service Principal Names. It works. It also gives you this kind of output:
Registered ServicePrincipalNames for CN=sqlsvc,OU=Service Accounts,DC=corp,DC=local:
MSSQLSvc/sqlserver01.corp.local:1433
MSSQLSvc/sqlserver01.corp.local
MSSQLSvc/sqlserver01:1433
MSSQLSvc/sqlserver01That's one instance. Now multiply it by however many SQL Server instances you're managing. Then factor in that you need to cross-reference what's registered against what should be registered based on the actual port configuration. Then add in the named instances. Then add in the possibility of duplicate SPNs (which cause their own authentication failures) and missing SPNs (which cause different authentication failures).
You're basically doing a diff in your head between what the tool printed and what you know about your environment. That's not a tool; that's a reading comprehension exercise.
How SqlSpnManager approaches it differently
The module runs a pipeline:
Step 1: Discover the service account.
Get-SqlSpnAccount -ComputerName sqlserver01This identifies the account running the SQL Server service — the account whose SPNs need to exist in Active Directory.
Step 2: Discover the infrastructure.
Get-SqlSpnInfrastructure -ComputerName sqlserver01This finds the actual SQL Server instances on the target machine, along with the ports they're listening on. Named instances, default instances, custom ports — it reads what's actually running, not what you think is running.
Step 3: Build a plan.
New-SqlSpnPlan -ComputerName sqlserver01This is where it gets different. Instead of immediately running setspn -A and hoping for the best, the module produces a plan: a list of SPNs that should exist, compared against what actually exists in Active Directory. Missing registrations. Duplicates. Anything that doesn't match.
Step 4: Test the plan.
Test-SqlSpnPlan -Plan $planValidates the plan before a single SPN is touched. Catches problems — like trying to register an SPN that already belongs to a different account — before they create more problems.
Step 5: Execute.
Invoke-SqlSpnExecutionEngine -Plan $planOnly now does anything actually change in Active Directory.
The interactive entry point
If you don't want to chain all that yourself, Start-SqlSpnManager walks you through it interactively:
Start-SqlSpnManagerIt prompts for the target, the scenario (default instance, named instance, a specific port), and then shows you the plan before asking for a final confirmation. If you're scripting this unattended, use the underlying functions directly and skip the prompts entirely.
The handoff problem
There's a conversation that happens in almost every Windows shop: the DBA knows the SQL instances cold, but doesn't have AD write rights. The AD admin has the rights but has no idea what SPNs need to exist or why. So the DBA says "I need these SPNs registered." The AD admin says "which ones?" The DBA sends a list. The AD admin comes back: "something's wrong." The DBA says "what's wrong?" The AD admin says "I don't know." And it goes from there.
Export-SqlSpnRegistrationScript exists for exactly that moment.
Export-SqlSpnRegistrationScript -Plan $plan -Path C:\Temp\register-spns.ps1Run the plan through the module, export the result as a script, hand it to whoever has the AD rights. They get something reviewable and runnable. You get SPNs registered without needing a standing AD delegation or a long back-and-forth about what MSSQLSvc/hostname:1433 means.
It's not a workaround. It's just how the handoff should work.
What it won't do
SqlSpnManager doesn't touch anything it can't verify first. If it can't discover the infrastructure, it won't guess at SPN names. If the plan has a conflict it can't resolve, it surfaces the conflict and stops. It's not trying to be clever; it's trying to make the right action obvious and the wrong action hard.
It also doesn't handle MSDTC SPNs. That's a separate problem and a separate fight for another day.
Installing it
Install-Module SqlSpnManagerThe website — including a walkthrough of the full pipeline with more context on the AD permissions model — is at sql-spn-manager.com.
Two years ago, I would've just fumbled through setspn the same way I always did and called it good. Building this forced me to actually understand the SPN model well enough to codify it. That part was worth the time even before anyone else used it.
What's the worst Kerberos situation you've had to dig out of? I'm genuinely curious whether anyone else has the "service account was moved and nobody told IT" story. I definitely do.