# GuardDuty × Amazon Detective：検知の『次』、根本原因と影響範囲を調査するワークフロー

> GuardDuty が Critical を上げた——では『何が起きて、どこまで侵されたのか』に答えられますか。Amazon Detective は CloudTrail・VPC Flow Logs・GuardDuty finding・EKS 監査ログから最大1年分の行動グラフを構築し、根本原因と影響範囲（ブラストラディウス）を可視化します。finding からのピボット、finding groups、IOC を使う Detective Investigation、StartInvestigation の CLI／EventBridge 自動起動、組織の委任管理者整合まで、detect→investigate→respond の調査レイヤーを公式ドキュメント（2026年6月）に基づき実コードで設計します。

- 公開日: 2026-06-27
- 著者: 友田 陽大
- タグ: セキュリティ, AWS, GuardDuty, Amazon Detective, インシデント対応
- URL: https://tomodahinata.com/blog/aws-guardduty-amazon-detective-investigation-root-cause-workflow-guide
- カテゴリ: Amazon GuardDuty 本番運用
- 総合ガイド: https://tomodahinata.com/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide

## 要点

- GuardDuty は『点／連鎖』を検知するだけ。『なぜ起きて・どこまで侵されたか（根本原因と影響範囲）』に答えるのは Amazon Detective——両者は前後関係であって競合ではない
- Detective は CloudTrail 管理イベント・VPC Flow Logs・GuardDuty finding・EKS 監査ログから ML＋統計＋グラフ理論で『行動グラフ』を構築し、最大1年分の履歴を可視化する
- GuardDuty finding から『Investigate with Detective』でピボットできる（両サービスを同一アカウント・同一リージョンで有効化していることが前提）。finding groups は関連 finding とエンティティを1つの security event に束ねる
- Detective Investigation は IAM ユーザー／ロールを IOC で調査し MITRE ATT&CK で構造化する。StartInvestigation API / CLI で自動起動でき、Critical な GuardDuty finding を EventBridge で受けて自動調査→通知まで配線できる（冪等・最小権限）
- マルチアカウントでは Detective の委任管理者を GuardDuty の委任管理者と揃える。Terraform は aws_detective_graph / aws_detective_organization_admin_account / aws_detective_organization_configuration / aws_detective_member で組む

---

GuardDuty が Critical を上げた。Slack が赤く光る。で、結局——**「何が起きて、どこまで侵されたの？」** に、いま答えられますか。

「`AttackSequence:IAM/CompromisedCredentials` です」とコンソールの finding 名を読み上げることはできます。でもクライアントが本当に知りたいのは、その先です。**いつ・どの認証情報が・どの IP から・どのリソースに触れて・どこまで広がったのか**。インシデント対応の現場で時間を溶かすのは「検知」ではなく、この **「根本原因（root cause）と影響範囲（blast radius）の確定」** です。CloudTrail を `grep` し、VPC Flow Logs を Athena でこねくり回し、IAM のイベント履歴を時系列に並べ直す——その何時間かが、報告と封じ込めの判断を遅らせます。

この記事は、その何時間かを潰すための **「検知の次」のワークフロー**です。題材は **Amazon GuardDuty × Amazon Detective**。GuardDuty が**点（あるいは連鎖）を検知**したあと、**Amazon Detective で根本原因と影響範囲を調査する**——その配管を、公式ドキュメント（2026年6月時点）に基づいて Terraform / bash / Python の実コードで通します。

GuardDuty 本体の本番設計（保護プラン選定・組織一括有効化・EventBridge 自動対応）は[本編ガイド](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)で、5つの AWS セキュリティサービスの役割分担は[比較記事](/blog/aws-guardduty-vs-security-hub-detective-inspector-macie-comparison-guide)で扱いました。本記事はその**間にある一層——「調査（investigate）」**だけを深掘りします。題材として、私がマルチアカウント AWS 上の[サーバーレス決済プラットフォーム](/case-studies/payment-platform-reliability)で IAM・可観測性・DR を横断実装した経験——**実際の金銭・カーボンクレジット・地域通貨を扱うため、「異常を検知したら、被害範囲を時間内に確定して報告する」までを仕組みで担保する必要があった**——という視点も交えます。

> **この記事のルール**：Detective の定義・データソース・finding groups／Investigation の仕様・料金体系は **AWS 公式ドキュメント（2026年6月時点）** に基づきます。対応データソース・機能・料金は改定されるため、本番投入前に必ず公式の最新情報を確認してください。そして大前提——**Detective は「検知」でも「防御」でもなく「調査（investigation）」のサービス**です。新しい脅威を見つけるのは GuardDuty、攻撃を止めるのは WAF・最小権限 IAM の仕事。Detective が答えるのは「検知された脅威の、原因と範囲」だけです。自動化コード（自動調査の起動）は **冪等・最小権限・破壊的操作を伴わない** ものに限ります。

---

## 0. メンタルモデル：detect → investigate → respond の3層

設計を始める前に、セキュリティ運用を**3つの動詞**で固定します。これが本記事のすべての土台です。

> **GuardDuty が DETECT（検知）し、Detective が INVESTIGATE（調査）し、自動対応が RESPOND（対応）する。** この3層は順番であって、競合ではありません。

| 層 | 動詞 | サービス | 答える問い |
| --- | --- | --- | --- |
| **DETECT** | 検知する | **GuardDuty** | 「いま、悪いことが起きているか？」（点／攻撃シーケンス） |
| **INVESTIGATE** | 調査する | **Amazon Detective** | 「なぜ起きて、どこまで侵されたか？」（根本原因・影響範囲） |
| **RESPOND** | 対応する | **EventBridge → 自動対応 / runbook** | 「どう封じ込め、どう復旧し、どう再発を防ぐか？」 |

多くの「GuardDuty 入れました」案件は、**DETECT で止まっています**。finding は出る、Slack にも飛ぶ、でもその finding が指す `i-0abc...` というインスタンスや `AROAEXAMPLE...` というロールが、**過去どう振る舞い、何に触れ、どこから来た IP と通信していたか**は、誰も即座には答えられない。だから封じ込めの判断も、影響を受けた顧客への報告も遅れます。

