# GuardDuty × Amazon Detective: The 'Next Step' After Detection—A Workflow to Investigate Root Cause and Blast Radius

> GuardDuty raised a Critical—but can you answer 'what happened, and how far did it spread'? Amazon Detective builds a behavior graph from CloudTrail, VPC Flow Logs, GuardDuty findings, and EKS audit logs over up to a year, and visualizes root cause and blast radius. From pivoting off a finding, finding groups, Detective Investigation using IOCs, automated StartInvestigation via CLI/EventBridge, to aligning the organization's delegated administrators—we design the detect→investigate→respond investigation layer in real code based on the official documentation (June 2026).

- Published: 2026-06-27
- Author: 友田 陽大
- Tags: セキュリティ, AWS, GuardDuty, Amazon Detective, インシデント対応
- URL: https://tomodahinata.com/en/blog/aws-guardduty-amazon-detective-investigation-root-cause-workflow-guide
- Category: Amazon GuardDuty in production
- Pillar guide: https://tomodahinata.com/en/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide

## Key points

- GuardDuty only detects 'a point / a chain.' What answers 'why did it happen and how far was it breached (root cause and blast radius)' is Amazon Detective—the two are sequential, not competing
- Detective builds a 'behavior graph' from CloudTrail management events, VPC Flow Logs, GuardDuty findings, and EKS audit logs using ML + statistics + graph theory, and visualizes up to a year of history
- You can pivot from a GuardDuty finding via 'Investigate with Detective' (assuming both services are enabled in the same account, same region). Finding groups bundle related findings and entities into a single security event
- Detective Investigation investigates IAM users/roles via IOCs and structures them with MITRE ATT&CK. You can auto-start it via the StartInvestigation API / CLI, and wire receiving Critical GuardDuty findings via EventBridge through to auto-investigation→notification (idempotent, least privilege)
- For multi-account, align Detective's delegated administrator with GuardDuty's. Build the Terraform with aws_detective_graph / aws_detective_organization_admin_account / aws_detective_organization_configuration / aws_detective_member

---

GuardDuty raised a Critical. Slack lights up red. So—**"what happened, and how far were we breached?"** Can you answer that right now?

You can read off the finding name in the console: "It's `AttackSequence:IAM/CompromisedCredentials`." But what the client really wants to know is what comes after. **When, which credential, from which IP, touched which resources, and how far it spread.** What burns time on the incident-response front line is not "detection" but this **"pinning down the root cause and the blast radius."** `grep`ing CloudTrail, wrangling VPC Flow Logs in Athena, re-ordering IAM event history into a timeline—those several hours delay the report and the containment decision.

This article is the **"next step after detection" workflow** to kill those several hours. The subject is **Amazon GuardDuty × Amazon Detective.** After GuardDuty **detects a point (or a chain)**, you **investigate root cause and blast radius with Amazon Detective**—we run that plumbing in real Terraform / bash / Python code based on the official documentation (as of June 2026).

GuardDuty's own production design (selecting protection plans, organization-wide enablement, EventBridge automated response) is covered in the [main guide](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide), and the division of roles across five AWS security services is covered in the [comparison article](/blog/aws-guardduty-vs-security-hub-detective-inspector-macie-comparison-guide). This article digs only into **the layer in between—"investigation."** As source material, I'll weave in my experience implementing IAM, observability, and DR across a [serverless payment platform](/case-studies/payment-platform-reliability) on multi-account AWS—**because it handled real money, carbon credits, and regional currencies, I needed to guarantee by mechanism the path of "when an anomaly is detected, pin down the impact scope within the time budget and report."**

> **The rules of this article**: Detective's definitions, data sources, finding groups / Investigation specs, and pricing model are based on the **AWS official documentation (as of June 2026)**. Supported data sources, features, and pricing get revised, so always confirm the latest official info before going to production. And the big premise—**Detective is neither "detection" nor "defense" but an "investigation" service.** Finding new threats is GuardDuty's job; stopping an attack is the job of a WAF and least-privilege IAM. What Detective answers is only "the cause and scope of a detected threat." The automation code (starting an auto-investigation) is limited to **idempotent, least-privilege, and non-destructive** ones only.

---

## 0. Mental model: the 3 layers of detect → investigate → respond

Before starting the design, fix security operations with **three verbs.** This is the foundation for everything in this article.

