# 画面設計書 144-AG SPNテスト

## 概要

本ドキュメントは、dbatools PowerShellモジュールにおけるAvailability Group（AG）リスナーのSPN検証機能の設計仕様を定義する。

### 本画面の処理概要

Availability GroupリスナーのService Principal Name（SPN）がActive Directoryに正しく登録されているかを検証する機能を提供する。AGリスナー経由でKerberos認証を使用するために必要なSPNが存在するかをチェックする。

**業務上の目的・背景**：AGリスナーを使用してSQL Serverに接続する場合、統合Windows認証（Kerberos）を正しく機能させるためには、適切なSPNがActive Directoryに登録されている必要がある。SPNが欠落している場合、クライアントはNTLM認証にフォールバックするか、接続に失敗する。本機能を使用することで、AGリスナーのSPN構成を事前に検証し、展開前の確認やトラブルシューティングに活用できる。

**画面へのアクセス方法**：PowerShellコンソールまたはスクリプトから以下のコマンドを実行する。
- `Test-DbaAgSpn -SqlInstance <インスタンス名>` - 全AGリスナーのSPN検証
- `Test-DbaAgSpn -SqlInstance <インスタンス名> -AvailabilityGroup <AG名>` - 特定AGのSPN検証

**主要な操作・処理内容**：
1. SqlInstanceまたはInputObjectからAG情報を取得
2. 各AGリスナーに対して必要なSPN（MSSQLSvc/listener.domain.com および MSSQLSvc/listener.domain.com:port）を生成
3. Active Directoryでサービスアカウントを検索
4. servicePrincipalNameプロパティにRequiredSPNが含まれているか確認
5. IsSetフラグで結果を報告

**画面遷移**：CLIベースのため画面遷移は存在しない。出力をSet-DbaSpnにパイプすることで、欠落SPNを自動登録可能。

**権限による表示制御**：Active Directoryの読み取り権限が必要。Credentialパラメータで代替資格情報を指定可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 344 | Test-DbaAgSpn | 主機能 | AGリスナーSPN検証 |
| 342 | Set-DbaSpn | 連携機能 | 欠落SPNの登録 |
| - | Get-DbaAvailabilityGroup | 補助機能 | AG情報の取得 |
| - | Get-DbaAgListener | 補助機能 | AGリスナー情報の取得 |
| - | Get-DbaADObject | 補助機能 | Active Directoryオブジェクト検索 |

## 画面種別

コマンドラインインターフェース（CLI）/ PowerShell関数

## URL/ルーティング

```powershell
# 全AGリスナーのSPN検証
Test-DbaAgSpn -SqlInstance <インスタンス名>

# 特定AGのSPN検証
Test-DbaAgSpn -SqlInstance sql01 -AvailabilityGroup SharePoint

# 特定リスナーのSPN検証
Test-DbaAgSpn -SqlInstance sql01 -AvailabilityGroup SharePoint -Listener spag01

# パイプライン入力
Get-DbaAvailabilityGroup -SqlInstance sql01 -AvailabilityGroup SharePoint | Test-DbaAgSpn

# 欠落SPNの自動登録
Test-DbaAgSpn -SqlInstance sql01 | Set-DbaSpn
```

## 入出力項目

### Test-DbaAgSpn パラメータ

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| SqlInstance | DbaInstanceParameter[] | No* | テスト対象SQL Serverインスタンス |
| SqlCredential | PSCredential | No | SQL Server接続用資格情報 |
| Credential | PSCredential | No | Active Directory接続用資格情報 |
| AvailabilityGroup | string[] | No | 検証対象AG名（複数指定可） |
| Listener | string[] | No | 検証対象リスナー名（複数指定可） |
| InputObject | AvailabilityGroup[] | No* | パイプライン入力用AGオブジェクト |
| EnableException | switch | No | 例外処理モードの有効化 |

*SqlInstanceまたはInputObjectのいずれかが必須。

## 表示項目

### Test-DbaAgSpn 出力

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ComputerName | string | SQL Serverホストの完全修飾ネットワーク名 |
| SqlInstance | string | SQL Serverインスタンス名 |
| InstanceName | string | インスタンス名（デフォルトインスタンスはMSSQLSERVER） |
| SqlProduct | string | SQL Server製品バージョン文字列 |
| InstanceServiceAccount | string | SQL Serverのサービスアカウント |
| RequiredSPN | string | Active Directoryに登録されるべきSPN |
| IsSet | boolean | SPNがADに登録されているか（true=登録済み、false=未登録） |
| Cluster | boolean | クラスター構成かどうか |
| TcpEnabled | boolean | TCP/IPが有効か（AGリスナーは常にtrue） |
| Port | int | リスナーのTCPポート番号 |
| DynamicPort | boolean | 動的ポートを使用しているか（AGリスナーは常にfalse） |
| Warning | string | 警告メッセージ（通常は"None"） |
| Error | string | エラーメッセージ（SPN未登録時は"SPN missing"） |

