はじめに
AWS Config をコンソールからセットアップしようとしたら、エラーが 2 連続で出ました。「まあ何とかなるだろう」と CLI で状態を確認したら、今度は「レコーダーが存在しない」と言われて頭が真っ白に。散々調べた結果、原因はまさかのところにありました。
先に結論を言ってしまうと、コンソールと CLI が、最初から別々の AWS アカウントを見ていたのが原因です。同じエラーで検索してたどり着いた方のために、詰まった順に書き残します。
環境
- AWS アカウントを 2 つ保有し、IAM Identity Center(いわゆる SSO)でログインしている
-
管理用アカウント:
111111111111(AWS Organizations の管理や監査ログの集約など、環境全体の管理に使っている方。今回 Config を作りたかったアカウント) -
ポートフォリオ用アカウント:
222222222222(アプリ開発やポートフォリオ置き場として普段よく使っている方)
-
管理用アカウント:
- リージョン: ap-northeast-1(東京)
- Windows + Git Bash + AWS CLI(コマンドは macOS / Linux でもそのまま使えます)
※記事中のアカウント ID はダミーです。以降も「管理用」「ポートフォリオ用」と呼びます。
用語をざっくり整理
| 用語 | ざっくり言うと |
|---|---|
| AWS Config | リソースの設定を記録して、ルール違反がないかチェックしてくれるサービス |
| レコーダー | 設定変更を記録する Config の本体。これが動いていないと何も始まらない |
| 配信チャネル | 記録したデータを S3 バケットに出力するための設定。レコーダーとセットで必要 |
| サービスリンクロール | AWS のサービスが動くために自動で使う専用の IAM ロール |
| プロファイル | AWS CLI の「どのアカウントに誰として入るか」の設定。今回の主役 |
Config は「レコーダー(記録役)+ 配信チャネル(出力役)+ S3 バケット(保存先)」の 3 点セットで動く、と覚えておくと、この後の話が追いやすくなります。
やろうとしたこと
ブラウザで管理用アカウントのコンソールを開き、AWS Config の初期セットアップ画面で次の構成を作ろうとしました。
- 記録: すべてのリソースタイプを記録(連続)
- 配信先: S3 バケット
- ルール: S3 系 4 つ + Lambda 1 つ
画面の案内どおりに選んで「確認」を押すだけ…のはずでした。
エラー 1: Service role name AWSServiceRoleForConfig has been taken
確認を押すと、まずこれが出ました。
Service role name AWSServiceRoleForConfig has been taken in this account,
please try a different suffix.
一見怖いですが、意味は「Config 用のサービスリンクロールが、もうこのアカウントにあるよ」です。過去に一度でも Config のセットアップを試した(途中でやめた場合も含む)アカウントでは、ロールだけが残っていることがあります。
対処: 新規作成ではなく、セットアップ画面で「既存の AWS Config サービスリンクロールを使用」を選ぶだけ。ロールはアカウントに 1 つあれば十分で、作り直す必要はありません。
「もうあるから作れない = あるものを使えばいい」。それだけの話でした。1 つ目クリア。
エラー 2: Insufficient delivery policy to s3 bucket
ロール問題を回避して進むと、今度はこれ。
Insufficient delivery policy to s3 bucket: aws-cloudtrail-logs-111111111111-xxxxxxxx,
unable to write to bucket, provided s3 key prefix is 'null', provided kms key is 'null'.
「delivery policy が足りない = そのバケットに書き込めない」と言われています。原因は、配信先としてすでにあった CloudTrail 用のバケットを流用したことでした。
S3 バケットには「バケットポリシー」という、バケット側に付ける許可設定があります。CloudTrail が作るバケットのポリシーは cloudtrail.amazonaws.com(CloudTrail)にしか書き込みを許可していないので、config.amazonaws.com(Config)が書き込もうとすると拒否されるわけです。
対処: Config には Config 専用のバケットを用意するのが正解。バケットの流用もできますが、ログが混在すると管理がしづらいので専用バケットを分けるのが望ましい。私も専用バケットを作る方針にしました
不可解ゾーン: CLI で見ると「レコーダーが存在しない」
2 連続のエラーで状況が分からなくなってきたので、一度 CLI で Config の状態を確認することにしました。ここまでの流れなら、少なくともロールは存在し、レコーダーも作りかけの状態で残っているはず…。
aws configservice describe-configuration-recorders --profile myprofile
{
"ConfigurationRecorders": []
}
空。何も、ない。コンソールであれだけ格闘したのに、CLI からは存在ごと見えません。ここで頭が真っ白になりました。
ここから犯人探しです。疑った順に書きます。
容疑者 1: SSO のログイン切れ → シロ
そういえば直前に、こんなエラーも出ていました。
Error when retrieving token from sso: Token has expired and refresh failed
これは SSO のログイン期限切れです。SSO 環境の CLI では数時間〜数日でログインが切れる仕組みなので、日常的に起きます。再ログインで直ります。
aws sso login --profile myprofile
ただ、ログインし直してもレコーダーは空のまま。認証は通っているのに見えない。原因は別にありました。
容疑者 2: リージョン違い → シロ
「東京で作ったのに、CLI がバージニア北部を見ているのでは?」と疑い、プロファイルの設定を確認。
aws configure list --profile myprofile
region は ap-northeast-1(東京)。コンソールで作業していたリージョンと一致していました。これでもない。
真犯人: CLI が「別のアカウント」を見ていた
決定打は、何気なく打ったバケット一覧でした。
aws s3 ls --profile myprofile
表示されたバケット名をよく見ると…
aws-cloudtrail-logs-222222222222-xxxxxxxx
バケット名に含まれるアカウント ID が、コンソールで作業していたアカウントと違う。
- コンソールで作業していたのは: 管理用アカウント(
111111111111) - CLI のプロファイルが向いていたのは: ポートフォリオ用アカウント(
222222222222)
SSO で複数アカウントにアクセスできる環境で、CLI のプロファイルが普段使いのポートフォリオ用アカウントのままだったのです。つまりコンソールと CLI は、最初からずっと別の世界を見ていました。ポートフォリオ用アカウントに Config は無いので、レコーダーが空なのは当たり前だったのです。
エラーでも故障でもなく、ただの見当違い。分かってしまえば単純ですが、認証エラー(容疑者 1)と混ざっていたせいで、たどり着くまでかなり遠回りしました。
教訓: 作業の最初に「自分は今どこにいるか」を確認する
このコマンドを最初に打っていれば、迷子は一瞬で防げました。
aws sts get-caller-identity --profile myprofile
{
"Account": "222222222222",
"Arn": "arn:aws:sts::222222222222:assumed-role/..."
}
返ってきた Account を見るだけです。もしこれを最初に打っていたら、「あ、ポートフォリオ用アカウントを向いてる」と気づけたはずです。複数アカウント環境で CLI を触るときの最初の一手として、心からおすすめします。
管理用アカウント用のプロファイルを作る
原因が分かったので、CLI を管理用アカウントに向けます。といっても既存のプロファイルを書き換えるのではなく、管理用アカウント用のプロファイルをもう 1 つ追加します(ポートフォリオ用プロファイルも今後使うので残しておきます)。
aws configure sso の対話で作れますが、仕組みを理解するには設定ファイル ~/.aws/config を直接見るのが早いです。追記後はこうなりました。
# SSO のログイン設定(全プロファイル共通で使い回す)
[sso-session my-sso]
sso_start_url = https://xxxxxxxxxx.awsapps.com/start
sso_region = ap-northeast-1
# もともとあったプロファイル(ポートフォリオ用アカウントに入る)
[profile myprofile]
sso_session = my-sso
sso_account_id = 222222222222 # ← ポートフォリオ用アカウント
sso_role_name = AdministratorAccess
region = ap-northeast-1
# 今回追加したプロファイル(管理用アカウントに入る)
[profile mynewprofile]
sso_session = my-sso
sso_account_id = 111111111111 # ← 管理用アカウント。違いは実質この 1 行だけ
sso_role_name = AdministratorAccess
region = ap-northeast-1
見ての通り、2 つのプロファイルの違いは sso_account_id の 1 行だけ。プロファイルとは「同じ SSO ログインで、どのアカウントに入るかの切り替えスイッチ」だと分かります。1 つの SSO に対してアカウントの数だけスイッチを並べておき、コマンドの --profile で使い分ける、という構造です。
追記したらログインして、必ず「今どこにいるか」を確認します。
aws sso login --profile mynewprofile
aws sts get-caller-identity --profile mynewprofile
Account が 111111111111(管理用アカウント)になっていることを見届けてから、次へ進みます。
改めて CLI で管理用アカウントを調査する
正しい世界に接続できたので、Config の 3 点セット(レコーダー・配信チャネル・バケット)がどこまで出来ているかを調べ直しました。describe 系のコマンドは状態を見るだけで何も壊さないので、遠慮なく打てます。
aws configservice describe-configuration-recorders --profile mynewprofile
aws configservice describe-configuration-recorder-status --profile mynewprofile
aws configservice describe-delivery-channels --profile mynewprofile
aws s3 ls --profile mynewprofile
結果はこうでした。
| 部品 | 状態 | 意味 |
|---|---|---|
| レコーダー |
あるが停止中(recording: false) |
コンソールの試行は、実は思ったより先まで通っていた |
| 配信チャネル |
無い(DeliveryChannels: []) |
エラー 2(S3 に書けない)で止まった地点 |
| Config 用バケット | 無い | CloudTrail バケットを流用しようとして失敗したままだった |
状況がやっと正確に見えました。Config は配信チャネルが無いとレコーダーを起動できない仕組みなので、「バケットを作る → 書き込み許可を付ける → 配信チャネルを作る → レコーダーを起動する」の順で、欠けている部品だけを足していきます。
コンソールのウィザードを最初からやり直すより、欠けた部品だけ CLI でピンポイントに足す方が、こういう中途半端な状態からの復旧には向いていました。
CLI で復旧する
1. Config 専用バケットを作る
エラー 2 の教訓どおり、流用ではなく専用バケットを作ります。
aws s3api create-bucket --profile mynewprofile \
--bucket config-bucket-111111111111 \
--create-bucket-configuration LocationConstraint=ap-northeast-1
2. バケットに「Config は書き込み OK」の許可を付ける
エラー 2 の原因は「バケット側が Config からの書き込みを許可していない」ことでした。同じ轍を踏まないよう、作ったバケットにバケットポリシーで許可を付けます。サービスリンクロール構成では、ロール側に権限を足せない代わりにバケット側で許可する方式になります。
config-bucket-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSConfigBucketPermissionsCheck",
"Effect": "Allow",
"Principal": {"Service": "config.amazonaws.com"},
"Action": ["s3:GetBucketAcl", "s3:ListBucket"],
"Resource": "arn:aws:s3:::config-bucket-111111111111",
"Condition": {"StringEquals": {"AWS:SourceAccount": "111111111111"}}
},
{
"Sid": "AWSConfigBucketDelivery",
"Effect": "Allow",
"Principal": {"Service": "config.amazonaws.com"},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::config-bucket-111111111111/AWSLogs/111111111111/Config/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"AWS:SourceAccount": "111111111111"
}
}
}
]
}
aws s3api put-bucket-policy --profile mynewprofile \
--bucket config-bucket-111111111111 \
--policy file://config-bucket-policy.json
3. レコーダーの設定を入れ直す
レコーダー自体は残っていましたが、設定を明示して上書きしておきます。ロールは、エラー 1 で「もうある」と言われたサービスリンクロールをそのまま使います。
aws configservice put-configuration-recorder --profile mynewprofile \
--configuration-recorder "name=default,roleARN=arn:aws:iam::111111111111:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig" \
--recording-group "allSupported=true,includeGlobalResourceTypes=true"
(もしサービスリンクロールが無いアカウントなら、先に aws iam create-service-linked-role --aws-service-name config.amazonaws.com を実行します。既にあると例の has been taken が出ますが、「もうある」だけなのでスキップで OK です)
4. 配信チャネルを作る(今回欠けていた部品)
aws configservice put-delivery-channel --profile mynewprofile \
--delivery-channel "name=default,s3BucketName=config-bucket-111111111111"
5. レコーダーを起動する
aws configservice start-configuration-recorder --profile mynewprofile \
--configuration-recorder-name default
ちなみに 2〜5 は、成功すると何も表示されません。AWS CLI は「無言 = 成功」が多いので、静かでも慌てず次へ進んで大丈夫です。
6. 動いているか確認する
aws configservice describe-configuration-recorder-status --profile mynewprofile
{
"ConfigurationRecordersStatus": [
{
"name": "default",
"recording": true,
"lastStatus": "SUCCESS"
}
]
}
recording: true(記録中)と lastStatus: SUCCESS(S3 への出力も成功)。復旧完了です。長かった…。
補足: Config はアカウントごと・リージョンごとに必要です
今回復旧したのは管理用アカウントの東京リージョンだけです。Config のレコーダーはアカウント×リージョンの単位で動くので、ポートフォリオ用アカウントを監視したければ、そちらにも同じ構築が必要でした(手順は同じで、アカウント ID とバケット名を読み替えるだけです)。
「アカウントが増えるたびに同じ作業をするの?」と思った方、正解です。それを楽にする仕組み(AWS Organizations の組織コンフォーマンスパックによるルールの一括配布)は先の記事にまとめました。
まとめ
今回の詰まりポイントと学びを、時系列で振り返ります。
-
Service role name ... has been taken→ 「もうある」と言われているだけ。既存のサービスリンクロールを使えば OK -
Insufficient delivery policy→ 配信先バケットの許可不足。CloudTrail バケットのままではConfigが書き込めない(ポリシーにConfig用の許可を追記すれば流用自体は可能。ただしログの混在や管理の面から、専用バケットに分けるのが望ましい。)Config 専用バケット + バケットポリシーで対応 - CLI でレコーダーが空 → エラーですらなく、プロファイルが別アカウントを向いていた。コンソールと CLI が同じアカウントを見ているとは限らない
- 復旧は CLI が向いていた → コンソールのウィザードは中途半端な状態からのやり直しが苦手。describe 系で現状を正確に把握し、欠けた部品だけをピンポイントで足せる CLI が有効だった
そして一番の教訓はこれです。
複数アカウント環境では、作業の最初に aws sts get-caller-identity を打つ。
エラーに 2 回怒られ、空のレコーダーに悩まされましたが、終わってみれば「今どこにいるかをまず確かめる」という基本と、「コンソールは全体像の把握に、CLI は状態確認と復旧に」という使い分けを体で覚えられたトラブルでした。