> **GuardDuty DETECTs, Detective INVESTIGATEs, and automated response RESPONDs.** These three layers are a sequence, not a competition.

| Layer | Verb | Service | Question it answers |
| --- | --- | --- | --- |
| **DETECT** | detect | **GuardDuty** | "Is something bad happening right now?" (a point / an attack sequence) |
| **INVESTIGATE** | investigate | **Amazon Detective** | "Why did it happen, and how far were we breached?" (root cause, blast radius) |
| **RESPOND** | respond | **EventBridge → automated response / runbook** | "How do we contain, recover, and prevent recurrence?" |

Many "we installed GuardDuty" projects **stop at DETECT.** Findings come out, they fly to Slack, but **how did that `i-0abc...` instance or that `AROAEXAMPLE...` role behave in the past, what did it touch, and which IP did it communicate with**—no one can answer immediately. So the containment decision and the report to affected customers both get delayed.

Three consequences follow from this.

1. **DETECT and INVESTIGATE are separate layers, handled by separate tools.** GuardDuty emits a finding saying "there is anomalous behavior," but that finding alone **has no context and no history.** Detective **automatically aggregates CloudTrail, VPC Flow Logs, GuardDuty findings, and EKS audit logs to build a "behavior graph,"** and over **up to a year of history** shows "how this entity normally behaves and when it deviated." Detective is what **turns points into lines and lines into context.**
2. **Skipping INVESTIGATE and going to RESPOND causes accidents.** Terminating an instance or deleting a key before pinning the blast radius **erases forensic evidence, drags in legitimate usage, and permanently loses the root cause.** The order "investigate → pin scope → contain" cannot be broken, **especially in an environment where money moves** like a payment foundation.
3. **The 3 layers can be wired as one pipeline.** GuardDuty finding → EventBridge → auto-start a Detective Investigation → notify the result → a human triages → [automated remediation / runbook](/blog/aws-guardduty-eventbridge-automated-remediation-incident-response-guide). The climax of this article is **Section 5 (auto-starting StartInvestigation)** and **Section 6 (the end-to-end workflow).**

Once you grasp these 3 layers, the task of "installing Detective" turns out to be three designs: **① link it with GuardDuty to create the entry point for investigation → ② produce root cause and scope with finding groups and Investigation → ③ bridge that to automated response and runbooks.** Let's build them in order.

---

## 1. What Amazon Detective is: a device that reconstructs "context" with a behavior graph

First, fix in one line what Detective is and isn't.

> **Amazon Detective = an investigation service that automatically collects CloudTrail, VPC Flow Logs, GuardDuty findings, and EKS audit logs, builds a "behavior graph" with ML, statistical analysis, and graph theory, and lets you quickly identify the root cause of security findings or suspicious activities.**

The official definition reads: *"Amazon Detective helps you analyze, investigate, and quickly identify the root cause of security findings or suspicious activities."* And: *"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 What it's looking at: the behavior graph's source data

Detective's heart is the **behavior graph**—"*a linked set of extracted and analyzed data* extracted and analyzed from one or more AWS accounts." The sources are these four.

| Source data | What it extracts | What it lets you answer |
| --- | --- | --- |
| **CloudTrail management events** | Login attempts, API calls (control-plane operations) | "Which APIs does this IAM role normally call? Is this call anomalous?" |
| **VPC Flow Logs** | EC2 / Kubernetes pod IP traffic volume and peers | "Is this traffic spike from this instance within expectations? Which IP did it talk to?" |
| **GuardDuty findings** | The detected threat itself (ingests all finding types) | "Which other findings does the entity involved in this finding appear in?" |
| **EKS audit logs** | Kubernetes control-plane operations | "Which secrets / APIs did the compromised pod touch?" |

What's decisive is that **you don't have to "collect and join these yourself."** As the official docs put it, *"With Detective, you don't have to organize any data or develop, configure, or tune your own queries and algorithms."* No Athena queries, no ETL joining CloudTrail and Flow Logs. Detective **auto-extracts, auto-joins, and auto-visualizes.**

### 1.2 Up to a year of history: "when did it deviate" becomes visible