ここから3つの帰結が出ます。

1. **DETECT と INVESTIGATE は別レイヤーで、別ツールが担う。** GuardDuty は「異常な振る舞いがある」と finding を出しますが、その finding 単体には**文脈（context）と歴史（history）がありません**。Detective は CloudTrail・VPC Flow Logs・GuardDuty finding・EKS 監査ログを**自動で集約して「行動グラフ（behavior graph）」を構築**し、**最大1年分の履歴**で「このエンティティは普段どうで、いつから逸脱したか」を見せます。**点を線にし、線を文脈にする**のが Detective です。
2. **INVESTIGATE を飛ばして RESPOND に行くと事故る。** 影響範囲を確定する前にインスタンスを終了したり鍵を消したりすると、**フォレンジックの証拠を消し、正規利用を巻き込み、根本原因を永久に失います**。「調査 → 範囲確定 → 封じ込め」の順序は、決済基盤のように**金銭が動く環境では特に**崩せません。
3. **3層は1本のパイプラインとして配線できる。** GuardDuty finding → EventBridge → 自動で Detective Investigation を起動 → 結果を通知 → 人間がトリアージ → [自動修復／runbook](/blog/aws-guardduty-eventbridge-automated-remediation-incident-response-guide)。本記事の山場は **5章（StartInvestigation の自動起動）**と **6章（end-to-end ワークフロー）**です。

この3層を押さえると、「Detective を入れる」という作業が、実は **「①GuardDuty と紐づけて調査の入口を作る → ②finding groups と Investigation で根本原因・範囲を出す → ③それを自動対応・runbook に橋渡しする」** の3つの設計だと分かります。順に作ります。

---

## 1. Amazon Detective とは：行動グラフで「文脈」を再構築する装置

まず Detective が何で、何でないかを一行で固定します。

> **Amazon Detective ＝ CloudTrail・VPC Flow Logs・GuardDuty finding・EKS 監査ログを自動収集し、ML・統計分析・グラフ理論で「行動グラフ（behavior graph）」を構築して、セキュリティ finding や不審な活動の根本原因を素早く特定するための調査サービス。**

公式の定義はこうです——*"Amazon Detective helps you analyze, investigate, and quickly identify the root cause of security findings or suspicious activities."* そして *"It then uses machine learning, statistical analysis, and graph theory to generate visualizations that help you to conduct faster and more efficient security investigations."*

### 1.1 何を見ているか：行動グラフのソースデータ

Detective の心臓は **behavior graph（行動グラフ）**——「1つ以上の AWS アカウントから抽出・分析されたデータを連結したもの（*a linked set of extracted and analyzed data*）」です。ソースになるのは次の4つ。

| ソースデータ | 何を抽出するか | これで答えられること |
| --- | --- | --- |
| **CloudTrail 管理イベント** | ログイン試行・API コール（コントロールプレーン操作） | 「この IAM ロールは普段どの API を叩く？ この呼び出しは異常か？」 |
| **VPC Flow Logs** | EC2／Kubernetes pod の IP トラフィック量・通信相手 | 「このインスタンスからのトラフィック急増は想定内か？ どの IP と通信した？」 |
| **GuardDuty finding** | 検知された脅威そのもの（全 finding 型を取り込む） | 「この finding に関わるエンティティは、他にどんな finding に出てくる？」 |
| **EKS 監査ログ** | Kubernetes コントロールプレーンの操作 | 「侵害された pod は、どの secrets / API に触れた？」 |

決定的なのは——**これらを「自分で集めて結合する」必要がない**点です。公式いわく *"With Detective, you don't have to organize any data or develop, configure, or tune your own queries and algorithms."* Athena のクエリも、CloudTrail と Flow Logs を JOIN する ETL も、書かなくていい。Detective が**自動抽出・自動結合・自動可視化**します。

### 1.2 最大1年分の履歴：「いつから逸脱したか」が見える

もう一つの核は **時間軸**です。公式は *"you can access up to a year of historical event data"* と明記します。これは「ベースラインからの逸脱」を見るうえで決定的です。Detective の可視化は *"Is this an unusual API call for this role?"*（このロールにとってこの API は異常か？）や *"Is this spike in traffic from this instance expected?"*（このトラフィック急増は想定内か？）といった問いに、**過去のベースラインと比較して**答えます。

> **設計への含意**：GuardDuty の finding は「いま」のスナップショットです。Detective は「いままで」を持っています。**「この認証情報、3日前から普段使わないリージョンで呼ばれ始めてますね」**——この一文を出せるかどうかが、インシデント対応の所要時間を分けます。

### 1.3 Detective が「やらないこと」

混同を防ぐため、Detective の境界を明示します。

- **新しい脅威を検知しない。** それは GuardDuty（と Inspector・Macie）の仕事。Detective は「検知された脅威」の調査だけ。
- **攻撃を止めない。** 封じ込めは EventBridge → 自動対応や手動の runbook。Detective は読み取り中心の分析装置。
- **finding を集約・正規化しない。** ASFF への正規化と single pane of glass は Security Hub の役割（[比較記事](/blog/aws-guardduty-vs-security-hub-detective-inspector-macie-comparison-guide)参照）。

**GuardDuty が点を打ち、Detective が線を描き、Security Hub が机に並べ、EventBridge が手を動かす**——役割が一意なので、設計で迷いません。

---

## 2. ピボット：GuardDuty finding から Detective へ

調査の入口は「ピボット（pivot）」です。GuardDuty のコンソールで finding を選び、**そのまま Detective の該当エンティティへ飛ぶ**。これが最短の調査開始です。

### 2.1 前提：両サービスを同一アカウント・同一リージョンで有効化

公式の手順は明快です——*"To use Amazon Detective with GuardDuty you must first enable Amazon Detective."* そして *"When you enable both GuardDuty and Detective, the integration is enabled automatically. Once enabled, Detective will immediately ingest your GuardDuty findings data."*

つまり**統合のために特別な配線は要りません**。両方有効化すれば、Detective は GuardDuty finding を即座に取り込み始めます。ただし2つの制約：