## イベント仕様

### 1-SPN検証処理

**Test-DbaAgSpn 実行時**

1. SqlInstanceまたはInputObjectからAG情報を取得
2. AvailabilityGroupパラメータでフィルタリング（指定時）
3. 各AGのリスナーを取得（Get-DbaAgListener）
4. Listenerパラメータでフィルタリング（指定時）
5. 各リスナーに対して2つのRequiredSPNを生成：
   - `MSSQLSvc/{listener}.{domain}`（デフォルトインスタンス）または `MSSQLSvc/{listener}.{domain}:{InstanceName}`（名前付きインスタンス）
   - `MSSQLSvc/{listener}.{domain}:{Port}`
6. サービスアカウントを判別（LocalSystem/NT SERVICE/*は仮想アカウント、*$はマネージドサービスアカウント）
7. Get-DbaADObjectでADからサービスアカウントを検索
8. servicePrincipalNameプロパティにRequiredSPNが含まれるか確認
9. IsSetフラグとErrorプロパティを設定
10. 結果をPSCustomObjectとして出力

### 2-サービスアカウント判別ロジック

**処理フロー**
1. LocalSystemまたはNT SERVICE\*の場合：
   - 仮想アカウントとして検出
   - InstanceServiceAccountをコンピュータアカウント形式に変換
   - searchfor = 'Computer'
2. 末尾が'$'の場合：
   - マネージドサービスアカウント（gMSA）として検出
   - searchfor = 'Computer'
3. その他：
   - ドメインユーザーアカウント
   - searchfor = 'User'

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| SPN検証 | なし | N/A | 読み取りのみ、データベース更新なし |

本機能はActive Directoryの読み取り操作のみを行い、データベースやADへの更新は行わない。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 発生条件 |
|-------------|------|---------------|---------|
| MSG001 | 成功 | Error = "None" | SPNがADに登録済み |
| MSG002 | エラー | Error = "SPN missing" | SPNがADに未登録 |
| MSG003 | 警告 | "Virtual account detected, changing target registration to computername" | 仮想アカウント検出時 |
| MSG004 | 警告 | "Managed Service Account detected" | gMSA検出時 |
| MSG005 | 警告 | "AD lookup failure..." | AD検索失敗時 |
| MSG006 | 警告 | "SQL Service account not found. Results may not be accurate." | サービスアカウント未発見 |
| MSG007 | 警告 | "The SQL Service account has been found, but you don't have enough permission to inspect its SPNs" | SPN読み取り権限不足 |
| MSG008 | エラー | "You must supply either -SqlInstance or an Input Object" | 入力パラメータ不足 |

## 例外処理

| 例外条件 | 対応処理 |
|---------|---------|
| SqlInstanceもInputObjectも未指定 | Stop-Functionでエラー記録、処理停止 |
| AD検索失敗 | 警告メッセージを出力、処理継続 |
| SPN読み取り権限不足 | 警告メッセージを出力、該当SPNをスキップ |
| サービスアカウント未発見 | 警告メッセージを出力、結果を出力して継続 |

## 備考

- AGリスナーには2つのSPNが必要：インスタンス名形式（またはデフォルトインスタンス形式）とポート番号形式
- SQL Server 2012以上が必要（AGの最小バージョン要件）
- TcpEnabledは常にtrue、DynamicPortは常にfalse（AGリスナーの仕様）
- 結果キャッシュを使用し、同一サービスアカウントの重複検索を回避
- CredentialとDomainNameプロパティはデフォルト表示から除外（Select-Object *で参照可能）

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

各リスナーSPN検証結果のPSCustomObjectの構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 54-74行目: .OUTPUTSセクション - 出力オブジェクト構造 |
| 1-2 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 144-159行目: 最初のPSCustomObject（インスタンス名形式SPN） |
| 1-3 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 161-176行目: 2番目のPSCustomObject（ポート番号形式SPN） |

**読解のコツ**: 各リスナーに対して2つのオブジェクトが生成される。Set-DbaSpnのパラメータにマッピングするためのRequiredSPN/InstanceServiceAccountプロパティ。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 93-102行目: パラメータ定義 |
| 2-2 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 103-107行目: begin ブロック - キャッシュ初期化 |
| 2-3 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 108-116行目: process開始 - 入力検証とAG取得 |

**主要処理フロー**:
- **105行目**: $resultCacheでAD検索結果をキャッシュ
- **109-111行目**: 入力パラメータ検証
- **115行目**: Get-DbaAvailabilityGroupでAG情報取得

#### Step 3: リスナーSPN生成ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 118-124行目: AGループとリスナー取得 |
| 3-2 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 127-142行目: リスナー情報からRequiredSPN生成 |

**主要処理フロー**:
- **134行目**: FQDNからドメイン名を抽出
- **136行目**: リスナー名とドメイン名を結合してホストエントリ生成
- **138-142行目**: デフォルト/名前付きインスタンスによるSPN形式決定

#### Step 4: AD検索とSPN検証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 180-191行目: サービスアカウント種別判定 |
| 4-2 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 194-206行目: AD検索とキャッシュ処理 |
| 4-3 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 207-221行目: SPN存在確認とIsSet設定 |
| 4-4 | Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | 222-226行目: エラーフラグ設定と出力 |

**主要処理フロー**:
- **183-186行目**: 仮想アカウント（LocalSystem/NT SERVICE\*）をコンピュータアカウントに変換
- **187-190行目**: gMSAアカウント（末尾$）の検出
- **197行目**: Get-DbaADObjectでAD検索
- **210-211行目**: servicePrincipalNameにRequiredSPNが含まれるか確認
- **222-224行目**: SPN未登録時に"SPN missing"エラーを設定

### プログラム呼び出し階層図

```
Test-DbaAgSpn
    │
    ├─ Test-Bound (入力検証)
    │
    ├─ Get-DbaAvailabilityGroup (AG情報取得)
    │      └─ Connect-DbaInstance
    │
    ├─ [foreach AG]
    │      │
    │      └─ Get-DbaAgListener (リスナー情報取得)
    │             │
    │             └─ [foreach Listener]
    │                    │
    │                    ├─ RequiredSPN生成（2つ）
    │                    │     ├─ MSSQLSvc/{listener}.{domain}[:InstanceName]
    │                    │     └─ MSSQLSvc/{listener}.{domain}:{port}
    │                    │
    │                    └─ PSCustomObject作成
    │
    └─ [foreach SPN]
           │
           ├─ サービスアカウント種別判定
           │     ├─ LocalSystem/NT SERVICE\* → Computer
           │     ├─ *$ (gMSA) → Computer
           │     └─ Other → User
           │
           ├─ Get-DbaADObject (AD検索, キャッシュ使用)
           │      └─ GetUnderlyingObject()
           │
           ├─ servicePrincipalName確認
           │      └─ IsSet = true/false
           │
           └─ Select-DefaultView (出力)
```

### データフロー図

```
[入力]                              [処理]                              [出力]

SqlInstance ─┬───▶ Get-DbaAvailabilityGroup
             │              │
InputObject ─┘              ▼
                    [foreach AG]
                          │
                          ▼
                   Get-DbaAgListener
                          │
                          ▼                         PSCustomObject × 2
                   [foreach Listener]              │
                          │                        ├─ ComputerName
                          ▼                        ├─ SqlInstance
                   リスナー情報抽出                  ├─ InstanceName
                          │                        ├─ SqlProduct
                          ├─ FQDN                   ├─ InstanceServiceAccount
                          ├─ Port                   ├─ RequiredSPN
                          ├─ ServiceAccount         ├─ IsSet
                          │                        ├─ Cluster
                          ▼                        ├─ TcpEnabled
                   RequiredSPN生成                  ├─ Port
                   (2種類/リスナー)                 ├─ DynamicPort
                          │                        ├─ Warning
                          ▼                        └─ Error
                   Get-DbaADObject
                   (キャッシュ使用)
                          │
                          ▼
                   servicePrincipalName
                   プロパティ確認
                          │
                          ▼
                   IsSet/Error設定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Test-DbaAgSpn.ps1 | `public/Test-DbaAgSpn.ps1` | ソース | AGリスナーSPN検証 |
| Get-DbaAvailabilityGroup.ps1 | `public/Get-DbaAvailabilityGroup.ps1` | ソース | AG情報取得 |
| Get-DbaAgListener.ps1 | `public/Get-DbaAgListener.ps1` | ソース | AGリスナー情報取得 |
| Get-DbaADObject.ps1 | `internal/functions/Get-DbaADObject.ps1` | ソース | AD検索ヘルパー |
| Set-DbaSpn.ps1 | `public/Set-DbaSpn.ps1` | ソース | SPN登録（パイプライン連携先） |