The other core is the **time axis.** The official docs state clearly: *"you can access up to a year of historical event data."* This is decisive for seeing "deviation from a baseline." Detective's visualizations answer questions like *"Is this an unusual API call for this role?"* and *"Is this spike in traffic from this instance expected?"* **by comparing against past baselines.**

> **Design implication**: a GuardDuty finding is a "now" snapshot. Detective has "until now." **"This credential started being called from a region it never uses, three days ago"**—whether you can produce that one sentence is what splits incident-response time.

### 1.3 What Detective "doesn't do"

To prevent confusion, make Detective's boundaries explicit.

- **It doesn't detect new threats.** That's the job of GuardDuty (and Inspector, Macie). Detective only investigates "detected threats."
- **It doesn't stop attacks.** Containment is EventBridge → automated response or a manual runbook. Detective is a read-centric analysis device.
- **It doesn't aggregate / normalize findings.** Normalization into ASFF and the single pane of glass are Security Hub's role (see the [comparison article](/blog/aws-guardduty-vs-security-hub-detective-inspector-macie-comparison-guide)).

**GuardDuty plots the points, Detective draws the lines, Security Hub lays them on the desk, and EventBridge moves the hands**—because the roles are unique, you don't get lost in design.

---

## 2. Pivot: from a GuardDuty finding to Detective

The entry point to investigation is the "pivot." Select a finding in the GuardDuty console and **jump straight to the corresponding entity in Detective.** This is the shortest way to start an investigation.

### 2.1 Prerequisite: enable both services in the same account, same region

The official procedure is clear: *"To use Amazon Detective with GuardDuty you must first enable Amazon Detective."* And: *"When you enable both GuardDuty and Detective, the integration is enabled automatically. Once enabled, Detective will immediately ingest your GuardDuty findings data."*

In other words, **no special wiring is needed for the integration.** Enable both and Detective immediately starts ingesting GuardDuty findings. But two constraints:

- **Detective is a regional service**: *"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."* You need to enable Detective in each region where you've enabled GuardDuty.
- **Set export frequency to 15 minutes**: the official docs recommend *"it is recommended that you change the export frequency to 15 minutes."* With the default of 6 hours, Detective is slow to receive finding updates. This is the same reason I recommended `finding_publishing_frequency = "FIFTEEN_MINUTES"` in the [main guide](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide).

### 2.2 The pivot operation

The flow in the console is this.

```text
GuardDuty console
  │  1. Select one finding from the findings table
  │  2. Click [Investigate with Detective] in the detail pane
  │  3. Choose the "aspect" to investigate
  ▼
Detective console (the relevant finding/entity profile opens)
```

What matters here is that **you can pivot to multiple entities depending on the finding type.** The pivot targets the official docs list are: *"AWS account, IAM role, user, or role session, user agent, federated user, Amazon EC2 instance, or IP address."* For a credential-leak-type finding, you jump to "that IAM role"; for a network-type one, to "that EC2 instance" or "the peer IP," and you get a panoramic view of each **entity profile**—normal behavior, related findings, communication peers.

> **An operational tip**: *"If you archive a GuardDuty finding in the Detective console, that finding gets archived in the GuardDuty console as well."* Archiving on the Detective side also archives it on the GuardDuty side in tandem. When you judge during investigation that "this is confirmed legitimate usage," closing it on one side closes it on both—keeping triage consistent.

---

## 3. Finding groups: bundle related findings into "one security event"

If a pivot is the entry to "deep-diving one finding," then **finding groups** is the higher-level view that "**bundles scattered findings into one incident.**" This is **the protagonist of root-cause analysis for HIGH / Critical GuardDuty findings.**

### 3.1 Why look at findings as a "group"

Attackers don't stop at one operation. To borrow the official wording: *"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."*

The problem is that **looking at these in isolation leads you to misread their meaning.** *"When security findings are investigated in isolation, it can lead to a misinterpretation of their significance, and difficulty in finding the root cause."* "Port scan," "failed login," "unfamiliar API call"—things that look like noise individually become one attack story when **connected by the same IAM role session and the same IP.**

Detective solves this with **graph analysis**—*"applying a graph analysis technique that infers relationships between findings and entities, and groups them together."* And the official recommendation is clear: *"We recommend treating finding groups as the starting point for investigating the involved entities and findings."* **Make the finding group the starting point of investigation.**

### 3.2 The 4 grouping criteria

How a finding group is built—the official docs list four criteria.