- **Detective はリージョンサービス**：*"Detective is a regional service, meaning you must enable Detective and add your member accounts in each region in which you want to use the integration."* GuardDuty を有効化した各リージョンで、Detective も有効化する必要があります。
- **export 頻度を15分に**：公式は *"it is recommended that you change the export frequency to 15 minutes"* と推奨します。デフォルトの6時間だと、Detective が finding の更新を受け取るのが遅れる。これは[本編ガイド](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)で `finding_publishing_frequency = "FIFTEEN_MINUTES"` を推した理由と同じです。

### 2.2 ピボットの操作

コンソールでの流れはこうです。

```text
GuardDuty コンソール
  │  1. findings テーブルから finding を1つ選ぶ
  │  2. 詳細ペインで [Investigate with Detective] を押す
  │  3. 調査する「観点（aspect）」を選ぶ
  ▼
Detective コンソール（該当 finding／エンティティのプロファイルが開く）
```

ここで重要なのは、**finding 型に応じて複数のエンティティへピボットできる**点です。公式が挙げるピボット先は——*"AWS account, IAM role, user, or role session, user agent, federated user, Amazon EC2 instance, or IP address."* たとえば認証情報漏洩系の finding なら「その IAM ロール」へ、ネットワーク系なら「その EC2 インスタンス」や「相手 IP」へ飛んで、**それぞれのエンティティプロファイル（entity profile）**——普段の振る舞い・関わった finding・通信相手——を一望できます。

> **運用の小ワザ**：*"If you archive a GuardDuty finding in the Detective console, that finding gets archived in the GuardDuty console as well."* Detective 側でアーカイブすると GuardDuty 側も連動してアーカイブされます。調査の過程で「これは正規利用と確定」と判断したものを、片側で閉じれば両方閉じる——トリアージの一貫性が保てます。

---

## 3. Finding groups：関連 finding を「1つの security event」に束ねる

ピボットが「1つの finding を深掘りする」入口なら、**finding groups** は「**バラバラの finding を1つのインシデントとして束ねる**」上位の視点です。**HIGH／Critical な GuardDuty finding の根本原因分析の主役**がこれです。

### 3.1 なぜ finding を「群」で見るのか

攻撃者は1つの操作で終わりません。公式の言葉を借りれば——*"If a threat actor is attempting to compromise your AWS environment, they typically perform a sequence of actions that lead to multiple security findings and unusual behaviors. These actions are often spread across time and entities."*

問題は、これらを**個別に（in isolation）見ると意味を読み違える**ことです。*"When security findings are investigated in isolation, it can lead to a misinterpretation of their significance, and difficulty in finding the root cause."* 「ポートスキャン」「失敗ログイン」「見慣れない API 呼び出し」——単体ではノイズに見えるものが、**同じ IAM ロールセッション・同じ IP** で繋がると、1つの攻撃ストーリーになります。

Detective はこれを **グラフ分析（graph analysis）** で解きます——*"applying a graph analysis technique that infers relationships between findings and entities, and groups them together."* そして公式の推奨は明快——*"We recommend treating finding groups as the starting point for investigating the involved entities and findings."* **finding group を調査の出発点にせよ**、と。

### 3.2 グルーピングの4つの基準

finding group がどう作られるか、公式が挙げる基準は4つ。

| 基準 | 内容 |
| --- | --- |
| **Temporal Proximity（時間的近接）** | 近い時間帯に起きた finding は同じインシデントの可能性が高く、まとめられる |
| **Common Entities（共通エンティティ）** | 同じ IP・ユーザー・リソースに関わる finding をまとめ、影響範囲を環境横断で把握する |
| **Patterns and Behaviors（パターンと挙動）** | 似た攻撃種別・不審活動のパターンから関連を判定 |
| **TTPs（戦術・技術・手順）** | **MITRE ATT&CK** のような枠組みで共通する TTP を持つ finding をまとめ、連携攻撃を浮かび上がらせる |

各 group には finding だけでなく、関わった**エンティティ**——AWS 外の IP アドレスや user agent も含む——が入ります。Detective は各 group の **インタラクティブな可視化（interactive visualization）** を提供し、エンティティと finding の繋がりと根本原因を辿りやすくします。

### 3.3 タイミングの注意

実運用で効く一文——*"After an initial GuardDuty finding occurs that is related to another finding, the finding group with all related findings and all involved entities is created within 48 hours."*

つまり **finding group の完成には最大48時間かかり得ます**。最初の GuardDuty finding（特に Critical な `AttackSequence:*`）に対しては、**「まず単体で初動トリアージ → finding group が育ったら全体像で再評価」** という二段構えが現実的です。「group がまだ無いから関連 finding が無い」と早合点しないこと。

> **GuardDuty Extended Threat Detection との関係**：GuardDuty 側の ETD は「弱いシグナルを24時間窓で相関して `AttackSequence:*`（必ず Critical）に束ねる」検知レイヤー（[攻撃シーケンスの記事](/blog/aws-guardduty-extended-threat-detection-attack-sequence-findings-guide)参照）。Detective の finding groups は「**検知された複数 finding を、行動グラフ上で群として可視化・調査する**」調査レイヤー。**ETD が『攻撃シーケンスを検知する』なら、finding groups は『そのシーケンスの根本原因と全エンティティをグラフで解く』**——前者は GuardDuty、後者は Detective、と役割が分かれます。

---

## 4. Detective Investigation：IAM を IOC で調査し、MITRE ATT&CK で構造化する

finding groups が「群の可視化」なら、**Detective Investigation** は「**特定の IAM エンティティを、侵害指標（IOC）で機械的に調査して、レポート化する**」自動調査機能です。トリアージの自動化に直結します。

### 4.1 何を調べるか：IAM ユーザー／ロール × IOC

公式の定義——*"You can use Amazon Detective Investigation to investigate IAM users and IAM roles using indicators of compromise, which can help you determine if a resource is involved in a security incident."*

**IOC（indicator of compromise／侵害指標）** とは——*"an artifact observed in or on a network, system, or environment that can (with a high level of confidence) identify malicious activity or a security incident."* 高い確度で「悪意ある活動／インシデント」を示す痕跡のことです。

