Kerberos を使う IIS の Windows 認証や委任設定では、SPN(Service Principal Name)の登録が成否を分けます。setspn で追加したはずなのに「本当に入った?」「どこに入った?」「HTTPS で登録していい?」と不安になったら、GUI に頼らずコマンドと PowerShell で検証・一覧化・整理までできるようにしておくのが近道です。
SPN(Service Principal Name)を最短で理解する:なぜ確認が必要なのか
SPN は「このサービスに対する Kerberos チケットを発行するための“宛先名”」です。クライアントが Web サイトへアクセスするとき、URL のホスト名から HTTP/ホスト名(または HTTP/FQDN)という SPN を組み立て、KDC(ドメインコントローラー)にチケットを要求します。
このとき、AD 上でその SPN がどのアカウント(コンピューターアカウント or サービスアカウント)に紐づいているかが一致していないと、典型的には次の問題が起きます。
- Kerberos ではなく NTLM にフォールバックしてしまう(委任ができない/二段階認証に弱いなどの運用課題)
- 重複 SPN によって Kerberos が不安定になる(チケットが別アカウントに発行される)
KRB_AP_ERR_MODIFIEDなどのエラーで認証が失敗する(“暗号化キーが一致しない”系)
つまり「追加したつもり」では不十分で、“どのオブジェクトに”“どの文字列が”“重複なく”登録されたかをコマンドで確認できる状態が重要です。
前提整理:SPN を登録すべきアカウントはどれ?
まず最初にここを間違えると、どれだけ setspn を叩いても期待通りになりません。SPN は原則として「そのサービスが実際に動作しているセキュリティプリンシパル」に登録します。
| Web サービスの実行アカウント | SPN を登録する先 | よくある例 |
|---|---|---|
| LocalSystem / NetworkService(既定のマシンアカウント) | コンピューターアカウント(例:SERVER01$) | 単一サーバーの IIS |
| ドメインユーザーのサービスアカウント | そのユーザーアカウント(例:svc_serviceaccount) | アプリプールをドメインユーザーで実行 |
| gMSA(グループ管理サービスアカウント) | gMSA オブジェクト | パスワード管理を自動化したい場合 |
| 負荷分散(NLB / ALB)配下の複数台 | 基本はサービスアカウント(共有)に集約 | 同一 SPN を複数コンピューターに置かない |
「サーバー 1 台なのにサービスアカウントに SPN を入れている」「逆に、アプリプールをサービスアカウントで動かしているのにコンピューター側へ入れている」など、登録先の取り違いがかなり多いです。まずは “どのアカウントで IIS が動いているか”(アプリプールの ID)を確認して、SPN の登録先を固定してから先へ進むのが安全です。
setspn で追加した SPN をコマンドで確認する(get-spn 相当)
結論から言うと、get-spn のような専用コマンドは基本的にありません。そのため「確認・一覧」も setspn(または AD コマンドレット)で行うのが実務上の定石です。
最短で“追加できているか”を確認する:対象アカウントの一覧(setspn -L)
サービスアカウントに SPN を追加したなら、まずは対象アカウントの SPN 一覧を出します。これがいちばん手堅い確認方法です。
setspn -L svc_serviceaccount
コンピューターアカウントに対して確認するなら末尾に $ を付けます。
setspn -L SERVER01$
ポイントは、ここで “目視できる形で” 目的の SPN 文字列が入っていることです。後工程(削除・付け直し)でも、この一覧出力が最も役に立ちます。
“その SPN がどのアカウントに入っているか”を逆引きする(setspn -Q)
追加した SPN が本当にそのアカウントに紐づいているか、あるいは既に別のアカウントへ入っていて重複していないかを確認するには、SPN 文字列で検索します。
setspn -Q HTTP/server01.contoso.com
フォレスト内のどこかに同じ SPN が存在しないか、より広く確認したい場合は -F を付けます(環境によっては有用です)。
setspn -F -Q HTTP/server01.contoso.com
また、ワイルドカードで関連する SPN をざっくり洗い出すのも実務ではよくやります。
setspn -Q HTTP/server01*
検索結果が複数オブジェクトにまたがって表示される場合、重複 SPN の疑いがあります。Kerberos は “どれにチケットを発行すべきか” を決められず不安定になりがちなので、後述の重複検査も必ずセットで行ってください。
“正しく入ったか”を強めに保証する:重複 SPN の検査(setspn -X)
SPN の確認で見落としがちなのが、同じ SPN が別アカウントにも登録されている状態です。これがあると「一覧にはあるのに Kerberos にならない」「突然エラーになる」など、原因不明の動作になりやすいです。
ドメイン内の重複 SPN を検出:
setspn -X
フォレスト全体で重複を検出(環境によっては時間がかかります):
setspn -F -X
“追加できているか”だけではなく、“重複なく正しいか”まで確認して初めて、トラブルを大きく減らせます。
追加時点から事故を減らす:setspn -S(重複チェック付き追加)
SPN の追加には -A(追加のみ)と、重複をチェックしながら追加する -S があります。運用上は基本的に -S を推奨します(オプションは大文字小文字を区別しないため、-s でも動作する環境があります)。
setspn -S HTTP/server01.contoso.com svc_serviceaccount
すでに同じ SPN が別オブジェクトに登録されている場合、-S は追加を拒否するため、後から気づくよりも安全です。
| 操作 | 推奨オプション | 意図 |
|---|---|---|
| 追加 | setspn -S | 重複を検知して事故を防ぐ |
| 特定 SPN の所在確認 | setspn -Q | どのアカウントに登録されているか逆引き |
| 対象アカウントの一覧 | setspn -L | 追加・削除のエビデンスになる |
| 重複検査 | setspn -X | Kerberos 不安定要因を先に潰す |
PowerShell で SPN 一覧を確認する(実務で使えるパターン集)
GUI(属性エディター)を開かずに、PowerShell で AD 属性 servicePrincipalName(表示上は ServicePrincipalNames)を取得する方法です。RSAT(ActiveDirectory モジュール)が使える端末・サーバーで実行します。
ドメインユーザー(サービスアカウント)が対象の場合
Get-ADUser -Identity svc_serviceaccount -Properties ServicePrincipalNames |
Select-Object -ExpandProperty ServicePrincipalNames
gMSA が対象の場合
Get-ADServiceAccount -Identity gmsa_webapp -Properties ServicePrincipalNames |
Select-Object -ExpandProperty ServicePrincipalNames
コンピューターアカウントが対象の場合
Get-ADComputer -Identity SERVER01 -Properties ServicePrincipalNames |
Select-Object -ExpandProperty ServicePrincipalNames
“この SPN を持っているのは誰?”を PowerShell で逆引きする
setspn -Q と同等のことを PowerShell でもできます。検索結果が複数返る場合は重複を疑います。
$spn = "HTTP/server01.contoso.com"
Get-ADObject -LDAPFilter "(servicePrincipalName=$spn)" -Properties servicePrincipalName |
Select-Object Name,ObjectClass,DistinguishedName
現場では、setspn(現場で通りがいい)+ PowerShell(出力加工がしやすい)を併用すると、確認・証跡作成・差分比較が非常に楽になります。
重要:SPN は通常 “HTTPS” ではなく “HTTP” を使う
ここが今回のテーマでも特に事故が多いポイントです。Web サイトが SSL/TLS で https:// だったとしても、Kerberos の SPN サービスクラスは多くのケースで HTTP を使います。
つまり、次のように “HTTPS” で登録してしまうと、クライアントが要求する SPN(HTTP/…)と一致せず、Kerberos が期待通りに動かない可能性が高まります。
setspn -S HTTPS/server01.contoso.com svc_serviceaccount
Web アクセスで実際に参照されるのは、基本的に URL のホスト部分から組み立てられる HTTP/hostname です。よって、実務では次のような組み合わせを押さえることが多いです。
| アクセスされる URL 例 | クライアントが要求しがちな SPN | 登録の考え方 |
|---|---|---|
https://server01/ | HTTP/server01 | 短縮名でアクセスするなら短縮名も登録 |
https://server01.contoso.com/ | HTTP/server01.contoso.com | FQDN でアクセスするなら FQDN も登録 |
https://app.contoso.com/(DNS 別名/CNAME) | HTTP/app.contoso.com | 別名でアクセスさせるなら別名分の SPN が必須 |
運用としては、利用される URL(短縮名・FQDN・別名)を洗い出し、必要な HTTP/... を過不足なく揃えるのが王道です。逆に言えば、不要な “HTTPS/…” は削除して整理したほうが、後々のトラブルシュートが圧倒的に楽になります。
不要な SPN を削除する:まずは setspn -D が確実
SPN の削除は Kerberos 認証(IIS の Windows 認証など)に直接影響します。やみくもに消すのではなく、削除対象が本当に使われていないことを確認してから実施してください(後述の「動作確認」と「よくあるハマりどころ」もあわせて参照してください)。
削除前にやるべき準備(失敗しても戻せる状態にする)
- 現在の SPN 一覧を退避(差分比較・戻しに使う)
- 対象サイトのアクセス URL(ホスト名)を棚卸し
- 重複 SPN の有無をチェック(
setspn -X) - 変更後に確認する観点を決める(Kerberos チケット/ログ/アプリ動作)
退避の例(コマンド出力をそのまま証跡化):
setspn -L svc_serviceaccount > spn_before.txt
setspn -D で “登録されている文字列そのもの” を削除する
不要なエントリを確実に消したいときは、setspn -D で “登録されている文字列そのもの” を指定して削除します。
setspn -D HTTPS/server02.contoso.com svc_serviceaccount
setspn -D HTTPS/server03.contoso.com svc_serviceaccount
削除後は再度一覧表示して、意図したものだけが残っているか確認します。
setspn -L svc_serviceaccount
もし正しくは HTTP/... で運用すべきなら、削除後に必要な分だけ付け直します。
setspn -S HTTP/server02.contoso.com svc_serviceaccount
setspn -S HTTP/server03.contoso.com svc_serviceaccount
PowerShell で SPN を削除する(まとめて消したい/自動化したい場合)
PowerShell での削除は、複数の SPN をまとめて処理したいときに便利です。注意点として、属性名は LDAP 表示名の servicePrincipalName(単数)を使うのが基本です。
Set-ADUser -Remove で削除(ユーザーのサービスアカウントの場合)
Set-ADUser -Identity svc_serviceaccount -Remove @{
servicePrincipalName = @(
"HTTPS/server02.contoso.com",
"HTTPS/server03.contoso.com"
)
}
gMSA の場合は Set-ADServiceAccount -Remove
Set-ADServiceAccount -Identity gmsa_webapp -Remove @{
servicePrincipalName = @(
"HTTPS/server02.contoso.com",
"HTTPS/server03.contoso.com"
)
}
削除 → 追加を PowerShell で一気にやる例(HTTP に正規化)
「HTTPS で入れてしまったものを整理して、HTTP に寄せたい」ケースでは、削除と追加をセットで実行したくなります。単純に連続実行するだけでもよいですが、事故を減らすなら “現状の一覧確認 → 変更 → 再確認” の流れをスクリプト化すると安全です。
$id = "svc_serviceaccount"
$remove = @(
"HTTPS/server02.contoso.com",
"HTTPS/server03.contoso.com"
)
$add = @(
"HTTP/server02.contoso.com",
"HTTP/server03.contoso.com"
)
# 現状退避
(Get-ADUser -Identity $id -Properties ServicePrincipalNames).ServicePrincipalNames |
Out-File -Encoding utf8 "spn_before_$id.txt"
# 削除
Set-ADUser -Identity $id -Remove @{ servicePrincipalName = $remove }
# 追加
Set-ADUser -Identity $id -Add @{ servicePrincipalName = $add }
# 変更後確認
(Get-ADUser -Identity $id -Properties ServicePrincipalNames).ServicePrincipalNames |
Out-File -Encoding utf8 "spn_after_$id.txt"
運用環境によっては「追加は setspn -S を使って、PowerShell は確認と出力整形に限定する」ほうが説明コストが低い場合もあります。チームのスキルセットに合わせて使い分けるのが現実的です。
変更後の動作確認:Kerberos で認証されているかを確かめる
SPN を追加・削除したら、コマンド上の一覧確認だけで終わらせず、実際にクライアントが Kerberos チケットを取得できているかまで確認するのがおすすめです。ここまでやると「入れたのに動かない」の切り分けが一気に短くなります。
クライアント側:klist で HTTP チケットを確認する
対象サイトへアクセスする前後で、Kerberos チケットを確認します(管理者権限が必要な場合があります)。
klist
より直接的に “この SPN のチケットを取りに行く” なら、次のような確認も有効です。
klist get HTTP/server01.contoso.com
ここでチケットが取れない場合は、SPN が見つからない・重複している・アクセスしているホスト名が想定と違う(別名/CNAME/短縮名)などの可能性が上がります。
サーバー側:Kerberos にならないときの観点
- IIS のサイトに Windows 認証が有効か、プロバイダ順が意図通りか(Negotiate が必要)
- アプリプール ID(実行アカウント)が想定と一致しているか
- DNS(A/CNAME)とアクセス URL のホスト名が棚卸し通りか
- ドメインコントローラー間のレプリケーションが反映されているか(変更直後は特に)
よくあるハマりどころ:SPN が“入っているのに”動かない理由
SPN の追加・削除は手順自体は単純ですが、周辺要素(DNS、別名、負荷分散、実行アカウント)が絡むと一気に複雑化します。よくある症状と原因の対応表を用意しておくと、現場の切り分けが速くなります。
| 症状 | 疑うポイント | 確認・対処 |
|---|---|---|
| 常に NTLM になる | アクセスホスト名と SPN が一致していない | setspn -Q HTTP/実際のホスト名 で逆引きし、必要な別名分の SPN を追加 |
| 時々 Kerberos、時々失敗 | 重複 SPN | setspn -X で重複を洗い出して整理 |
KRB_AP_ERR_MODIFIED | SPN の紐づけ先が間違っている(別アカウントの鍵で暗号化されたチケット) | サービス実行アカウントを再確認し、SPN を正しいオブジェクトへ移動(誤登録を削除) |
| 変更直後だけ不安定 | AD レプリケーション未反映 | 複数 DC 環境では反映まで時間差が出る。確認は -F -Q も活用 |
| 短縮名では NG、FQDN では OK(または逆) | 短縮名/FQDN の SPN 片方しか登録していない | HTTP/server01 と HTTP/server01.contoso.com を必要に応じて揃える |
| LB 配下で一部のノードだけ失敗 | ノードごとに SPN を持ってしまっている/共有アカウント設計が崩れている | 同一 SPN は原則 1 つのプリンシパルへ。設計を見直し、SPN をサービスアカウントに集約 |
運用で事故を減らす:SPN 管理のベストプラクティス
SPN は “一度通れば終わり” ではなく、URL 追加、別名追加、サーバー更改、アプリプール変更などで簡単に崩れます。属人化を避けるためにも、次の運用をおすすめします。
- SPN は「アクセスされるホスト名」起点で棚卸しし、必要な
HTTP/...をリスト化して管理する - 追加はなるべく
setspn -Sを使い、重複を最初から作らない - 変更前後は
setspn -Lの出力をファイル退避して証跡を残す - 定期的に
setspn -Xを実行し、重複が発生していないか点検する - サービスアカウント運用では、可能なら gMSA を検討し、パスワード管理と監査を簡素化する
- SPN は Kerberos だけでなく、攻撃面(Kerberoasting など)の観点でも重要になるため、サービスアカウントのパスワード強度と権限最小化をセットで考える
最後に、今回の要点を“手順として”まとめると次の流れが堅いです。
- サービス実行アカウント(アプリプール ID など)を確定する
setspn -L 対象で現状一覧を退避setspn -Q HTTP/ホスト名で登録先と重複を確認- 不要な
HTTPS/...等があればsetspn -D(または PowerShell)で削除 - 必要な
HTTP/...をsetspn -Sで追加 setspn -Xで重複がないことを確認- クライアントで
klist等により Kerberos チケット取得を確認
この一連を押さえておけば、「追加したのに動かない」「削除したら壊れた」を最小限にしつつ、GUI を開かずに SPN を管理できます。

コメント