| Criterion | Content |
| --- | --- |
| **Temporal Proximity** | Findings that occur in nearby time windows are likely the same incident and are grouped |
| **Common Entities** | Group findings involving the same IP / user / resource to grasp the blast radius across the environment |
| **Patterns and Behaviors** | Infer relationships from similar attack types / suspicious-activity patterns |
| **TTPs (tactics, techniques, procedures)** | Group findings sharing common TTPs in a framework like **MITRE ATT&CK** to surface coordinated attacks |

Each group contains not only findings but the **entities** involved—including AWS-external IP addresses and user agents. Detective provides an **interactive visualization** of each group, making it easy to trace the connections between entities and findings and the root cause.

### 3.3 A timing caveat

A sentence that bites in real operations: *"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."*

In other words, **a finding group can take up to 48 hours to complete.** For the first GuardDuty finding (especially a Critical `AttackSequence:*`), a two-stage approach is realistic: **"first triage in isolation → re-evaluate the whole picture once the finding group has grown."** Don't jump to "there are no related findings because there's no group yet."

> **Relationship with GuardDuty Extended Threat Detection**: GuardDuty's ETD is the detection layer that "correlates weak signals over a 24-hour window and bundles them into `AttackSequence:*` (always Critical)" (see the [attack-sequence article](/blog/aws-guardduty-extended-threat-detection-attack-sequence-findings-guide)). Detective's finding groups is the investigation layer that "**visualizes and investigates multiple detected findings as a group on the behavior graph.**" **If ETD 'detects an attack sequence,' finding groups 'solves that sequence's root cause and all entities on a graph'**—the former is GuardDuty, the latter Detective, with roles divided.

---

## 4. Detective Investigation: investigate IAM with IOCs and structure it with MITRE ATT&CK

If finding groups is "group visualization," then **Detective Investigation** is the auto-investigation feature that "**mechanically investigates a specific IAM entity by indicators of compromise (IOCs) and reports.**" It connects directly to automating triage.

### 4.1 What it investigates: IAM users / roles × IOCs

The official definition: *"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."*

An **IOC (indicator of compromise)** is *"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."* A trace that indicates "malicious activity / an incident" with high confidence.

Detective Investigation uses **ML models and threat intelligence** *"to surface only the most critical, suspicious issues."* What it investigates, per the official docs, is *"attack tactics, impossible travel, flagged IP addresses, and finding groups"*—attack tactics, geographically impossible travel, flagged IPs, and finding groups. And it **runs initial investigation steps and generates a report highlighting the risks** (*"performs initial security investigation steps and generates a report highlighting the risks identified by Detective"*).

> **Why IAM-centric**: most cloud breaches start from "abuse of leaked credentials." Detective Investigation focuses on **IAM users / roles** because this is where attacks tend to originate. When a GuardDuty finding like `AttackSequence:IAM/CompromisedCredentials` comes up, running an Investigation against that IAM entity is the natural flow.

### 4.2 How to start it: console / API / CLI

Detective Investigation can be started in three ways.

1. **Console**: run manually from the Detective console.
2. **API**: the `StartInvestigation` operation (programmatic).
3. **CLI**: `aws detective start-investigation`.

The **required parameters** of `StartInvestigation` can be confirmed in the official API reference (all Required).

| Parameter | Type | Content |
| --- | --- | --- |
| **`GraphArn`** | String | The behavior graph's ARN (`arn:aws:detective:<region>:<account>:graph:<id>`) |
| **`EntityArn`** | String | The **ARN of the IAM user / role** to investigate (starts with `arn:`) |
| **`ScopeStartTime`** | Timestamp | The investigation's start datetime (UTC ISO8601, e.g. `2026-06-25T00:00:00Z`) |
| **`ScopeEndTime`** | Timestamp | The investigation's end datetime (UTC ISO8601) |

The response is **`InvestigationId`** (a fixed-length 21-digit numeric string). You reference the report later with this.

> **Caveat**: `EntityArn` is the **ARN of an IAM user / role**, not an EC2 instance ID or bucket name (the API pattern is `^arn:.*`, intended for IAM). So Investigation is a tool that asks "**did this IAM entity participate in a breach in this time window?**" Trace EC2/IP/S3 context with the Section 2 pivot or the Section 3 finding groups.