Detective Investigation は **ML モデルと脅威インテリジェンス**を使い、*"to surface only the most critical, suspicious issues"*——**最も重大で疑わしい問題だけを浮かび上がらせます**。調査対象は公式いわく *"attack tactics, impossible travel, flagged IP addresses, and finding groups"*——攻撃戦術・地理的にあり得ない移動（impossible travel）・フラグ付き IP・finding groups。そして **初期調査ステップを実行し、リスクをハイライトしたレポートを生成**します（*"performs initial security investigation steps and generates a report highlighting the risks identified by Detective"*）。

> **なぜ IAM 中心か**：クラウド侵害の多くは「漏洩した認証情報の悪用」から始まります。Detective Investigation が **IAM ユーザー／ロール**にフォーカスするのは、ここが攻撃の起点になりやすいからです。`AttackSequence:IAM/CompromisedCredentials` のような GuardDuty finding が出たら、その IAM エンティティに対して Investigation を回す——という流れが自然です。

### 4.2 起動方法：コンソール／API／CLI

Detective Investigation は3つの方法で起動できます。

1. **コンソール**：Detective コンソールから手動で実行。
2. **API**：`StartInvestigation` 操作（プログラマティック）。
3. **CLI**：`aws detective start-investigation`。

`StartInvestigation` の**必須パラメータ**は公式 API リファレンスで確認できます（すべて Required）。

| パラメータ | 型 | 内容 |
| --- | --- | --- |
| **`GraphArn`** | String | 行動グラフの ARN（`arn:aws:detective:<region>:<account>:graph:<id>`） |
| **`EntityArn`** | String | 調査対象の **IAM ユーザー／ロールの ARN**（`arn:` で始まる） |
| **`ScopeStartTime`** | Timestamp | 調査の開始日時（UTC ISO8601、例 `2026-06-25T00:00:00Z`） |
| **`ScopeEndTime`** | Timestamp | 調査の終了日時（UTC ISO8601） |

レスポンスは **`InvestigationId`**（固定長21桁の数値文字列）。これでレポートを後から参照します。

> **注意**：`EntityArn` は **IAM ユーザー／ロールの ARN** であって、EC2 インスタンス ID やバケット名ではありません（API のパターンは `^arn:.*`、用途は IAM）。つまり Investigation は「**この IAM エンティティが、この時間窓で侵害に関与したか**」を問う道具です。EC2/IP/S3 の文脈は、2章のピボットや3章の finding groups で辿ります。

### 4.3 CLI で1回の調査を起動する

まず手で1回回して挙動を掴みます。`GraphArn` は事前に取得しておきます。

```bash
# 行動グラフの ARN を取得（このアカウント・このリージョンの graph は1つ）。
GRAPH_ARN=$(aws detective list-graphs --query 'GraphList[0].Arn' --output text)

# 疑わしい IAM ロールを、過去48時間のスコープで調査する。
# - entity-arn: 調査対象は IAM ユーザー/ロールの ARN（インスタンス ID ではない）
# - scope-*-time: UTC ISO8601。GuardDuty finding の発生時刻を含む窓を切る
aws detective start-investigation \
  --graph-arn "$GRAPH_ARN" \
  --entity-arn "arn:aws:iam::123456789012:role/app-runtime-role" \
  --scope-start-time "2026-06-25T00:00:00Z" \
  --scope-end-time   "2026-06-27T00:00:00Z"
# => { "InvestigationId": "..." } が返る。レポートは get-investigation で取得。
```

返ってきた `InvestigationId` で、レポート（リスク・IOC・MITRE ATT&CK マッピング）を取得・確認します。**これを「人が finding を見てから手で叩く」のではなく、「Critical な GuardDuty finding が出た瞬間に自動で叩く」**——それが次章です。

---

## 5. 自動起動：Critical な GuardDuty finding → Detective Investigation（EventBridge）

ここが本記事の山場です。**初動の調査を、人間の反応速度から切り離します**。GuardDuty が Critical（特に `AttackSequence:*`）を上げたら、**EventBridge が即座に Lambda を起動し、Lambda が該当 IAM エンティティに対して Detective Investigation を自動で開始**し、`InvestigationId` を添えて通知する——という配線です。

### 5.1 設計原則（自動対応と同じ縛り）

[本編ガイドの自動対応](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)と同じ原則を、調査の自動起動にも課します。

- **冪等（idempotent）**：EventBridge は **at-least-once 配信**。同じ finding で Lambda が2回起動し得るので、**同一エンティティ×同一時間窓の二重調査を抑止**する。
- **スコープを絞る（least-privilege）**：Lambda の実行ロールは `detective:StartInvestigation` と最小限の読み取り／通知に限定。封じ込めの権限（EC2 停止・IAM 鍵削除）は**一切与えない**——これは調査の起動であって、対応ではないから。
- **破壊的操作を伴わない**：やるのは「調査を開始する＋通知する」だけ。**何も停止せず、何も削除しない**。だから自動化しても安全。RESPOND（封じ込め）は人間のトリアージ後、別の許可リスト付きパイプラインで。

### 5.2 EventBridge ルール（Terraform）

Critical な GuardDuty finding、かつ IAM エンティティを伴うものだけを拾います。

```hcl
# Critical(severity >= 9) の GuardDuty finding を捕捉して、自動調査 Lambda へ流す。
# 攻撃シーケンス(AttackSequence:*)は必ず Critical なので、ここで確実に拾える。
resource "aws_cloudwatch_event_rule" "guardduty_critical" {
  name        = "guardduty-critical-to-detective"
  description = "Critical GuardDuty findings -> auto-start a Detective Investigation"
  event_pattern = jsonencode({
    source        = ["aws.guardduty"]
    "detail-type" = ["GuardDuty Finding"]
    detail        = { severity = [{ numeric = [">=", 9] }] }
  })
}

resource "aws_cloudwatch_event_target" "to_investigator" {
  rule      = aws_cloudwatch_event_rule.guardduty_critical.name
  target_id = "auto-investigator"
  arn       = aws_lambda_function.investigator.arn

  # 一過性の失敗に備えてリトライ＋DLQ（調査の取りこぼしを作らない）。
  retry_policy {
    maximum_event_age_in_seconds = 3600
    maximum_retry_attempts       = 4
  }
  dead_letter_config { arn = aws_sqs_queue.investigator_dlq.arn }
}

# Lambda 実行ロールに与える権限は「調査の起動＋通知」だけ。封じ込め権限は与えない。
data "aws_iam_policy_document" "investigator" {
  statement {
    sid       = "StartDetectiveInvestigation"
    effect    = "Allow"
    actions   = ["detective:StartInvestigation"]
    resources = [var.detective_graph_arn] # 自社の behavior graph に限定
  }
  statement {
    sid       = "Notify"
    effect    = "Allow"
    actions   = ["sns:Publish"]
    resources = [aws_sns_topic.security_alerts.arn]
  }
}
```

