sqlspn-review — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited sqlspn-review (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Analyze SQL Server SPN configuration and Active Directory delegation attributes to surface Kerberos authentication failures, NTLM fallback causes, and double-hop connectivity problems. Applies 40 checks (K1–K40) across seven categories:
short-hostname variant, port mismatch, and FCI Virtual Network Name
vs domain account, stale SPNs from old accounts, MSA/gMSA auto-registration gaps
alias SPN, multi-subnet listener coverage
unconstrained delegation, NTLM fallback signal
configured, missing target SPN, protocol transition, RBCD misconfiguration, delegation scope
Users membership on end-user, computer account SPN conflict, computer account unconstrained delegation, service account in Protected Users
auth with orphaned AD SPN, Azure SQL MI on-premises SPN, gMSA rollover drift, FCI node SPN leak, distributed AG forwarder SPN, S4U2Proxy without protocol transition, Kerberos FAST incompatibility, AdminSDHolder SPN write block, DNS CNAME alias without SPN
Accept any of:
setspn -L domain\sqlsvc (SPNs registered on a specific account)setspn -Q MSSQLSvc/* (all MSSQLSvc SPNs in the domain)setspn -X (duplicate SPN report across all accounts)Get-ADUser or Get-ADComputer showingdelegation attributes (TrustedForDelegation, TrustedToAuthForDelegation, msDS-AllowedToDelegateTo, msDS-AllowedToActOnBehalfOfOtherIdentity, memberOf)
name, the service account, and any error messages observed
For best results, provide output from all capture commands below. When only partial data is available, state which checks cannot be evaluated and why.
setspn -Q MSSQLSvc/*
setspn -L DOMAIN\sqlsvc
setspn -X
Get-ADUser DOMAIN\sqlsvc -Properties TrustedForDelegation, TrustedToAuthForDelegation, msDS-AllowedToDelegateTo, ServicePrincipalNames, MemberOf
Get-ADComputer SQLNODE1 -Properties TrustedForDelegation, msDS-AllowedToActOnBehalfOfOtherIdentity, ServicePrincipalNames
# Verify cached Kerberos tickets on the client machine (run as the connecting user)
klist
# Clear ticket cache to force fresh acquisition during testing
klist purge| Threshold | Value | Used by |
|---|---|---|
| Duplicate SPN | 2 or more accounts holding identical SPN | K8 — Critical |
| Port mismatch tolerance | Exact match required between SPN port and SQL TCP port | K5, K13 |
| Unconstrained delegation | Any account with TrustedForDelegation = True | K19, K29 — Critical |
| Delegation target missing SPN | Any missing target SPN in msDS-AllowedToDelegateTo | K22 — Critical |
| Protected Users membership | Any SQL service account or end-user in Protected Users | K27, K30 — Critical |
Run these first. They confirm the KDC can resolve the SQL Server target.
MSSQLSvc/<host>:1433, MSSQLSvc/<FQDN>:1433, or the portless form MSSQLSvc/<FQDN> (used by clients that do not specify a port in their connection string)setspn -S MSSQLSvc/<host>:1433 DOMAIN\sqlsvc, setspn -S MSSQLSvc/<host.domain.com>:1433 DOMAIN\sqlsvc, and setspn -S MSSQLSvc/<host.domain.com> DOMAIN\sqlsvc (portless form for legacy or unspecified-port clients)MSSQLSvc/<host>:<port> SPN exists for the instance's TCP port AND no MSSQLSvc/<host>:<instancename> SPN exists for named-pipe / shared-memory connections. Both forms are valid per Microsoft documentation and both should be registered.setspn -S MSSQLSvc/<host>:<port> DOMAIN\sqlsvc (using the actual TCP port from SQL Server Configuration Manager) AND setspn -S MSSQLSvc/<host>:<instancename> DOMAIN\sqlsvc (using the instance name, e.g. SQLNODE1\INST1). Clients connecting via TCP use the port-based form; clients using named pipes or shared memory use the instance-name form.MSSQLSvc/SQLNODE1:1433) but no fully-qualified SPN (MSSQLSvc/SQLNODE1.domain.com:1433)setspn -S MSSQLSvc/<host.domain.com>:1433 DOMAIN\sqlsvc; clients using FQDN in their connection string fail Kerberos without the FQDN variantMSSQLSvc/SQLNODE1.domain.com:1433) but no short-hostname SPN (MSSQLSvc/SQLNODE1:1433)setspn -S MSSQLSvc/<netbios>:1433 DOMAIN\sqlsvc; clients using NetBIOS name in the connection string will fall back to NTLMMSSQLSvc/<host>:<port> SPN exists but the port does not match the SQL Server's actual TCP listening portsetspn -D MSSQLSvc/<host>:<wrong-port> DOMAIN\sqlsvc then setspn -S MSSQLSvc/<host>:<correct-port> DOMAIN\sqlsvcMSSQLSvc/<VNN>:1433 (or appropriate port) on the service account; the VNN, not the physical node name, is what clients connect toMSSQLSvc/<host>:<port> SPN is registered on an account other than the SQL Server service account currently running the instancesetspn -D MSSQLSvc/<host>:<port> DOMAIN\wrongaccount then setspn -S MSSQLSvc/<host>:<port> DOMAIN\sqlsvc; verify the SQL Server service account in SQL Server Configuration Managersetspn -X or setspn -Q reveals the same MSSQLSvc/<host>:<port> registered on 2 or more accounts — see Thresholds Referencesetspn -D MSSQLSvc/<host>:<port> DOMAIN\duplicate-account; only one account should own the SPN; the KDC cannot disambiguate and will reject all Kerberos tickets for that targetMSSQLSvc/<host>:<port> SPN is found on the machine (computer) accountMSSQLSvc/<host>:<port> SPN found on a former service account after the service account was changedsetspn -D MSSQLSvc/<host>:<port> DOMAIN\oldsqlsvc; stale SPNs cause K8 (duplicate) even after a planned account migrationServicePrincipalNames attributeMSSQLSvc/<host>:port and MSSQLSvc/<host.domain.com>:port exist; MSA/gMSA auto-registration creates the short-hostname SPN but sometimes skips the FQDN variantMSSQLSvc/<listener>:<port> SPN is registeredsetspn -S MSSQLSvc/<listener-name>:1433 DOMAIN\sqlsvc on each replica's service account; the listener name resolves differently than the node hostnameMSSQLSvc/<host>:1433) but the instance is not the default instance — see Thresholds ReferenceStart-Service SQLBrowser); named instances depend on SQL Browser for port resolution when clients omit the explicit port from the connection stringMSSQLSvc/<alias-name>:<port> SPNsetspn -S MSSQLSvc/<alias-name>:<port> DOMAIN\sqlsvc; Kerberos ticket requests use the connection target name, not the resolved hostnameHTTP/<host> SPN is registered on the relevant service accountsetspn -S HTTP/<host> DOMAIN\svcaccount; Kerberos delegation to HTTP targets requires the HTTP SPN on the target service accountWrite ServicePrincipalName permission on its own AD user object, preventing self-registration of SPNs; or SQL Server ERRORLOG contains error 17806, 17807, or Windows return code 0x2098 in an SPN registration failure messagedsacls; alternatively, a Domain Admin can register SPNs manually using setspn -STrustedForDelegation = True on the SQL Server service account — see Thresholds ReferencemsDS-AllowedToDelegateTo with only the target service SPNs; unconstrained delegation allows credential forwarding to any servicesys.dm_exec_connections shows auth_scheme = NTLM); or Kerberos ticket requests fail with "target principal name is incorrect"; or the connection is a loopback (SQL Agent job, SSIS package on the same host, OPENQUERY to (local)) where Windows loopback detection blocks Kerberos regardless of SPN statemsDS-AllowedToDelegateTo is empty on the middle-tier service accountmsDS-AllowedToDelegateTo is populated) but one or more listed target SPNs do not exist on any AD account — see Thresholds ReferenceTrustedToAuthForDelegation is absent from the service accountmsDS-AllowedToActOnBehalfOfOtherIdentity is present on the target computer, but the initiating service account is not in the ACLSet-ADComputer <target> -PrincipalsAllowedToDelegateToAccount <initiating-account>msDS-AllowedToDelegateTo contains service SPNs beyond MSSQLSvc/* — for example, cifs/* or host/* — that are not required for the intended SQL Server delegation pathAccountNotDelegated = True is set on the end-user AD account that needs to authenticate through a delegating SQL ServerAccountNotDelegated flag if delegation is intentional for this user (Set-ADUser <user> -AccountNotDelegated 0). Note: RBCD (K24) uses S4U2Proxy which has different ticket requirements than classic KCD, but if the user is also a member of Protected Users (K27), neither KCD nor RBCD will work — Protected Users membership blocks ALL delegation regardless of type.MSSQLSvc/<host>:<port> SPNs — both accounts have the same SPNsetspn -DTrustedForDelegation = True — see Thresholds ReferenceMSSQLSvc/<host>:<port> SPN is registered in the on-premises AD for the SQL instancesetspn -S MSSQLSvc/<host>:<port> DOMAIN\sqlsvc); Entra Kerberos for hybrid-joined devices still resolves SPNs through on-premises AD for SQL Server targets — the SPN must exist on both legsCREATE LOGIN ... FROM EXTERNAL PROVIDER) but setspn -L shows a traditional AD MSSQLSvc SPN still registered on the service account — SQL 2022+ / Azure SQLsetspn -D MSSQLSvc/<host>:<port> DOMAIN\sqlsvc) to avoid confusing on-premises clients that attempt Kerberos against a SQL instance that no longer accepts Windows-integrated loginsMSSQLSvc/<mi-hostname>:1433 SPN is registered in the on-premises AD — Azure SQL MI onlysetspn -L differs from the ServicePrincipalNames attribute in Get-ADServiceAccount output for the same gMSATest-ADServiceAccount and setspn -Lsetspn -Q MSSQLSvc/* results alongside the Virtual Network Name (VNN) SPNsetspn -D MSSQLSvc/<node-name>:<port> DOMAIN\sqlsvc); clients connecting to the physical node name may succeed with Kerberos while VNN connections fail, creating intermittent authentication failures after failoverMSSQLSvc/<forwarder-listener>:<port> SPN registered — SQL 2016+setspn -S MSSQLSvc/<forwarder-listener-name>:<port> DOMAIN\sqlsvc on each replica's service account; the forwarder introduces a second AG whose listener is an additional Kerberos target distinct from either underlying AG's listenermsDS-AllowedToActOnBehalfOfOtherIdentity present on target) but the initiating service account lacks TrustedToAuthForDelegation (S4U2Self / protocol transition not enabled)Set-ADUser <account> -TrustedToAuthForDelegation $true); S4U2Proxy (forwarding a ticket to the target) requires S4U2Self to first obtain a forwardable service ticket for the usermsDS-SupportedEncryptionTypes attribute does not include AES keys — Windows Server 2012+Set-ADUser <sqlsvc> -KerberosEncryptionType AES128,AES256); RC4-only accounts cannot authenticate under FAST policy, causing silent fallback to NTLMServicePrincipalName attribute shows DENY on Write ServicePrincipalName or the account is a member of a privileged AD group (Domain Admins, Enterprise Admins, etc.) — AdminSDHolder resets ACLs hourly via SDPropMSSQLSvc/<cname>:<port> SPN is registered for the alias namesetspn -S MSSQLSvc/<cname>:<port> DOMAIN\sqlsvc; Kerberos ticket requests use the name from the client connection string, not the resolved A record; a CNAME alias requires its own SPN entry independent of the host's SPNIf the SQL Server version is stated by the user, read VERSION_COMPATIBILITY.md (~/.claude/skills/VERSION_COMPATIBILITY.md if installed, or skills/VERSION_COMPATIBILITY.md from the repo). If unavailable, skip silently. For checks whose minimum version exceeds the instance version: verbose mode → log as SKIP (version: requires SQL 20XX+, instance is SQL 20YY); standard report → omit entirely. Do not suppress NOT ASSESSED rows from missing input — only suppress version-inapplicable checks.
Structure the report as follows:
## SPN Review Analysis
### Summary
- X Critical, Y Warnings, Z Info
- Service account: [detected or "unknown — not provided"]
- SQL instances / listeners found: [list]
- Highest-risk finding: [check name and ID]
### Critical Issues
### [C1 — K8] Duplicate SPN — MSSQLSvc/SQLNODE1:1433
- **Observed:** SPN MSSQLSvc/SQLNODE1:1433 registered on both DOMAIN\sqlsvc and DOMAIN\oldsqlsvc (from setspn -X output)
- **Impact:** KDC cannot determine which account holds the correct decryption key; all Kerberos logins to SQLNODE1:1433 fail with "The target principal name is incorrect"
- **Fix:** Remove duplicate: setspn -D MSSQLSvc/SQLNODE1:1433 DOMAIN\oldsqlsvc
### Warnings
### Info
### Passed Checks
| Check | Result |
|-------|--------|
| K1 — Missing Default-Instance SPN | PASS — MSSQLSvc/SQLNODE1:1433 and MSSQLSvc/SQLNODE1.domain.com:1433 both present on DOMAIN\sqlsvc |
| K6 — Missing VNN SPN for FCI | SKIP — no FCI topology indicated in input |
---
*Analyzed by: [state the AI model and version you are running as, e.g. "Claude Sonnet 4.6", "DeepSeek R1", "GPT-4o"] · [current date and time in the user's local timezone, or UTC if timezone is unknown, e.g. "2026-05-16 20:15 NZST"]*Output labels use [C1], [W1], [I1] sequential numbering with check ID in parentheses. Each finding includes Observed (what the input shows), Impact (why it matters), and Fix (concrete command or step). The Passed Checks table explicitly lists every check evaluated and the evidence for each PASS or SKIP.
setspn -X output is not provided, K8 (duplicate SPN) cannot be fully evaluated — state the limitation.`--brief` — Omit the Passed Checks table and attribution footer. Output the Summary, Findings, and Prioritized Fix Sequence sections only. Use when a quick scan of what fired is all that's needed.
`--critical-only` — Suppress Warning and Info findings. Show only Critical findings. The Passed Checks table is also omitted. Use when triaging an incident and only actionable blockers matter.
Both flags can be combined: --brief --critical-only produces the Summary section plus Critical findings only.
When neither flag is present, produce the full report as documented above.
When the user's request includes --verbose, --trace, or the word verbose:
1. Append a `## Check Evaluation Log` section after the Passed Checks table.
Include one row for every check in this skill's ruleset, in check-ID order:
| Check | Evidence | Threshold | Result |
|---|---|---|---|
| [ID — Name] | [key attribute(s) and value found, or "absent"] | [threshold or condition] | PASS / FIRE → [severity] / NOT ASSESSED |
Result conventions:
PASS — attribute present, threshold not met**FIRE → Critical/Warning/Info** — threshold met; bold to distinguish from passesNOT ASSESSED — required attribute absent from input2. Save both files to the current working directory using the Write tool:
output/<skill-name>/<YYYY-MM-DD-HHmmss>-<input-prefix>/analysis.md ← full report output/<skill-name>/<YYYY-MM-DD-HHmmss>-<input-prefix>/trace.md ← Check Evaluation Log
Derive <input-prefix>:
horrible.sqlplan → horrible)runSanitize: alphanumeric + hyphens/underscores only, max 32 chars.
File headers: analysis.md → # Analysis — <skill-name> / # Input: <first 80 chars> / # Generated: <UTC timestamp> trace.md → # Check Evaluation Log — <skill-name> / # Input: <first 80 chars> / # Generated: <UTC timestamp>
Create directories as needed. When --verbose is not present, write nothing to disk.
(17806, 17807, 0x8009030c) in ERRORLOG are the first signal that authentication is broken; sqlspn-review finds the SPN or delegation root cause
(L10, L17) may have missing VNN or listener SPNs as root cause
when the listener name has no registered SPN and clients fall back to NTLM or fail entirely
re-registration after a migration moves a service account or computer object to a new host
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.