「GuardDuty、本番アカウントでは有効にしたんですけど……ステージングと監査用アカウントは、まだなんですよね」——マルチアカウントの AWS を運用しているチームと話すと、ほぼ必ずこの台詞が出ます。
アカウントが増えるほど、GuardDuty の「入れ忘れ」が静かに増えます。新しくアカウントを切るたびに、誰かが手で有効化し、全リージョンで有効化し、保護プランを揃える——この手作業は、忘れた瞬間に「監視されていないアカウント」という穴を生みます。そして攻撃者は、いちばん手薄なアカウント・いちばん手薄なリージョンを突いてきます。
この記事は、Amazon GuardDuty を AWS Organizations で全社一括統制するための実装ガイドです。単一アカウントでの有効化はこちらのピラー記事で扱っているので、本記事はその先——**「組織全体に、人手を介さず、漏れなく GuardDuty を広げ、維持する」**という統制レイヤーに絞って深掘りします。委任管理者モデル、なぜ管理アカウントで運用しないのか、auto_enable_organization_members を ALL にする理由、全リージョン戦略、そして provider alias と for_each を使った Terraform のマルチリージョン実装までを通します。
題材として、私がマルチアカウント AWS 上のサーバーレス決済プラットフォームで 本番/ステージング/監査用にアカウントを分離し、IAM・可観測性・DR を横断実装した経験——実際の金銭・カーボンクレジット・地域通貨を扱うため、アカウントの増減に対して「監視の網に穴を作らない」ことが要件だった——という視点も交えます。
この記事のルール:委任管理者・自動有効化・リージョン・Terraform リソースの仕様は AWS 公式ドキュメントおよび Terraform AWS Provider のリソース仕様(2026年6月時点) に基づきます。GuardDuty はリージョン単位・仕様は改定されるため、本番投入前に必ず公式の最新情報を確認してください。そして大前提——GuardDuty は検知の一層であり、WAF・最小権限の IAM・暗号化を代替しません。本記事の統制設計そのものも、「管理アカウントに権限を集中させない」最小権限の原則に貫かれています。
0. メンタルモデル:誰が・どこで・何を設定するのか
組織での GuardDuty 統制が混乱する最大の原因は、「どのアカウントで、どのリソースを設定するのか」が3者に分かれていることです。ここを最初に固定します。
GuardDuty の組織統制 =「管理アカウントが委任管理者を“指名”し、委任管理者が全メンバーを“統制”し、メンバーが実際に“監視される”」という三層の役割分担。設定するリソースもアカウントも、それぞれ別。
| 役割 | アカウント | ここで設定するもの |
|---|---|---|
| 管理アカウント(payer / management) | 組織のルート支払いアカウント | 委任管理者の指名のみ(aws_guardduty_organization_admin_account) |
| 委任管理者(delegated administrator) | 専用のセキュリティ/監査アカウント | detector、組織設定(自動有効化)、保護プランの組織自動有効化、finding 集約 |
| メンバーアカウント | 本番・ステージング・各サービス | (委任管理者が自動有効化するので、原則ノータッチ) |
ここから3つの帰結が出ます。これが本記事のすべての土台です。
-
委任管理者は「管理アカウント」であってはならない(推奨されない)。 AWS 公式は "the AWS security best practices follow the principle of least privilege and doesn't recommend" と明言し、管理アカウントを委任管理者にすることを推奨していません。専用のセキュリティ用アカウントを委任管理者にします。理由は単純で、支払い・組織管理・脅威検知という性質の違う責務を1アカウントに集めると、その1アカウントの侵害が全社を巻き込むからです(SRP/最小権限)。
-
設定するアカウントが2つに割れる。 委任管理者の「指名」は管理アカウントで、組織設定や自動有効化は委任管理者アカウントで実行します。Terraform で言えば、**2つの異なる provider(=2つの異なるロール/state)**が必要になります。ここを混同すると、
AccessDeniedか「設定したのに効かない」で詰まります。 -
GuardDuty はリージョン単位。組織統制も「リージョンごと」に繰り返す。 AWS Organizations はグローバルですが、GuardDuty は Regional です。公式いわく "a delegated GuardDuty administrator account is Regional"。委任管理者の指名も、組織設定も、有効化したい各リージョンで行う必要があります。だからこそ後半で Terraform の
for_each× provider alias が効いてきます。
この3点を押さえると、組織統制は 「①委任管理者を正しいアカウントに指名し → ②全メンバーを自動有効化し → ③それを全リージョンに展開する」 の3ステップだと分かります。順に作ります。
1. なぜ管理アカウントで運用しないのか:委任管理者モデル
1.1 委任管理者(delegated administrator)とは
AWS Organizations では、管理アカウントが組織内の任意のアカウントを 委任管理者(delegated GuardDuty administrator account) に指名できます。公式の定義はこうです——"the management account can designate any account within this organization as the delegated GuardDuty administrator account"。指名されたアカウントは、そのリージョンで、組織内の全メンバーアカウントの GuardDuty を有効化・管理できるようになります("the administrator account can enable and manage GuardDuty for all the member accounts in the organization within that Region")。
1.2 管理アカウントを委任管理者にしない理由
「管理アカウントでやればロールも1つで済むし楽では?」——技術的には可能です。公式も "Your organization's management account can be the delegated GuardDuty administrator account" と認めています。しかし続けて "However, the AWS security best practices follow the principle of least privilege and doesn't recommend this configuration" と明確に推奨しません。
理由を構造で言語化します。
- 侵害の影響範囲(blast radius)を分ける。 管理アカウントは「組織の作成・アカウントの追加削除・一括請求」という、組織で最も強い権限を持ちます。ここに脅威検知の運用権限まで足すと、1アカウントの侵害=組織全体の支配+全検知の無効化になりかねません。
- 日常運用するアカウントを最小化する。 委任管理者では finding のトリアージ、抑制ルールの編集、保護プランの調整など日常的な操作が発生します。これを「めったに触らないべき管理アカウント」で行うのは、操作ミス・過剰権限の温床です。
- 監査の独立性。 セキュリティ専用アカウントに検知・集約を寄せると、「誰が検知設定を変えたか」の監査線が、組織運用の操作ノイズと混ざりません。
決済プラットフォームで 本番/ステージング/監査用にアカウントを分離したのも同じ思想です。「強い権限を持つアカウントほど、日常的に触らない」——GuardDuty の委任管理者は、この原則を組織レベルで体現する仕組みです。専用の security-tooling(または audit)アカウントを委任管理者にします。
1.3 委任管理者まわりの“硬い制約”(公式)
設計前に知っておくべき公式の制約を表で固定します。これを知らないと後で詰みます。
| 制約 | 内容(公式) |
|---|---|
| メンバー上限 | 委任管理者1つあたり 最大 50,000 メンバー("a maximum of 50,000 members")。超過すると CloudWatch / Health Dashboard / メールで通知 |
| リージョン単位 | 委任管理者は Regional。有効化したい各リージョンで指名が必要("must be added through AWS Organizations in each desired Region") |
| 全リージョンで同一アカウント | 全リージョンで同じアカウントを委任管理者にする必要がある。リージョンごとに別の委任管理者は不可 |
| 委任管理者の変更は検知を止めない | 委任管理者を外しても、メンバーの GuardDuty は有効のまま("GuardDuty still remains enabled for all these member accounts") |
特に2行目・3行目が Terraform 設計に直結します。「リージョンごとに指名が要る」かつ「全リージョンで同一アカウント」——つまり 同じ admin_account_id を、複数リージョンに対して繰り返し適用するコードになります(後述)。
2. Organizations 方式 vs 招待方式(と移行)
GuardDuty でマルチアカウントを束ねる方法は2つあります。公式は AWS Organizations 統合を明確に推奨しています("GuardDuty recommends that you integrate with AWS Organizations")。
| 観点 | AWS Organizations 方式(推奨) | 招待方式(レガシー) |
|---|---|---|
| アカウントの紐付け | 組織のメンバーシップから自動 | 管理者が招待を送り、各メンバーが承認 |
| 新規アカウントの取り込み | 自動有効化(ALL/NEW)で自動 | 手動で都度招待・承認 |
| スケール | 大規模向き(最大 50,000) | 小規模・組織外アカウント向き |
| 取りこぼしリスク | 低い(構造的に自動) | 高い(承認漏れ・招待漏れ) |
| 主な用途 | 同一組織のマルチアカウント統制 | 組織に入っていない外部アカウントを一時的に束ねる |
2.1 どちらを選ぶか
同じ AWS Organizations 配下のアカウント群を統制するなら、Organizations 方式の一択です。招待方式は「組織のメンバーが招待を承認する」という人手のステップが各アカウントに発生し、これが取りこぼしの温床になります。承認を忘れたアカウントは、永久に監視外のまま残り得ます。
招待方式が今も価値を持つのは、**「組織に属していない外部アカウント(別組織・別取引先のアカウント)を一時的に finding 集約に加えたい」**といった限定ケースです。本記事のテーマ(全社統制)では Organizations 方式で進めます。
2.2 招待方式から Organizations 方式への移行
既に招待方式で運用していて Organizations 方式へ寄せる場合、考え方はシンプルです。
- 管理アカウントで GuardDuty の委任管理者を指名する(次章)。
- 委任管理者で
auto_enable_organization_members = "ALL"を設定する。 - 招待方式で紐付いていたメンバーは、組織のメンバーとして委任管理者の管理下に統合される。なお委任管理者の50,000上限は "includes member accounts that are added through AWS Organizations or those who accepted the GuardDuty administrator account's invitation" とあり、招待経由のメンバーも同じ枠で数える点に注意します。
移行時の鉄則は **「既存メンバーを取りこぼさないために、NEW ではなく ALL を選ぶ」**ことです(理由は次章)。移行は本番投入前に、サンプル finding で「委任管理者に finding が集約されるか」を必ず検証します(ピラー記事の create-sample-findings 参照)。
3. 委任管理者を指名する:管理アカウント側の Terraform
ここからコードです。まず管理アカウントで委任管理者を“指名”するだけ。これ自体は GuardDuty の中身を何も設定しません——「このアカウントに GuardDuty 管理を委ねる」という宣言だけです。
# ──────────────────────────────────────────────────────────────
# 【管理(management/payer)アカウントで実行する provider】
# このスタックは「委任管理者の指名」だけを担う。中身の設定はしない。
# ──────────────────────────────────────────────────────────────
provider "aws" {
alias = "management"
region = "ap-northeast-1"
assume_role {
# 管理アカウントの「組織管理用ロール」を引き受ける。
# 日常運用ではめったに使わない、強い権限のロール。
role_arn = "arn:aws:iam::${var.management_account_id}:role/OrgGuardDutyAdminSetup"
}
}
# GuardDuty の委任管理者を、専用 security アカウントに指名する。
# これは「指名」であって、GuardDuty の検知設定ではない点に注意。
resource "aws_guardduty_organization_admin_account" "delegate" {
provider = aws.management
admin_account_id = var.security_account_id # 例: 専用 security-tooling / audit アカウント
}
admin_account_idは管理アカウントの ID ではない:ここに入れるのは委任先(security アカウント)の ID です。混同して管理アカウント自身の ID を入れると、まさに 1 章で避けたかった「管理アカウントが委任管理者」という非推奨構成になります。
この aws_guardduty_organization_admin_account を適用すると、AWS が裏側で GuardDuty のサービスにリンクされたロールを委任管理者アカウントに用意し、委任管理者が組織 API を呼べる状態になります。指名した瞬間、そのリージョンでは委任管理者アカウントの GuardDuty が自動で有効になります(公式:"GuardDuty gets enabled automatically only in the current AWS Region")。
4. 全メンバーを自動有効化する:委任管理者側の Terraform
指名が済んだら、今度は委任管理者アカウント側の provider に切り替えて、組織設定を入れます。ここが統制の心臓部です。
# ──────────────────────────────────────────────────────────────
# 【委任管理者(security-tooling)アカウントで実行する provider】
# detector / 組織設定 / 保護プラン自動有効化 は、すべてこちら側で行う。
# ──────────────────────────────────────────────────────────────
provider "aws" {
alias = "security"
region = "ap-northeast-1"
assume_role {
role_arn = "arn:aws:iam::${var.security_account_id}:role/GuardDutyDelegatedAdmin"
}
}
# 委任管理者アカウント自身の detector。
# 指名時に自動有効化されるが、Terraform 管理下に置いて構成をコード化する。
resource "aws_guardduty_detector" "security" {
provider = aws.security
enable = true
finding_publishing_frequency = "FIFTEEN_MINUTES" # 自動対応の遅延を縮める(ピラー記事 6章)
}
# 組織メンバーの自動有効化。これが「全社に漏れなく広げる」中核。
# ALL = 既存 + 将来すべてのアカウントを有効化(推奨:サイレントな穴を作らない)
# NEW = 今後 join するアカウントのみ(既存は対象外のまま残り得る)
# NONE = 自動有効化しない
resource "aws_guardduty_organization_configuration" "this" {
provider = aws.security
detector_id = aws_guardduty_detector.security.id
auto_enable_organization_members = "ALL"
}
aws_guardduty_organization_configuration の必須引数は detector_id と auto_enable_organization_members(値は "ALL" / "NEW" / "NONE")です。
【重要】旧
auto_enable(boolean)は使わない:かつてのauto_enable = true/falseやdatasources { ... }ブロックは deprecated で、保護プランごとの組織設定は後述のaws_guardduty_organization_configuration_featureに分離されました。新規構築ではauto_enable_organization_members(3値の文字列)+_featureリソースで書きます。古いブログ記事のコードをコピペすると deprecation 警告や意図しない挙動の原因になります。
4.1 なぜ ALL を選ぶのか——NEW のサイレントな穴
3値の選択は「セキュリティの網に穴を作るかどうか」を直接決めます。
| 値 | 対象 | リスク |
|---|---|---|
ALL(推奨) | 既存+将来の全メンバー | なし。設定時点で組織にいる全アカウントが即対象になる |
NEW | 今後 join するメンバーのみ | 既存アカウントが永久に対象外のまま残り得る(サイレントな穴) |
NONE | 自動有効化しない | メンバーごとに手動管理。取りこぼし多発 |
NEW の落とし穴は深刻です。NEW を選ぶと、設定を入れた“その瞬間”に既に組織にいたアカウントは、自動有効化の対象になりません。新規アカウントだけが対象になるため、既存の本番・ステージングアカウントが GuardDuty なしのまま放置され得ます。「全社で漏れなく検知する」というセキュリティの目的に対して、NEW は構造的な穴です。
さらに公式は opt-in リージョンでの NEW の順序依存という罠も挙げています。NEW 運用では「①メンバーが opt-in リージョンを有効化 → ②そのメンバーを組織に追加」の順を守らないと、"the member account is no longer new to the organization" となり NEW が効きません。ALL ならこの順序問題自体が消える("the order of these steps is not relevant")——これも ALL を推す実務的な理由です。
意図的に GuardDuty から除外したいアカウント(たとえばサンドボックス)がある場合のみ、ALL のまま個別にそのアカウントだけ無効化する方が、NEW/NONE で全体を緩めるより安全です。
4.2 新規アカウントが join したらどうなるか
ALL(または NEW)を設定しておくと、組織に新しく作られた/招待されたアカウントは、人手を介さず自動的に GuardDuty 配下に入ります。これが Organizations 方式の最大の価値です。
決済プラットフォームのように アカウントを役割ごとに分離し、サービス追加のたびにアカウントを切る運用では、これが効きます——「新しいアカウントを作ったけど GuardDuty 入れ忘れた」という事故が、構造的に起きなくなる。運用の注意深さに頼るのをやめ、設定(ALL)に肩代わりさせるわけです。
5. 保護プランも組織で自動有効化する(per-feature)
基盤検知だけでなく、**S3 Protection・Runtime Monitoring などの保護プランも「組織全体で自動有効化」**できます。使うのは aws_guardduty_organization_configuration_feature です。
# S3 Protection を組織全体で自動 ON。
# 全アカウントで S3 データイベントを監視し、ETD の S3 攻撃シーケンス相関を全社で効かせる。
resource "aws_guardduty_organization_configuration_feature" "s3_org" {
provider = aws.security
detector_id = aws_guardduty_detector.security.id
name = "S3_DATA_EVENTS"
auto_enable = "ALL" # ここも ALL(既存+将来の全メンバー)
}
# Runtime Monitoring を組織で自動 ON し、各環境のエージェント配置も自動管理させる。
# ★コストが vCPU 比例で増えるため、「全社一律 ALL」は慎重に。重要ワークロードに絞る判断もあり。
resource "aws_guardduty_organization_configuration_feature" "runtime_org" {
provider = aws.security
detector_id = aws_guardduty_detector.security.id
name = "RUNTIME_MONITORING"
auto_enable = "ALL"
additional_configuration {
name = "EKS_ADDON_MANAGEMENT" # EKS のエージェントを自動デプロイ・更新
auto_enable = "ALL"
}
additional_configuration {
name = "ECS_FARGATE_AGENT_MANAGEMENT" # Fargate タスクにサイドカーを自動注入
auto_enable = "ALL"
}
additional_configuration {
name = "EC2_AGENT_MANAGEMENT" # EC2 のエージェントを SSM 経由で自動管理
auto_enable = "ALL"
}
}
aws_guardduty_organization_configuration_feature の必須引数は detector_id / name / auto_enable(auto_enable は ALL/NEW/NONE)です。name に指定できる代表的な保護プラン機能は次のとおりです。
name(feature) | 監視対象 | 組織自動有効化の判断 |
|---|---|---|
S3_DATA_EVENTS | CloudTrail の S3 データイベント | ALL が定番(安価・効果大・ETD 相関を全社で広げる) |
EKS_AUDIT_LOGS | EKS 監査ログ(コントロールプレーン) | EKS を使う組織なら ALL、使わないなら不要 |
RUNTIME_MONITORING | コンテナ/ホスト内のプロセス・syscall | vCPU 課金で最も高い。全社 ALL か重要WLに絞るかを要判断 |
EBS_MALWARE_PROTECTION | EC2 起因 finding 時に EBS をエージェントレススキャン | EC2 中心なら ALL |
RDS_LOGIN_EVENTS | Aurora / RDS のログインアクティビティ | DB を持つ組織で有効 |
LAMBDA_NETWORK_LOGS | Lambda のネットワーク活動(VPC Flow Logs) | サーバーレス中心なら有効 |
トレードオフの言語化(per-feature の
ALLは一律ではない):基盤検知と S3 Protection は 全社ALLが費用対効果で勝ちます。一方 Runtime Monitoring は vCPU 比例課金なので、「全社一律ALL」が必ずしも最適とは限りません。組織の検知網羅性とコストの綱引きは、GuardDuty のコスト最適化記事で詳しく扱います——委任管理者からはメンバーアカウント別の想定使用量が見えるので、まず可視化してから「どの feature をALLにし、どれを限定するか」を決めるのが正攻法です。
RUNTIME_MONITORINGとEKS_RUNTIME_MONITORINGは排他:Terraform Provider 上、"Only one of two features EKS_RUNTIME_MONITORING or RUNTIME_MONITORING can be added"。新規は統合版のRUNTIME_MONITORINGを使い、additional_configurationで EKS/ECS/EC2 のエージェント管理を個別に制御するのが現行の書き方です。
6. 全リージョン戦略:グローバルイベントを取りこぼさない
ここが組織統制で最も誤解されるポイントです。「東京リージョンだけ GuardDuty を入れておけば十分」は危険です。
6.1 なぜ未使用リージョンも有効化するのか
GuardDuty はリージョン単位のサービスです。そして IAM・STS・S3・CloudFront・Route 53 などのグローバルサービスのイベントは、特別な扱いを受けます。公式の説明はこうです——GuardDuty はこれらのグローバルサービスイベント(GSE)を "replicates those events and processes them in each Region where you have enabled GuardDuty"。つまりグローバルイベントは、あなたが GuardDuty を有効化している全リージョンに複製・処理される。
これが意味するのは——GuardDuty を有効化していないリージョンでは、これらグローバルイベントの一部が処理されない。攻撃者がそこを突きます。公式も "Threat actors can potentially launch attacks through global services... They can attempt to create unauthorized resources to exploit Regions where you have limited presence" と警告し、結論として 「全リージョンで有効化すべき」("We recommend that you enable GuardDuty in all AWS Regions available in your AWS account")。default リージョンも opt-in リージョンも対象です("including both default and opt-in Regions")。
finding のリージョン値の注意:グローバルイベント由来の finding は、finding の
Region値が、実際に検知したリージョンと異なることがあります。公式は "a finding might show us-east-1 as the Region even if GuardDuty creates the detection in a different Region" と注意しています。自動対応(EventBridge)でリージョンをキーに分岐するとき、グローバルイベントは想定外のリージョン値で飛んでくることを織り込んでおきます。
6.2 リージョンごとに「指名 → 組織設定」を繰り返す
5 章までの「指名(管理アカウント)→ 組織設定・保護プラン(委任管理者)」という一連を、有効化したい各リージョンで繰り返す必要があります。公式:委任管理者は "must be added through AWS Organizations in each desired Region"、かつ全リージョンで同一の委任管理者アカウントを使う必要があります。
手で全リージョン × 各リソースを設定するのは非現実的です。ここで Terraform の provider alias × for_each が本領を発揮します。
6.3 opt-in リージョンの注意
opt-in リージョン(アジアパシフィックの一部など、明示的に有効化が必要なリージョン)には固有の注意があります。公式いわく、委任管理者が opt-in リージョンから opt-out すると、NEW/ALL 設定でも、現在 GuardDuty が無効なメンバーを有効化できなくなることがあります。さらに前述の NEW の順序依存問題も opt-in リージョン特有です。ALL を採れば順序問題は回避できるため、ここでも ALL 推奨が効きます。
7. Terraform:provider alias × for_each によるマルチリージョン実装
「リージョンを増やしてもコードが線形に膨らまない」構造を作ります。鍵は (1) リージョンごとに provider alias を持つ、(2) 委任管理者の登録と組織設定で state を分けることです。
7.1 設計:state とロールを2つに割る
| スタック | 実行アカウント | ロール | 責務 |
|---|---|---|---|
org-admin-registration | 管理アカウント | OrgGuardDutyAdminSetup(強い・低頻度) | 委任管理者の指名(全リージョン分) |
guardduty-org-config | 委任管理者アカウント | GuardDutyDelegatedAdmin(日常運用) | detector・組織設定・保護プラン(全リージョン分) |
2つに割る理由は、まさに 1 章の最小権限です。管理アカウントの強権ロールで日常運用(保護プランの調整など)を回したくない。委任管理者の運用ロールに「アカウント指名」の権限を持たせたくもない。state を分ければ、apply に使うロールも自然に分かれます。state 分離と drift 検知の考え方はTerraform モジュール設計の記事で深掘りしています。
7.2 スタック①:委任管理者の指名(管理アカウント・全リージョン)
# variables.tf
variable "guardduty_regions" {
description = "GuardDuty を有効化する全リージョン(未使用・opt-in 含む)"
type = set(string)
default = ["ap-northeast-1", "us-east-1", "eu-west-1"] # 実際は対象を網羅する
}
variable "management_account_id" { type = string }
variable "security_account_id" { type = string }
# 各リージョンの provider を alias で定義する。
# ★ Terraform の制約: provider は for_each で動的生成できないため、対象リージョンを明示列挙する。
provider "aws" {
alias = "mgmt_apne1"
region = "ap-northeast-1"
assume_role { role_arn = "arn:aws:iam::${var.management_account_id}:role/OrgGuardDutyAdminSetup" }
}
provider "aws" {
alias = "mgmt_use1"
region = "us-east-1"
assume_role { role_arn = "arn:aws:iam::${var.management_account_id}:role/OrgGuardDutyAdminSetup" }
}
provider "aws" {
alias = "mgmt_euw1"
region = "eu-west-1"
assume_role { role_arn = "arn:aws:iam::${var.management_account_id}:role/OrgGuardDutyAdminSetup" }
}
# 各リージョンで同一の security アカウントを委任管理者に指名する。
# 全リージョンで同じ admin_account_id(公式の必須要件)。
resource "aws_guardduty_organization_admin_account" "apne1" {
provider = aws.mgmt_apne1
admin_account_id = var.security_account_id
}
resource "aws_guardduty_organization_admin_account" "use1" {
provider = aws.mgmt_use1
admin_account_id = var.security_account_id
}
resource "aws_guardduty_organization_admin_account" "euw1" {
provider = aws.mgmt_euw1
admin_account_id = var.security_account_id
}
Terraform の現実的な制約:
providerブロック自体はfor_eachで動的生成できません(provider の数はプラン前に確定している必要がある)。そのためリージョンごとに provider alias を明示列挙し、各 alias に対してリソースを書きます。リージョンが多い場合は、この列挙部分をモジュール化するか、後述のように「リージョンを引数に取る子モジュール」をfor_eachで呼ぶ構成に寄せます。
7.3 スタック②:組織設定をリージョン横断モジュールで(委任管理者)
保護プランや組織設定は項目が多いので、「1リージョン分の設定」を子モジュールに切り出し、リージョンごとに provider を渡して呼びます。これで「リージョン追加=module ブロックを1つ足す」だけになります。
# modules/guardduty-org-region/variables.tf
variable "finding_publishing_frequency" {
type = string
default = "FIFTEEN_MINUTES"
}
# modules/guardduty-org-region/main.tf
# このモジュールは「呼び出し側が渡した1つの provider(=1リージョン)」に対して
# detector + 組織設定 + 保護プランを一式作る。リージョンの知識はモジュール内に持たない(ETC)。
# 呼び出し側から provider を受け取る前提なので、configuration_aliases を宣言する。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
configuration_aliases = [aws]
}
}
}
resource "aws_guardduty_detector" "this" {
enable = true
finding_publishing_frequency = var.finding_publishing_frequency
}
resource "aws_guardduty_organization_configuration" "this" {
detector_id = aws_guardduty_detector.this.id
auto_enable_organization_members = "ALL" # 全社・漏れなく
}
resource "aws_guardduty_organization_configuration_feature" "s3" {
detector_id = aws_guardduty_detector.this.id
name = "S3_DATA_EVENTS"
auto_enable = "ALL"
}
# 呼び出し側(委任管理者アカウントの provider を各リージョン分用意して渡す)
provider "aws" {
alias = "sec_apne1"
region = "ap-northeast-1"
assume_role { role_arn = "arn:aws:iam::${var.security_account_id}:role/GuardDutyDelegatedAdmin" }
}
provider "aws" {
alias = "sec_use1"
region = "us-east-1"
assume_role { role_arn = "arn:aws:iam::${var.security_account_id}:role/GuardDutyDelegatedAdmin" }
}
# リージョンを足したいときは、provider alias と module 呼び出しを1つ追加するだけ。
module "guardduty_apne1" {
source = "./modules/guardduty-org-region"
providers = { aws = aws.sec_apne1 }
}
module "guardduty_use1" {
source = "./modules/guardduty-org-region"
providers = { aws = aws.sec_use1 }
}
適用順序(依存関係):スタック②(組織設定)は、スタック①(委任管理者の指名)がそのリージョンで完了した後でないと失敗します。指名されていないアカウントでは、組織設定 API を呼ぶ権限がないからです。①→②の順で apply します(別 state なので、CI のジョブ依存で順序を保証するのが堅牢)。鍵レスな CI からの apply はOIDC 鍵レス CI/CD の記事の構成(
planとapplyでロールを分ける)に乗せられます。
7.4 メンバー個別のオーバーライド
ALL で全社一律にしつつ、特定アカウントだけ保護プランを変えたい(例:サンドボックスだけ Runtime Monitoring を切る)場合は、委任管理者からメンバーアカウントの detector を個別に上書きします。Terraform では aws_guardduty_detector_feature をメンバーの detector に対して適用するか、UpdateMemberDetectors 相当の操作で個別管理します。原則は 「全社デフォルトは ALL、例外は明示的に1アカウントずつ」——NEW/NONE で全体を緩めて穴を作るより、構造的に安全です。
8. finding 集約・publishing・ランディングゾーン
8.1 finding は委任管理者に自動集約される
組織を組むと、全メンバー・全リージョンの finding が委任管理者アカウントに自動集約されます(公式:委任管理者は "review all generated findings" できる)。委任管理者は組織全体のセキュリティ姿勢を一望でき、メンバーアカウントごとの finding と想定使用量を確認できます。これが「セキュリティ専用アカウントに検知を寄せる」という設計の実利です。
8.2 組織レベルで S3 へエクスポート・Security Hub へ集約
委任管理者を起点に、finding を S3 にエクスポート(長期保管・分析)し、AWS Security Hub CSPM に流して他の検知(Macie 等)と横断的に優先順位づけします。深掘り調査は Amazon Detective。「GuardDuty で検知 → 委任管理者で集約 → Security Hub で横断 → Detective で深掘り」が組織運用の王道です。
finding を行動に変える EventBridge → 冪等な自動対応は、本記事の範囲を超えるため自動対応・インシデントレスポンスの記事に譲ります。組織構成では、委任管理者アカウントに集約された finding を起点に EventBridge ルールを組むのが定石です。
8.3 ランディングゾーン / Control Tower との関係
新規に組織を設計するなら、AWS Control Tower / ランディングゾーンの文脈に乗せるのが自然です。Control Tower は典型的に Audit アカウントと Log Archive アカウントを用意します。GuardDuty の委任管理者は、この Audit(または専用 Security)アカウントに割り当てるのが定番配置です——本記事で一貫して言ってきた「管理アカウントではなく専用セキュリティアカウント」と完全に一致します。
既存組織への後付け:Control Tower を使っていなくても本記事の構成はそのまま適用できます。重要なのはツールではなく原則——委任管理者を専用アカウントに置き、
ALLで全リージョン自動有効化すること。Control Tower はそれを“規約として”敷いてくれるだけです。
9. アカウント間の最小権限境界(IAM 設計)
最後に、本記事で2つに割ったロールの境界を明示します。これは飾りではなく、統制設計の本体です。
| ロール | 置き場所 | 許す主な操作 | 許さない操作 |
|---|---|---|---|
OrgGuardDutyAdminSetup | 管理アカウント | guardduty:EnableOrganizationAdminAccount / Disable... / 関連 organizations:*(最小限) | 保護プラン調整・finding 操作・抑制ルール編集 |
GuardDutyDelegatedAdmin | 委任管理者アカウント | guardduty:UpdateOrganizationConfiguration / Update*Detector* / CreateFilter(抑制)/ finding 参照 | アカウント指名(委任管理者の登録)・組織のアカウント追加削除 |
設計原則を言語化します。
- 指名権限と運用権限を交差させない。 管理アカウントのロールは「指名・解除」まで。委任管理者のロールは「日常運用」まで。どちらのロールも、もう一方の領域に踏み込めないようにします。これが侵害の影響範囲を分ける本体です。
- 管理アカウントの強権ロールは“低頻度・要承認”に。
OrgGuardDutyAdminSetupは委任管理者を変えるときくらいしか使いません。CI から触るなら、environment承認ゲート付きの OIDC ロールに限定します(OIDC 記事の environment 承認)。 Resourceを絞れる API は絞る。 finding 参照系は読み取り専用ロールに、設定変更系は権限境界(permissions boundary)付きロールに分けます。- GuardDuty は IAM の代替ではない。 念押しです。GuardDuty は「漏れた認証情報の悪用」を検知しますが、漏らさないための最小権限 IAM・鍵レス認証・暗号化は別途必要です。GuardDuty はこの多層防御の中で「組織横断で兆候を早く知らせる層」を担います。
10. まとめ:GuardDuty 組織統制チートシート
迷ったときの早見表です。
- 方式:マルチアカウントは AWS Organizations 統合が公式推奨。招待方式は組織外アカウントを束ねる限定用途。移行時は
ALLで既存メンバーを取りこぼさない。 - 委任管理者:管理(payer)アカウントでは運用しない(公式が最小権限の観点で非推奨)。専用の
Security/Auditアカウントを委任管理者に。指名は管理アカウントで(aws_guardduty_organization_admin_account)、組織設定は委任管理者アカウントで。 - 自動有効化:
aws_guardduty_organization_configurationのauto_enable_organization_members = "ALL"(既存+将来)。NEWは既存アカウントがサイレントに対象外になる穴。旧auto_enable(boolean)/datasourcesブロックは deprecated——使わない。 - 保護プラン:
aws_guardduty_organization_configuration_feature(name+auto_enable)で組織自動有効化。S3 はALLが定番、Runtime Monitoring は vCPU 課金なので全社一律か限定かを要判断。RUNTIME_MONITORINGとEKS_RUNTIME_MONITORINGは排他。 - リージョン:GuardDuty は Regional。指名→組織設定を各リージョンで繰り返す。全リージョンで同一の委任管理者アカウントが必須。グローバルイベント(IAM/STS/S3/CloudFront/Route 53)は全有効リージョンに複製・処理されるため、未使用・opt-in 含め全リージョン有効化。finding の
Region値は検知リージョンと異なり得る。 - Terraform:provider は
for_each不可 → リージョンごとに alias を列挙し、1リージョン分を子モジュール化して provider を渡す。①指名(管理アカウント・強権ロール)と ②組織設定(委任管理者・運用ロール)で state とロールを分離。①→②の順で apply。 - 新規アカウント:
ALL下では組織に join したアカウントが自動で GuardDuty 配下に入る。入れ忘れが構造的に消える。 - 集約:finding は委任管理者に自動集約。S3 エクスポート・Security Hub・Detective で横断。Control Tower の
Auditアカウントが委任管理者の定番配置。 - IAM 境界:指名権限と運用権限を交差させない。GuardDuty は検知の一層であり、最小権限 IAM・鍵レス認証・暗号化を代替しない。
組織での GuardDuty は「全アカウントで手動有効化する作業」ではなく、「委任管理者に集約し、ALL で自動有効化し、全リージョンに Terraform で展開する」という統制の設計です。最大のレバレッジは、アカウントが増減しても監視の網に穴が空かない構造を、運用の注意ではなくコードで担保することにあります。
私はマルチアカウントのサーバーレス決済プラットフォームで、実際の金銭・カーボンクレジット・地域通貨を扱う基盤の IAM・可観測性・DR を横断実装し、本番/ステージング/監査用のアカウント分離を「運用の注意深さ」ではなくコードの構造で支えてきました。GuardDuty の組織統制も同じ思想で設計します——①委任管理者を専用アカウントに置いて影響範囲を分け、②ALL × 全リージョンで取りこぼしを構造的に消し、③Terraform のマルチリージョン構成で「リージョン追加=1行追加」に保つ。
「自社のマルチアカウント AWS に GuardDuty をどう全社展開し、委任管理者をどこに置き、どのリージョン・どの保護プランを ALL にするか」——Organizations 設計から Terraform 実装、アカウント間の最小権限境界、finding 集約まで、一人 × 生成AI(Claude Code)で速く・安全に伴走できます。 既存組織への後付けでも、要件整理の段階からでも、お気軽にご相談ください。
参考(公式ドキュメント)
- Managing GuardDuty accounts with AWS Organizations — 委任管理者の概念・50,000上限・Regional 制約・opt-in リージョンの注意・管理アカウントを委任管理者にしない推奨
- Multiple accounts in Amazon GuardDuty — Organizations 方式 vs 招待方式・委任管理者とメンバーの関係・公式推奨
- Amazon GuardDuty Regions and endpoints — 全リージョン有効化の推奨・リージョン別の機能差
- GuardDuty foundational data sources(global service events) — グローバルサービスイベント(IAM/STS/S3/CloudFront/Route 53)の全リージョン複製・処理、finding の Region 値が異なり得る注意
- Terraform: aws_guardduty_organization_configuration —
auto_enable_organization_members(ALL/NEW/NONE)・datasourcesブロックの deprecation - Terraform: aws_guardduty_organization_configuration_feature —
name/auto_enable・additional_configuration(EKS_ADDON_MANAGEMENT ほか)・RUNTIME_MONITORING と EKS_RUNTIME_MONITORING の排他 - Terraform: aws_guardduty_organization_admin_account — 管理アカウントで委任管理者を登録する
admin_account_id