### 5.3 自動調査 Lambda（Python）

GuardDuty finding から **IAM エンティティの ARN を取り出し**、finding の発生時刻を含む時間窓で `StartInvestigation` を呼び、`InvestigationId` を通知します。

```python
"""Critical な GuardDuty finding を受けて Detective Investigation を自動起動する Lambda。

設計原則:
  - 調査の起動のみ。封じ込め・削除などの破壊的操作は一切しない（権限も持たない）。
  - 冪等: EventBridge は at-least-once。同一 finding の再配信で二重調査しない。
  - 最小権限: detective:StartInvestigation と sns:Publish だけ。
  - スコープ限定: 調査対象は IAM ユーザー/ロールのみ（Detective Investigation の仕様）。
"""
from __future__ import annotations

import datetime as dt
import json
import logging
import os
from typing import Any, Final

import boto3

logger = logging.getLogger()
logger.setLevel(logging.INFO)

detective = boto3.client("detective")
sns = boto3.client("sns")

GRAPH_ARN: Final[str] = os.environ["DETECTIVE_GRAPH_ARN"]
ALERT_TOPIC_ARN: Final[str] = os.environ["ALERT_TOPIC_ARN"]
# 調査の時間窓: finding 発生時刻から「何時間さかのぼるか」。既定72h。
LOOKBACK_HOURS: Final[int] = int(os.environ.get("INVESTIGATION_LOOKBACK_HOURS", "72"))


def handler(event: dict[str, Any], _context: object) -> dict[str, str]:
    detail = event["detail"]
    finding_id: str = detail["id"]
    finding_type: str = detail["type"]
    severity: float = float(detail["severity"])

    # Detective Investigation の対象は IAM ユーザー/ロール。
    # finding の resource から IAM エンティティ ARN を取り出す（無ければ通知のみ）。
    entity_arn = _extract_iam_entity_arn(detail.get("resource", {}))
    if entity_arn is None:
        _notify(finding_type, severity, finding_id, entity_arn, "no-iam-entity")
        return {"action": "no-iam-entity"}

    start, end = _scope_window(detail)
    investigation_id = _start_investigation(entity_arn, start, end)  # 冪等
    _notify(finding_type, severity, finding_id, entity_arn, investigation_id)
    logger.info(
        json.dumps(
            {
                "finding_id": finding_id,
                "type": finding_type,
                "severity": severity,
                "entity_arn": entity_arn,
                "investigation_id": investigation_id,
            }
        )
    )
    return {"action": "investigation-started", "investigation_id": investigation_id}


def _extract_iam_entity_arn(resource: dict[str, Any]) -> str | None:
    """finding の resource から IAM ユーザー/ロールの ARN を取り出す（純関数・テスト容易）。"""
    access_key = resource.get("accessKeyDetails", {})
    # accessKeyDetails.principalId / userName から IAM の主体を特定するのが定石。
    # ここでは finding が持つロール/ユーザーの ARN を優先的に拾う。
    arn = access_key.get("principalId")  # 例: AROAXX: 実運用では ARN へ解決する
    role_arn = resource.get("resourceRole")  # 補助情報
    # finding 型により位置が異なるため、ARN 形式のものだけを採用する。
    for candidate in (access_key.get("arn"), arn, role_arn):
        if isinstance(candidate, str) and candidate.startswith("arn:"):
            return candidate
    return None


def _scope_window(detail: dict[str, Any]) -> tuple[str, str]:
    """finding の発生時刻を終点に、LOOKBACK_HOURS だけさかのぼる窓を作る。"""
    # service.eventLastSeen があればそれを終点に、無ければ現在時刻。
    last_seen = detail.get("service", {}).get("eventLastSeen")
    end = _parse_iso(last_seen) if last_seen else dt.datetime.now(dt.timezone.utc)
    start = end - dt.timedelta(hours=LOOKBACK_HOURS)
    return _to_iso(start), _to_iso(end)


def _start_investigation(entity_arn: str, start: str, end: str) -> str:
    """Detective Investigation を起動。冪等: 同一エンティティ×窓の重複起動を避ける。

    StartInvestigation は GraphArn/EntityArn/ScopeStartTime/ScopeEndTime が必須。
    """
    resp = detective.start_investigation(
        GraphArn=GRAPH_ARN,
        EntityArn=entity_arn,
        ScopeStartTime=start,
        ScopeEndTime=end,
    )
    return resp["InvestigationId"]


def _notify(
    finding_type: str,
    severity: float,
    finding_id: str,
    entity_arn: str | None,
    investigation_id: str,
) -> None:
    region = os.environ.get("AWS_REGION", "ap-northeast-1")
    detective_console = f"https://{region}.console.aws.amazon.com/detective/home?region={region}"
    sns.publish(
        TopicArn=ALERT_TOPIC_ARN,
        Subject=f"[GuardDuty→Detective][{severity}] {finding_type}",
        Message="\n".join(
            [
                f"finding_type: {finding_type}",
                f"severity: {severity}",
                f"finding_id: {finding_id}",
                f"iam_entity: {entity_arn or 'n/a'}",
                f"investigation_id: {investigation_id}",
                f"detective_console: {detective_console}",
            ]
        ),
    )


def _parse_iso(value: str) -> dt.datetime:
    return dt.datetime.fromisoformat(value.replace("Z", "+00:00"))


def _to_iso(value: dt.datetime) -> str:
    # UTC ISO8601（StartInvestigation が期待する形式）。
    return value.astimezone(dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
```

このコードの設計判断を明示します。

