はじめに
個人の学習用に AWS アカウントを 2 つ持っています。「S3 バケットを誤って公開していないか」「バージョニングを忘れていないか」といったチェックを AWS Config でやろうとしたとき、ふと気づきました。
アカウントが 2 つあるということは、同じルールを 2 回設定しないといけないのでは…?
調べてみると、AWS Organizations を使っている環境なら「組織コンフォーマンスパック」という機能で、ルール一式を全アカウントにまとめて配布できると分かりました。この記事は、それを実際にやってみた記録です。配布して終わりではなく、その後に出てきた「非準拠」への対応(直すのか、あえて直さないのか)まで書きます。
環境
- AWS アカウント 2 つ(管理アカウント
111111111111、メンバーアカウント222222222222)を AWS Organizations でまとめている - ログインは IAM Identity Center(いわゆる SSO)
- リージョン: ap-northeast-1(東京)
- 操作は Windows + Git Bash + AWS CLI(コマンドは macOS / Linux のターミナルでもそのまま使えます)
※記事中のアカウント ID はダミーです。
用語を先にざっくり整理
この記事に出てくる言葉を、ひとことずつ。
| 用語 | ざっくり言うと |
|---|---|
| AWS Config | リソースの設定を記録して、ルール違反がないかチェックしてくれるサービス |
| Config ルール | チェック項目 1 つ 1 つのこと(例:「S3 は公開禁止」) |
| レコーダー | 設定変更を記録する Config の本体。アカウント×リージョンごとに必要 |
| コンフォーマンスパック | ルールを複数束ねて 1 枚のテンプレートにしたもの(ルールの詰め合わせ) |
| 組織コンフォーマンスパック | その詰め合わせを、組織内の全アカウントへ一括配布する仕組み(今回の主役) |
| 非準拠(NON_COMPLIANT) | ルール違反として検出された状態 |
組織コンフォーマンスパックの嬉しいポイントは 3 つです。
- 1 コマンドで全アカウントに配布できる(アカウントが 10 個でも同じ手間)
- 今後組織にアカウントを追加すると、自動で同じルールが適用される
- 配布されたルールはメンバーアカウント側では消せない(勝手にチェックを緩められない)
事前に必要なもの
やってみて分かったのですが、パックを配る前に揃えておく条件が 3 つあります。
- 操作は管理アカウントから行う(メンバーアカウントからは配布できません)
- Organizations 側で Config との連携を有効化しておく(後述の 1 コマンド)
- 各アカウントで Config のレコーダーが動いていること
3 が地味に重要です。パックはあくまで「ルールの配布」で、記録する本体(レコーダー)までは配ってくれません。レコーダーが動いていないアカウントには配布が失敗します。
レコーダーの稼働確認はこれです。
aws configservice describe-configuration-recorder-status --profile <プロファイル名>
"recording": true が返れば OK。まだのアカウントは、コンソールの Config セットアップ画面か CLI で先に作っておきます(この構築だけで一記事書ける長さになるので、今回は割愛します)。
やってみる
ステップ 1: 組織の状態を確認する
まず「自分の組織にどのアカウントがいるか」「どれが管理アカウントか」を確認します。
aws organizations list-accounts --profile mgmt
結果の ARN(arn:aws:organizations::111111111111:account/...)に入っている ID が管理アカウントです。以降の操作は、この管理アカウント用のプロファイルで行います。
ハマりポイント: 複数アカウント環境の CLI では「今どのアカウントに向かって打っているか」の取り違えが起きがちです。作業前に
aws sts get-caller-identity --profile mgmtを打って、Accountが管理アカウントになっているか確認する癖をつけると安全です(私はこれを怠って一度盛大に迷子になりました)。
ステップ 2: Organizations と Config の連携を有効化する
管理アカウントから、1 回だけ実行します。
aws organizations enable-aws-service-access --service-principal config-multiaccountsetup.amazonaws.com --profile mgmt
aws organizations enable-aws-service-access --service-principal config.amazonaws.com --profile mgmt
成功すると何も表示されません(AWS CLI は「無言 = 成功」が多いです)。心配なら確認コマンドで:
aws organizations list-aws-service-access-for-organization --profile mgmt
一覧に config-multiaccountsetup.amazonaws.com と config.amazonaws.com が出ていれば OK です。
ステップ 3: 配布するルールを YAML に書く
配布する「ルールの詰め合わせ」をテンプレートとして書きます。今回は S3 の基本 4 つ + Lambda 1 つの計 5 ルールにしました。
org-pack.yaml:
Resources:
# --- ルール1: S3 バケットの公開読み取り禁止 ---
# パブリックに読み取りできる設定のバケットを検出する(セキュリティ上いちばん重要)
S3BucketPublicReadProhibited: # ← テンプレート内での呼び名(自由に付けてよい)
Type: AWS::Config::ConfigRule # 「Config ルールを作る」という宣言(全ルール共通)
Properties:
ConfigRuleName: org-s3-bucket-public-read-prohibited # 実際に表示されるルール名
Source:
Owner: AWS # AWS 提供のマネージドルールを使う、の意味
SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED # どのマネージドルールかの ID
# --- ルール2: S3 バケットの暗号化必須 ---
# サーバーサイド暗号化が無効なバケットを検出する
S3BucketServerSideEncryptionEnabled:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: org-s3-bucket-server-side-encryption-enabled
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED
# --- ルール3: S3 バケットのアクセスログ必須 ---
# アクセスログ未設定のバケットを検出する(ログ保管用バケット自身は例外扱いにする予定)
S3BucketLoggingEnabled:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: org-s3-bucket-logging-enabled
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_LOGGING_ENABLED
# --- ルール4: S3 バケットのバージョニング必須 ---
# 誤削除・誤上書きからの復元を可能にするバージョニングが無効なバケットを検出する
S3BucketVersioningEnabled:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: org-s3-bucket-versioning-enabled
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_VERSIONING_ENABLED
# --- ルール5: Lambda 関数の公開アクセス禁止 ---
# 誰でも呼び出せるリソースポリシーが付いた Lambda 関数を検出する
LambdaFunctionPublicAccessProhibited:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: org-lambda-function-public-access-prohibited
Source:
Owner: AWS
SourceIdentifier: LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED
コメント(# 以降)は実行時に無視されるので、このまま org-pack.yaml として保存してデプロイしても動きます。
中身の読み方は「SourceIdentifier = AWS が用意しているマネージドルールの ID」「ConfigRuleName = 自分の環境での表示名」です。表示名に org- を付けたのは、「これは組織パックで配ったルール」と一目で分かるようにするためです。
注意: YAML はインデント(行頭の空白)がズレると失敗します。コピーするときは丸ごとコピーしてください。
ステップ 4: 配布する
いよいよ本番。この 1 コマンドです。
aws configservice put-organization-conformance-pack --profile mgmt \
--organization-conformance-pack-name org-baseline \
--template-body file://org-pack.yaml
配布には数分かかります。進み具合はこれで見られます。
aws configservice get-organization-conformance-pack-detailed-status --profile mgmt \
--organization-conformance-pack-name org-baseline
ステップ 5: 届いたか確認する
各アカウントのルール一覧を見てみます。
aws configservice describe-config-rules --profile mgmt --query "ConfigRules[].ConfigRuleName"
aws configservice describe-config-rules --profile member --query "ConfigRules[].ConfigRuleName"
[
"org-lambda-function-public-access-prohibited-conformance-pack-xxxxxxxxx",
"org-s3-bucket-logging-enabled-conformance-pack-xxxxxxxxx",
"org-s3-bucket-public-read-prohibited-conformance-pack-xxxxxxxxx",
"org-s3-bucket-server-side-encryption-enabled-conformance-pack-xxxxxxxxx",
"org-s3-bucket-versioning-enabled-conformance-pack-xxxxxxxxx"
]
両アカウントに 5 ルールが入りました。 手元で設定したのは管理アカウントでの 1 コマンドだけです。
ルール名の末尾に -conformance-pack-xxxxxxxxx という文字列が勝手に付いていますが、これは「パックで配布されたルール」の目印としてAWS が自動で付けるものです(アカウントごとに違う値になります)。消したり変えたりする必要はありません。
配って終わりではなかった: 非準拠が出てくる
しばらく待つと初回のチェックが走り、「非準拠」つまりルール違反が検出され始めます。ここからが本当の運用でした。
どこが違反しているかを見る
# どのルールで違反が出ているか
aws configservice describe-compliance-by-config-rule --profile member \
--compliance-types NON_COMPLIANT --query "ComplianceByConfigRules[].ConfigRuleName"
# そのルールで、どのリソースが違反しているか
aws configservice get-compliance-details-by-config-rule --profile member \
--config-rule-name <上で出たルール名> --compliance-types NON_COMPLIANT \
--query "EvaluationResults[].EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId"
私の環境では「バージョニング未設定」と「アクセスログ未設定」の 2 ルールで、メンバーアカウント側は 20 個以上のバケットが引っかかりました。一瞬ひるむ数ですが、落ち着いて仕分けると、やることは意外と少なかったです。
対応は三択:「直す」「例外にする」「消す」
非準拠 = 全部直す、ではありませんでした。バケットの性格ごとに 3 つに仕分けます。
① 直す — 自分のアプリの本体やデータが入っているバケット。バージョニング(誤って消したり上書きしても前の版に戻せる機能)を付ける価値が大きいので、素直に有効化します。1 バケット 1 コマンドです。
aws s3api put-bucket-versioning --profile member \
--bucket <バケット名> --versioning-configuration Status=Enabled
特に Terraform の state ファイルを置いているバケットは最優先です。state が壊れると復旧がとても大変なので、実務でもバージョニング必須とされています。
② 例外にする(あえて直さない) — 次のようなバケットは、直さない判断をしました。
- ログ保管用バケットの「アクセスログ未設定」: ログバケットのログをまた別のバケットに…とやると際限がないので、「ログのログは取らない」と方針を決めて受け入れ
-
CDK や CloudFormation が自動で作ったバケット: ツールが管理しているものを CLI で直接いじると、ツール側の認識と実際の状態がズレて後で事故ります。直したいなら CDK のコード側に
versioned: trueを 1 行足してcdk deployするのが正しい経路です
ここで大事なのは、「放置」と「例外」は別物ということ。同じ「直さない」でも、理由をメモに残しておけば「判断済みの例外」、残さなければ後から見た人(未来の自分含む)に「対応漏れ」と区別がつきません。私は「ログバケットはログ設定対象外。判断日 ◯/◯」のような一行メモを残すことにしました。
③ 消す — 実験で作ったきり使っていないバケットや、作り直しで残った古いバケットは、消すのが一番きれいな対応です。非準拠が減ってコストも減ります。
直したら、準拠に変わるのを見届ける
修正すると Config が自動で再チェックしてくれますが、待ちきれないときは手動でチェックを蹴れます。
aws configservice start-config-rules-evaluation --profile member \
--config-rule-names <ルール名>
数分後に非準拠一覧を見直して、直したルールが消えていれば一巡完了です。最終的にうちの環境は「非準拠は、理由をメモした例外だけ」という状態に落ち着きました。個人的には、この検出 → 仕分け → 修正 → 準拠を確認の一周を回せたことが一番の収穫でした。
今後ルールを増やしたくなったら
org-pack.yaml にルールを書き足して、ステップ 4 と同じコマンドをもう一度打つだけです。全アカウントに反映されます。チェックの基準が YAML 1 枚に集約されるので、Git で履歴管理する運用にも自然につながります。
ちなみに、ルールは最初から欲張らない方がいいと感じました。使っていないサービスのルールを入れてもチェック対象ゼロで意味がないですし、非準拠が大量に出ると本当に危ないものが埋もれます。「非準拠ゼロ(か、説明できる例外だけ)を保てる範囲で少しずつ増やす」のが、形だけのチェックにしないコツだと思います。
まとめ
- 組織コンフォーマンスパックを使うと、Config ルールの配布が「アカウントごとの手作業」から「管理アカウントからの 1 コマンド」になる
- 事前条件は 3 つ: 管理アカウントから実行 / Organizations と Config の連携有効化 / 各アカウントでレコーダー稼働
- 配布した後に出てくる非準拠は「直す・例外にする・消す」の三択で仕分ける。例外は理由をメモに残す
- IaC(CDK 等)が管理しているリソースは、CLI で直接触らず IaC のコード側で直す
- 新しいアカウントを組織に足すと自動で同じルールが入るので、組織が育つほど効いてくる
ちなみに、こうして「全アカウントに共通のチェック基準を配り、違反を検出して対応を記録する」仕組みは、実務では「監査」や「ガバナンス」と呼ばれる領域です。個人の学習環境でやったことが、そのまま実務用語につながっていました。
アカウント 2 つの個人環境でも十分に試せて、会社で行われているようなマルチアカウント統制の縮図を体験できました。同じような構成の方はぜひ試してみてください。