### 4.3 Start a single investigation via CLI

First run it once by hand to grasp the behavior. Obtain `GraphArn` in advance.

```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 で取得。
```

With the returned `InvestigationId`, fetch and review the report (risks, IOCs, MITRE ATT&CK mapping). Instead of "a human looking at a finding and running this by hand," **the next chapter runs it automatically the moment a Critical GuardDuty finding appears.**

---

## 5. Auto-start: Critical GuardDuty finding → Detective Investigation (EventBridge)

This is the climax of the article. **We decouple the initial investigation from human reaction speed.** When GuardDuty raises a Critical (especially `AttackSequence:*`), **EventBridge immediately starts a Lambda, the Lambda auto-starts a Detective Investigation against the relevant IAM entity**, and notifies with the `InvestigationId` attached.

### 5.1 Design principles (the same constraints as automated response)

We impose the same principles as the [main guide's automated response](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide) on auto-starting an investigation.

- **Idempotent**: EventBridge is **at-least-once delivery.** The same finding can start the Lambda twice, so **suppress double investigation of the same entity × same time window.**
- **Scope it down (least-privilege)**: the Lambda's execution role is limited to `detective:StartInvestigation` and minimal read / notification. Containment permissions (stopping EC2, deleting IAM keys) are **given none of**—this is starting an investigation, not responding.
- **Non-destructive**: all it does is "start an investigation + notify." It **stops nothing and deletes nothing.** So it's safe even when automated. RESPOND (containment) happens after human triage, in a separate, allowlisted pipeline.

### 5.2 EventBridge rule (Terraform)

Pick up only Critical GuardDuty findings that involve an IAM entity.

```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 Auto-investigation Lambda (Python)

It **extracts the IAM entity's ARN** from the GuardDuty finding, calls `StartInvestigation` over a time window containing the finding's occurrence time, and notifies the `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")
```

Let me make this code's design decisions explicit.

- **Starting an investigation only**: it calls nothing but `detective:StartInvestigation` and `sns:Publish`. It **neither stops instances nor deletes keys**, so even a false-positive start does zero harm (just one wasted investigation runs). That's why even a broad trigger of "auto-start on every Critical" is safe.
- **Notify only if there's no IAM entity**: Detective Investigation's target is an IAM user / role. If a finding contains no IAM principal (a pure network type, etc.), it doesn't start an investigation, just notifies, and routes to **a human tracing EC2 / IP via the Section 2 pivot.**
- **Tie the time window to the finding**: build `ScopeStartTime`/`ScopeEndTime` as "a window going back from the finding's occurrence time." A window **focused on around the attack** has less noise than a fixed "past 7 days."
- **Carve out pure functions**: `_extract_iam_entity_arn` and `_scope_window` are side-effect-free. With a sample finding JSON as input you can **cover the decisions exhaustively in unit tests**, and swapping out `boto3` lets you mock the `StartInvestigation` call too (the same thinking as the [main guide's testability](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)).

> **Extracting the IAM ARN from the finding requires implementation care**: a GuardDuty finding's `resource` structure differs per finding type. Implement and test **the logic to resolve from `accessKeyDetails` contents (`accessKeyId` / `principalId` / `userType`) to the IAM role / user's ARN** to match the target finding types (resolving an ARN from a principalId is environment-dependent, so this is a template). **If you do this sloppily, "the IAM entity can't be obtained and the investigation doesn't start."** Before going to production, fire the target types with `create-sample-findings` and always verify the ARN extraction passes.

---

## 6. End-to-end workflow: from detection to root cause and blast radius, then response

We assemble the parts so far into **one incident-response flow.** The subject is the worst case—a Critical `AttackSequence:IAM/CompromisedCredentials`.

### 6.1 Text architecture diagram

```text
  [ DETECT ]  GuardDuty
      │   Critical finding: AttackSequence:IAM/CompromisedCredentials (severity 9.x)
      │   (ETD correlated weak signals over a 24h window into an attack sequence)
      ▼
  ┌─────────────────────────────────────────────────────────────────┐
  │ EventBridge (source=aws.guardduty, severity >= 9)                │
  └───────────┬──────────────────────────────────┬──────────────────┘
              │                                  │
              ▼ ① for humans                      ▼ ② for machines
        SNS → Slack/email               Lambda: auto-investigation trigger
        (start triage immediately)       detective:StartInvestigation
                                          (IAM entity × window around the attack)
                                                │ InvestigationId
                                                ▼
  [ INVESTIGATE ]  Amazon Detective (behavior graph, up to 1 year)
      │
      ├─ Pivot: finding → IAM role / EC2 / IP entity profile
      ├─ Finding groups: related findings + all entities into one security event
      │                  (visualize coordinated attacks with MITRE ATT&CK TTPs, generated within 48h)
      └─ Detective Investigation report: IOCs, impossible travel, risk assessment
      │
      ▼  root cause + blast radius pinned down
  ┌─────────────────────────────────────────────────────────────────┐
  │  Human triage: legitimate use? / how far breached? / what to contain  │
  └───────────┬─────────────────────────────────────────────────────┘
              ▼
  [ RESPOND ]  containment, recovery, recurrence prevention
      ├─ Automated remediation: EventBridge → idempotent, allowlisted containment
      │           (isolation SG / key revocation are ticketed + approved)
      └─ runbook: on-call procedure → postmortem
```

### 6.2 "Who answers what" at each step

| Step | Tool | Question it answers | Output |
| --- | --- | --- | --- |
| **Detect** | GuardDuty (+ETD) | Is an attack chain happening right now? | Critical `AttackSequence:*` finding |
| **Initial triage** | SNS/Slack notification | Does this need immediate response? | Assignment, investigation started |
| **Auto-start investigation** | Lambda → `StartInvestigation` | Did this IAM participate in the breach? | `InvestigationId` |
| **Entity investigation** | Detective pivot | What's normal for this role / IP / instance? When did it deviate? | Entity profile, timeline |
| **Grasp the group** | finding groups | Which findings and all entities are involved in this attack? | Security event (blast radius) |
| **IOC investigation** | Detective Investigation report | What are the IOCs, impossible travel, MITRE TTPs? | Risk report (core of root cause) |
| **Response** | EventBridge auto-remediation / runbook | How to contain, recover, prevent? | Containment, postmortem |

### 6.3 Bridge to response (this flow's "exit")

Once investigation **pins down root cause and blast radius**, you hand it to RESPOND for the first time. Reversing the order erases evidence, as stated in Section 0.

- **Containment (automated)**: high-confidence findings on an allowlist auto-run idempotent, reversible containment (an isolation SG, etc.). The implementation is run through in Terraform/Python in the [automated-remediation / incident-response article](/blog/aws-guardduty-eventbridge-automated-remediation-incident-response-guide).
- **Containment (destructive)**: key revocation and instance termination are **ticketed + human-approved.** Because Detective's blast radius tells you "which key, and how far," you **avoid over-dragging in collateral.**
- **Runbook and postmortem**: use the investigation report directly as the basis of the [incident-response runbook / postmortem](/blog/incident-response-runbook-postmortem-oncall-sre-guide). Detective's timeline, IOCs, and MITRE mapping nearly fill out the "timeline" and "root cause" sections of the postmortem.

> **Where it bites on a payment foundation**: in an environment where money moves, "stopping before pinning the blast radius" drags in legitimate transactions, and "slow pinning" expands the damage. **Inserting Detective means 'you know the scope before you stop'**—this one move raises both the accuracy of containment and the speed of reporting at once.

---

## 7. Multi-account: align Detective's delegated administrator with GuardDuty

If you have two or more accounts, manage Detective **centrally with the organization** too. The key is **"making Detective's delegated administrator the same account as GuardDuty's delegated administrator."**

### 7.1 Why align the administrators

The official docs state clearly: *"It is recommended that you use the same GuardDuty Administrator account as the administrator account for Detective."* The reason is operational consistency. You aggregate findings in GuardDuty's delegated administrator and **investigate with Detective's behavior graph from the same account**—if the administrators are misaligned, you end up crossing accounts every time you investigate. If you made the security-tooling account the administrator in the [GuardDuty delegated-administrator design](/blog/aws-guardduty-multi-account-organizations-delegated-administrator-terraform-guide), then **make the same security-tooling account Detective's administrator too.**

The mechanism in an organization is this: *"When an account enables Detective, it becomes the administrator account for a behavior graph."* And: *"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."* The management account designates the delegated administrator, and the delegated administrator enables organization accounts as members—the same two-tier structure as GuardDuty.

### 7.2 Build it with Terraform

There are four official resources. **Create the behavior graph with `aws_detective_graph`, designate the delegated administrator, and enable organization auto-enable.**

```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
}
```

To individually invite existing accounts as members, use `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
}
```

> **The `auto_enable` pitfall**: `aws_detective_organization_configuration`'s `auto_enable = true` auto-enrolls **"accounts added going forward."** It behaves differently from GuardDuty's "all existing too" bulk specification like `auto_enable_organization_members = "ALL"`, so it's safer to **explicitly enroll organization accounts that already exist at configuration time with `aws_detective_member`.** Furthermore, since Detective is a regional service, you need to **align the graph, delegated administrator, and members per region you use** (a reprise of 2.1).

---

## 8. Security Lake integration and cost (verify)

### 8.1 Security Lake integration: dig down to raw logs

Detective's main value is the behavior graph's "aggregated visualization," but when you want to **dig down to the raw logs**, the **Amazon Security Lake integration** is effective. As the official docs say, *"you can query and retrieve the raw log data stored by Security Lake."* The sources you can handle via the integration are the ones Security Lake natively supports:

- **AWS CloudTrail management events** (version 1.0 and later)
- **Amazon VPC Flow Logs** (version 1.0 and later)
- **Amazon EKS Audit Log** (version 2.0)

After integrating, Detective ingests CloudTrail management events' and VPC Flow Logs' raw logs from Security Lake, and you can **query the raw logs within Detective.** "Get your bearings with the behavior graph's summary, then back it up with the raw logs"—the resolution of investigation rises a level.

### 8.2 Cost: a 30-day free trial + a per-GB tiered flat rate (verify)

The key pricing points (**always confirm on the [official pricing page](https://aws.amazon.com/detective/pricing/) before going to production, as they can be revised**):

| Item | Official description |
| --- | --- |
| **Free trial** | A **30-day free trial** at first enablement (also applies to individual accounts enabled in an organization) |
| **Pricing model** | *"a tiered flat rate per GB for all data regardless of the source"*—a **tiered flat rate** on ingested data volume (GB), regardless of source |
| **What you pay for** | *"you pay only for the events analyzed"*—usage-based on the volume of events analyzed. No upfront, no minimum |
| **Additional cost** | Using the Security Lake integration / Detective Investigations may incur **additional cost in combination with other services** |

Detective provides **estimated usage costs** in the console / API. During the trial you can grasp the expected bill at production volume, so a design that **positions it as "a tool to deep-dive GuardDuty's high-severity findings" rather than "always full-on org-wide"** tends to win on cost-effectiveness (the same cost perspective as the [comparison article](/blog/aws-guardduty-vs-security-hub-detective-inspector-macie-comparison-guide)).

> **Honest note**: this article's pricing is official up to the **structure** ("per-GB tiered flat rate," "30 days free"), but **I don't state specific unit prices because they vary by region and time.** Do the estimate from the trial's actuals or the latest values on the pricing page.

---

## Summary: a GuardDuty × Detective investigation-workflow cheat sheet

A quick reference for when you're unsure.

- **Think in 3 layers**: **GuardDuty = DETECT → Detective = INVESTIGATE → EventBridge/runbook = RESPOND.** Detective is neither detection nor defense but **investigation.** Breaking the order to "respond before investigating" erases evidence.
- **What Detective is**: it **builds a behavior graph with ML + statistics + graph theory** from CloudTrail management events, VPC Flow Logs, GuardDuty findings, and EKS audit logs, and visualizes root cause and blast radius over **up to a year of history.** No ETL or queries of your own.
- **Pivot**: from a GuardDuty finding to the relevant entity (IAM role / EC2 / IP, etc.) via **[Investigate with Detective].** The prerequisite is **both services enabled in the same account, same region.** Export frequency recommended at **15 minutes.**
- **Finding groups**: bundle related findings + all entities into **one security event**, used for **root-cause analysis of HIGH/Critical.** Criteria are temporal proximity, common entities, patterns, and **MITRE ATT&CK TTPs.** Up to **48 hours** to complete.
- **Detective Investigation**: **investigate IAM users / roles by IOCs.** Surface only the critical issues with ML + threat intel, and generate impossible-travel, MITRE ATT&CK, and risk reports. **Programmatic start** via `StartInvestigation` (required = `GraphArn`/`EntityArn`/`ScopeStartTime`/`ScopeEndTime`) / `aws detective start-investigation`.
- **Auto-start**: **Critical GuardDuty finding → EventBridge → Lambda → `StartInvestigation`.** Safe to automate because it's **start-investigation only with no destructive operations.** **Idempotent, least-privilege** (`detective:StartInvestigation` + `sns:Publish` only).
- **Multi-account**: **align Detective's delegated administrator with GuardDuty's.** The Terraform is `aws_detective_graph` (`tags` only, no `enable` argument) / `aws_detective_organization_admin_account` / `aws_detective_organization_configuration` (`auto_enable`) / `aws_detective_member`. Align **per region.**
- **Security Lake / cost**: to dig raw logs, the Security Lake integration (CloudTrail/VPC Flow/EKS Audit). Pricing is **30 days free + a per-GB tiered flat rate (verify specific unit prices).** Position it as "a tool to deep-dive high-severity" rather than always full-on.

GuardDuty makes "a red dashboard." Detective **fills in the "why and how far"** of that red. Only by turning detection into not a terminal point but a **line of root cause and blast radius** do you get incident response that can "report, contain, and prevent recurrence." The greatest leverage is neither detection nor response but **how fast, accurately, and automatically you stand up the investigation in between.**

On the multi-account [serverless payment platform](/case-studies/payment-platform-reliability), I **implemented IAM, observability, and DR across a foundation handling real money, carbon credits, and regional currencies**, and guaranteed "correctness" not by operational carefulness but by **the structure and idempotency of code.** I design the GuardDuty × Detective investigation flow with the same philosophy—**① align GuardDuty and Detective under the same organizational delegated administrator, ② produce root cause and blast radius via pivot, finding groups, and Investigation, ③ receive Critical findings via EventBridge and auto-start investigation (idempotent, least-privilege, non-destructive), and ④ bridge the pinned scope to containment and runbooks.** I won't claim I operated Detective on a named project. But I **can design and implement this detect→investigate→respond pipeline end-to-end for your AWS environment**—grounded in the real experience of working across incident response, observability, and DR on multi-account AWS.

**"We installed GuardDuty, but can't immediately answer 'what happened and how far we were breached' after a Critical"—I can accompany you on that investigation layer, from pivot, finding groups, and auto-starting Investigation, to organizational governance and cost design, fast and safely with one person × generative AI (Claude Code).** Feel free to reach out, even from the requirements-organizing stage.

---

### References (official documentation)

- [Integrating with Amazon Detective (GuardDuty UG)](https://docs.aws.amazon.com/guardduty/latest/ug/detective-integration.html) — automatic integration on enabling both services, pivot-target entities, 15-minute export, the recommendation to align delegated administrators, regional service
- [What is Amazon Detective?](https://docs.aws.amazon.com/detective/latest/userguide/what-is-detective.html) — root-cause investigation, ML/statistics/graph theory, behavior graph, up to a year of history, organization integration, 30 days free, per-GB tiered flat rate, Security Lake integration
- [Analyzing finding groups (Detective UG)](https://docs.aws.amazon.com/detective/latest/userguide/groups-about.html) — bundling related findings into one security event, 4 criteria (temporal / common entities / patterns / MITRE ATT&CK TTP), generated within 48 hours
- [Detective Investigation (Detective UG)](https://docs.aws.amazon.com/detective/latest/userguide/investigations-about.html) — investigate IAM users/roles by IOCs, ML + threat intel, impossible travel, MITRE, report generation
- [StartInvestigation (Detective API Reference)](https://docs.aws.amazon.com/detective/latest/APIReference/API_StartInvestigation.html) — required parameters (GraphArn/EntityArn/ScopeStartTime/ScopeEndTime), InvestigationId
- [aws_detective_graph (Terraform AWS Provider)](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/detective_graph) — create the behavior graph (`tags` only argument, outputs graph_arn). Related: `aws_detective_organization_admin_account` / `aws_detective_organization_configuration` / `aws_detective_member`
- [Amazon Detective pricing](https://aws.amazon.com/detective/pricing/) — tiered flat rate, 30-day free trial, usage-based on analyzed event volume (verify the latest unit prices)