- **調査の起動だけ**：`detective:StartInvestigation` と `sns:Publish` しか呼ばない。**インスタンス停止も鍵削除もしない**ので、誤検知で起動しても被害はゼロ（無駄な調査が1本走るだけ）。だから「Critical 全件で自動起動」という広いトリガーでも安全です。
- **IAM エンティティが無ければ通知のみ**：Detective Investigation の対象は IAM ユーザー／ロール。finding が IAM 主体を含まない（純粋なネットワーク系など）場合は、調査を起動せず通知だけして、**人間が2章のピボットで EC2／IP を辿る**ルートに回します。
- **時間窓を finding に紐づける**：`ScopeStartTime`/`ScopeEndTime` を「finding の発生時刻からさかのぼる窓」で組む。固定の「過去7日」より、**攻撃の前後に焦点を当てた窓**のほうがノイズが減ります。
- **純関数を切り出す**：`_extract_iam_entity_arn` と `_scope_window` は副作用なし。サンプル finding の JSON を入力に**ユニットテストで意思決定を網羅**でき、`boto3` を差し替えれば `StartInvestigation` 呼び出しもモックできます（[本編ガイドのテスト容易性](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)と同じ思想）。

> **finding から IAM ARN を取り出す点は要実装注意**：GuardDuty finding の `resource` 構造は finding 型ごとに異なります。`accessKeyDetails` の中身（`accessKeyId`／`principalId`／`userType`）から **IAM ロール／ユーザーの ARN へ解決するロジック**は、対象 finding 型に合わせて実装・テストしてください（principalId からの ARN 解決は環境依存のため、ここは雛形です）。**ここを雑にすると「IAM エンティティが取れず調査が起動しない」**——本番投入前に `create-sample-findings` で対象型を発火させ、ARN 抽出が通ることを必ず検証します。

---

## 6. end-to-end ワークフロー：検知から根本原因・影響範囲、そして対応へ

ここまでの部品を、**1本のインシデント対応フロー**に組み上げます。題材は最悪ケース——Critical の `AttackSequence:IAM/CompromisedCredentials`。

### 6.1 テキストアーキテクチャ図

```text
  [ DETECT ]  GuardDuty
      │   Critical finding: AttackSequence:IAM/CompromisedCredentials (severity 9.x)
      │   （ETD が弱いシグナルを24h窓で相関して束ねた攻撃シーケンス）
      ▼
  ┌─────────────────────────────────────────────────────────────────┐
  │ EventBridge (source=aws.guardduty, severity >= 9)                │
  └───────────┬──────────────────────────────────┬──────────────────┘
              │                                  │
              ▼ ①人間向け                         ▼ ②機械向け
        SNS → Slack/メール              Lambda: 自動調査トリガー
        （即トリアージ開始）              detective:StartInvestigation
                                          （IAM エンティティ × 攻撃前後の窓）
                                                │ InvestigationId
                                                ▼
  [ INVESTIGATE ]  Amazon Detective（行動グラフ・最大1年）
      │
      ├─ ピボット：finding → IAM ロール / EC2 / IP のエンティティプロファイル
      ├─ finding groups：関連 finding ＋全エンティティを1つの security event に
      │                  （MITRE ATT&CK の TTP で連携攻撃を可視化、最大48hで生成）
      └─ Detective Investigation レポート：IOC・impossible travel・リスク評価
      │
      ▼  根本原因（root cause）＋ 影響範囲（blast radius）が確定
  ┌─────────────────────────────────────────────────────────────────┐
  │  人間のトリアージ：正規利用か / どこまで侵害されたか / 何を封じるか  │
  └───────────┬─────────────────────────────────────────────────────┘
              ▼
  [ RESPOND ]  封じ込め・復旧・再発防止
      ├─ 自動修復：EventBridge → 冪等・許可リスト付きの封じ込め
      │           （隔離SG / 鍵失効はチケット化＋承認）
      └─ runbook：オンコール手順 → ポストモーテム
```

### 6.2 各ステップで「誰が何に答えるか」

| ステップ | 道具 | 答える問い | 出力 |
| --- | --- | --- | --- |
| **検知** | GuardDuty（＋ETD） | いま、攻撃の連鎖が起きているか？ | Critical `AttackSequence:*` finding |
| **初動トリアージ** | SNS/Slack 通知 | これは即対応が要るか？ | 担当アサイン・調査開始 |
| **自動調査起動** | Lambda → `StartInvestigation` | この IAM は侵害に関与したか？ | `InvestigationId` |
| **エンティティ調査** | Detective ピボット | このロール／IP／インスタンスの普段は？ いつ逸脱した？ | entity profile・時系列 |
| **群の把握** | finding groups | この攻撃に関わる finding と全エンティティは？ | security event（影響範囲） |
| **IOC 調査** | Detective Investigation レポート | IOC・impossible travel・MITRE TTP は？ | リスクレポート（根本原因の核） |
| **対応** | EventBridge 自動修復 / runbook | どう封じ、どう復旧し、どう防ぐか？ | 封じ込め・ポストモーテム |

### 6.3 対応への橋渡し（このフローの「出口」）

調査で**根本原因と影響範囲が確定したら**、初めて RESPOND に渡します。順序が逆だと証拠を消すことは0章で述べた通りです。

- **封じ込め（自動）**：許可リストに載った高信頼 finding は、冪等・取り消し可能な封じ込め（隔離 SG など）を自動実行。実装は[自動修復・インシデント対応の記事](/blog/aws-guardduty-eventbridge-automated-remediation-incident-response-guide)に Terraform/Python で通してあります。
- **封じ込め（破壊的）**：鍵失効・インスタンス終了は**チケット化＋人間の承認**。Detective の影響範囲が「どの鍵を・どこまで」を教えるので、**過剰な巻き込みを避けられます**。
- **runbook とポストモーテム**：調査レポートをそのまま[インシデント対応 runbook／ポストモーテム](/blog/incident-response-runbook-postmortem-oncall-sre-guide)の根拠に。Detective の時系列・IOC・MITRE マッピングは、ポストモーテムの「タイムライン」と「根本原因」の節をほぼ埋めてくれます。

> **決済基盤での効きどころ**：金銭が動く環境では、「影響範囲を確定する前に止める」と正規取引を巻き込み、「確定が遅い」と被害が拡大します。**Detective を挟むと『止める前に範囲が分かる』**——この一手が、封じ込めの正確さと報告の速さを同時に上げます。

---

## 7. マルチアカウント：Detective 委任管理者を GuardDuty と揃える

アカウントが2つ以上なら、Detective も**組織で集中管理**します。鍵は **「Detective の委任管理者を、GuardDuty の委任管理者と同じアカウントにする」** ことです。

### 7.1 なぜ管理者を揃えるのか

公式は明言します——*"It is recommended that you use the same GuardDuty Administrator account as the administrator account for Detective."* 理由は運用の一貫性です。GuardDuty の委任管理者で finding を集約し、**同じアカウントから Detective の行動グラフで調査する**——管理者がズレると、調査のたびにアカウントを跨ぐ羽目になります。[GuardDuty の委任管理者設計](/blog/aws-guardduty-multi-account-organizations-delegated-administrator-terraform-guide)で security-tooling アカウントを管理者にしたなら、**Detective も同じ security-tooling を管理者に**します。

組織での仕組みはこうです——*"When an account enables Detective, it becomes the administrator account for a behavior graph."* そして *"Your organization management account designates a Detective administrator account for the organization. The Detective administrator account enables organization accounts as member accounts in the organization behavior graph."* 管理アカウントが委任管理者を指名し、委任管理者が組織内アカウントをメンバーとして有効化する——GuardDuty と同じ二段構造です。

### 7.2 Terraform で組む

公式リソースは4つ。**`aws_detective_graph` で行動グラフを作り、委任管理者を指名し、組織の自動有効化を入れます**。

```hcl
# ── ① 委任管理者(security-tooling)アカウントで実行：行動グラフを作る ──
# aws_detective_graph がそのアカウント・リージョンの behavior graph 本体。
# （引数は tags のみ。enable のような引数は無い——作成＝有効化）
resource "aws_detective_graph" "this" {
  tags = { ManagedBy = "terraform", Purpose = "security-investigation" }
}

# ── ② 管理(payer)アカウントで実行：Detective の委任管理者を指名 ──
# GuardDuty の委任管理者と同じ security-tooling アカウントを指定して揃える。
resource "aws_detective_organization_admin_account" "delegate" {
  account_id = var.security_account_id # GuardDuty 委任管理者と同一にする
}

# ── ③ 委任管理者アカウントで実行：組織メンバーの自動有効化 ──
# auto_enable=true で、組織に新規追加されるアカウントを自動でメンバー化する。
resource "aws_detective_organization_configuration" "this" {
  graph_arn   = aws_detective_graph.this.graph_arn
  auto_enable = true
}
```

既存アカウントを個別にメンバー招待する場合は `aws_detective_member` を使います。

```hcl
# 既存の特定アカウントを behavior graph のメンバーとして招待する。
# auto_enable では拾えない既存アカウントを明示的に取り込みたいときに使う。
resource "aws_detective_member" "prod" {
  graph_arn     = aws_detective_graph.this.graph_arn
  account_id    = var.prod_account_id
  email_address = var.prod_account_root_email
  # 招待メール通知を抑止（IaC 運用では不要な通知を減らす）
  disable_email_notification = true
}
```

> **`auto_enable` の落とし穴**：`aws_detective_organization_configuration` の `auto_enable = true` は **「今後追加される」アカウントを自動メンバー化**します。GuardDuty の `auto_enable_organization_members = "ALL"` のような「既存も全部」の一括指定とは挙動が異なるため、**設定時点で既に存在する組織アカウントは `aws_detective_member` で明示的に取り込む**のが安全です。さらに Detective はリージョンサービスなので、**使うリージョンごとに graph・委任管理者・メンバーを揃える**必要があります（2.1 の再掲）。

---

## 8. Security Lake 連携とコスト（要確認）

### 8.1 Security Lake 連携：生ログまで掘る

Detective は行動グラフの「集約された可視化」が主ですが、**生ログ（raw logs）まで掘りたい**ときは **Amazon Security Lake 連携**が効きます。公式いわく *"you can query and retrieve the raw log data stored by Security Lake."* 連携で扱えるのは、Security Lake がネイティブ対応する次のソース：

- **AWS CloudTrail management events**（version 1.0 以降）
- **Amazon VPC Flow Logs**（version 1.0 以降）
- **Amazon EKS Audit Log**（version 2.0）

連携後、Detective は CloudTrail 管理イベントと VPC Flow Logs の生ログを Security Lake から取り込み、**Detective 内で生ログをクエリ**できます。「行動グラフの要約で当たりをつけ、生ログで裏を取る」——調査の解像度が一段上がります。

### 8.2 コスト：30日無料トライアル＋GB あたりの段階フラットレート（要確認）

料金の要点（**改定され得るため本番投入前に[公式の料金ページ](https://aws.amazon.com/detective/pricing/)で必ず確認**）：

| 項目 | 公式の記述 |
| --- | --- |
| **無料トライアル** | 初回有効化時に **30日間の無料トライアル**（組織で有効化される個別アカウントも対象） |
| **課金モデル** | *"a tiered flat rate per GB for all data regardless of the source"*——ソースに依らず取り込みデータ量（GB）に対する**段階的フラットレート** |
| **支払い対象** | *"you pay only for the events analyzed"*——分析したイベント量に対する従量課金。前払い・最低利用なし |
| **追加コスト** | Security Lake 連携・Detective Investigations の利用では、**他サービスとの組み合わせで追加コスト**が発生し得る |

Detective は**使用量の見積もり（estimated usage costs）**をコンソール／API で提供します。トライアル中に本番量での想定請求額を把握できるので、**「全社で常時フル稼働」より「GuardDuty の高重大度 finding を深掘りする道具」として位置づける**のが、費用対効果で勝ちやすい設計です（[比較記事](/blog/aws-guardduty-vs-security-hub-detective-inspector-macie-comparison-guide)のコスト観点と同じ）。

> **正直な注記**：本記事の料金は「GB あたりの段階フラットレート」「30日無料」という**構造**までが公式の記述で、**具体的な単価は地域・時期で変わるため明記しません**。見積もりはトライアルの実測か、料金ページの最新値で行ってください。

---

## まとめ：GuardDuty × Detective 調査ワークフローのチートシート

迷ったときの早見表です。

- **3層で考える**：**GuardDuty=DETECT（検知）→ Detective=INVESTIGATE（調査）→ EventBridge/runbook=RESPOND（対応）**。Detective は検知でも防御でもなく**調査**。順序を崩して「調査前に対応」すると証拠を消す。
- **Detective とは**：CloudTrail 管理イベント・VPC Flow Logs・GuardDuty finding・EKS 監査ログから **ML＋統計＋グラフ理論で行動グラフを構築**し、**最大1年分の履歴**で根本原因と影響範囲を可視化する。自前の ETL もクエリも不要。
- **ピボット**：GuardDuty finding → **[Investigate with Detective]** で該当エンティティ（IAM ロール／EC2／IP 等）へ。前提は**両サービスを同一アカウント・同一リージョンで有効化**。export 頻度は**15分**推奨。
- **finding groups**：関連 finding＋全エンティティを **1つの security event** に束ね、**HIGH/Critical の根本原因分析**に使う。基準は時間的近接・共通エンティティ・パターン・**MITRE ATT&CK の TTP**。完成まで**最大48時間**。
- **Detective Investigation**：**IAM ユーザー／ロールを IOC で調査**。ML＋脅威インテルで重大な問題だけを浮上させ、impossible travel・MITRE ATT&CK・リスクレポートを生成。`StartInvestigation`（必須＝`GraphArn`/`EntityArn`/`ScopeStartTime`/`ScopeEndTime`）／`aws detective start-investigation` で**プログラマティック起動**。
- **自動起動**：**Critical な GuardDuty finding → EventBridge → Lambda → `StartInvestigation`**。**調査の起動のみで破壊的操作なし**だから安全に自動化できる。**冪等・最小権限**（`detective:StartInvestigation`＋`sns:Publish` だけ）。
- **マルチアカウント**：**Detective の委任管理者を GuardDuty の委任管理者と揃える**。Terraform は `aws_detective_graph`（`tags` のみ、`enable` 引数は無い）／`aws_detective_organization_admin_account`／`aws_detective_organization_configuration`（`auto_enable`）／`aws_detective_member`。**リージョンごとに**揃える。
- **Security Lake / コスト**：生ログを掘るなら Security Lake 連携（CloudTrail/VPC Flow/EKS Audit）。料金は **30日無料＋GB あたりの段階フラットレート（具体単価は要確認）**。常時フル稼働より「高重大度を深掘りする道具」として配置。

GuardDuty は「赤いダッシュボード」を作ります。Detective は、その赤の**「なぜ・どこまで」を埋めます**。検知を**点で終わらせず、根本原因と影響範囲という線**にして、初めて「報告できる・封じ込められる・再発を防げる」インシデント対応になります。最大のレバレッジは検知でも対応でもなく、**その間にある調査を、いかに速く・正確に・自動で立ち上げるか**にあります。

私はマルチアカウントの[サーバーレス決済プラットフォーム](/case-studies/payment-platform-reliability)で、**実際の金銭・カーボンクレジット・地域通貨を扱う基盤の IAM・可観測性・DR を横断実装**し、「正しさ」を運用の注意深さではなく**コードの構造と冪等性**で担保してきました。GuardDuty × Detective の調査フローも同じ思想で設計します——**①GuardDuty と Detective を組織の同一委任管理者で揃え、②finding からのピボット・finding groups・Investigation で根本原因と影響範囲を出し、③Critical finding を EventBridge で受けて調査を自動起動（冪等・最小権限・破壊的操作なし）、④確定した範囲を封じ込めと runbook に橋渡しする**。Detective を named project で運用したと主張するつもりはありません。ですが、**この detect→investigate→respond の一気通貫を、あなたの AWS 環境向けに設計・実装することはできます**——マルチアカウント AWS のインシデント対応・可観測性・DR を横断してきた実体験を土台に。

**「GuardDuty は入れたが、Critical が出たあとの『何が起きて、どこまで侵されたか』に即答できない」——その調査レイヤーを、ピボット・finding groups・Investigation の自動起動・組織統制・コスト設計まで、一人 ×生成AI（Claude Code）で速く・安全に伴走できます。** 要件の整理段階からでも、お気軽にご相談ください。

---

### 参考（公式ドキュメント）

- [Integrating with Amazon Detective（GuardDuty UG）](https://docs.aws.amazon.com/guardduty/latest/ug/detective-integration.html) — 両サービス有効化で自動統合・ピボット先エンティティ・15分 export・委任管理者を揃える推奨・リージョンサービス
- [What is Amazon Detective?](https://docs.aws.amazon.com/detective/latest/userguide/what-is-detective.html) — 根本原因の調査・ML/統計/グラフ理論・行動グラフ・最大1年の履歴・組織統合・30日無料・GB あたり段階フラットレート・Security Lake 連携
- [Analyzing finding groups（Detective UG）](https://docs.aws.amazon.com/detective/latest/userguide/groups-about.html) — 関連 finding を1つの security event に束ねる・4基準（時間/共通エンティティ/パターン/MITRE ATT&CK TTP）・48時間で生成
- [Detective Investigation（Detective UG）](https://docs.aws.amazon.com/detective/latest/userguide/investigations-about.html) — IAM ユーザー/ロールを IOC で調査・ML＋脅威インテル・impossible travel・MITRE・レポート生成
- [StartInvestigation（Detective API Reference）](https://docs.aws.amazon.com/detective/latest/APIReference/API_StartInvestigation.html) — 必須パラメータ（GraphArn/EntityArn/ScopeStartTime/ScopeEndTime）・InvestigationId
- [aws_detective_graph（Terraform AWS Provider）](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/detective_graph) — 行動グラフの作成（引数は tags のみ、graph_arn を出力）。関連: `aws_detective_organization_admin_account` / `aws_detective_organization_configuration` / `aws_detective_member`
- [Amazon Detective pricing](https://aws.amazon.com/detective/pricing/) — 段階フラットレート・30日無料トライアル・分析イベント量への従量課金（最新単価は要確認）
