結論: 信頼ポリシーは参照先ロールの実在をチェックする

先に結論を書きます。

CloudFormationでクロスアカウントのAssumeRole構成を組むとき、監視対象アカウント側の信頼ポリシー(Trust Policy)は、参照先のIAMロールARNが実在するかどうかを検証します。つまり、AssumeRole元のロールが存在しない状態でスタックを作ると、Invalid principal in policy で弾かれます。

正しい作成順序はこうです。

graph TD
    A[1. CIアカウントのIAMロールを作成/更新] --> B[2. ロールARNを確認]
    B --> C[3. 監視対象アカウントAにクロスアカウントロールを作成]
    B --> D[3. 監視対象アカウントBにクロスアカウントロールを作成]
    B --> E[3. 監視対象アカウントCにクロスアカウントロールを作成]
    C --> F[4. CI側のAssumeRole権限に各ロールARNを追加]
    D --> F
    E --> F

「当たり前じゃないか」と思うかもしれません。ただ、複数アカウントを行き来しながら作業していると、この順序を見失ってハマることがあります。今回の自分がまさにそうでした。

前提: やりたかったこと

CIサーバー(Jenkins)から、複数のAWSアカウントのリソースを監視する構成を作りたかった、というのが背景です。

[CIアカウント]                    [監視対象アカウント A, B, C]
  Jenkins Agent                     監視用ロール
  (IAMロール)  --- AssumeRole --->  (クロスアカウントロール)

ここで「AssumeRole」と「信頼ポリシー」について補足しておきます。

AWSでは、あるアカウントのIAMロールが別のアカウントのIAMロールを「引き受ける(AssumeRole)」ことで、クロスアカウントのアクセスを実現します。このとき、引き受けられる側のロールには**「誰からの引き受けを許可するか」を定義する設定が必要です。これが信頼ポリシー(Trust Policy)**です。

つまり、登場人物は2つです。

この2つが揃って初めて、クロスアカウントのAssumeRoleが成立します。自転車の両輪みたいなものです。

CloudFormationテンプレートも2種類用意しました。

  1. CIアカウント側: Jenkinsエージェント用のIAMロール(IAMポリシーでAssumeRole権限を付与)
  2. 監視対象アカウント側: 監視用ロール(信頼ポリシーでCIアカウントのロールARNを指定)

それぞれのアカウントにデプロイすれば完成、のはずでした。

ハマり①: 既存ロールとの名前競合

結論: CloudFormationは同一アカウント・同一リージョン内で、異なるスタックから同名のIAMロールを作れません。

何が起きたか: スタック作成時にロールバックが発生しました。

Resource already exists in stack arn:aws:cloudformation:...

原因: 以前に別の目的で作ったスタックが、同じ名前のIAMロールをすでに持っていました。命名規則が統一されておらず、既存は {用途}-role-{環境} だったり {スタック名}-role-{環境} だったり。ここが曖昧だったことが根本原因です。

対処: ロール名のプレフィックスを変えて回避しました。この時点で「命名規則を標準化しないとまずいな」と気づき、後続の作業で統一することになります。

ハマり②: Invalid principal in policy

結論: 信頼ポリシーに指定したIAMロールARNが存在しないと、スタック作成が失敗します。

何が起きたか: 名前の競合を解消して再デプロイ。今度は別のエラーです。

Invalid principal in policy

原因: CIアカウント側のロール名を新しいものに変更する前に、監視対象アカウント側で新しいロール名を参照する信頼ポリシーを作ろうとしていました。まだ存在しないロールARNを指定していたので弾かれた、ということです。

正直ここで詰まりました。ARNのタイポを疑って何度も見直しましたが、問題はARNの正しさではなくロール自体が存在していなかったことでした。

sequenceDiagram
    participant CI as CIアカウント
    participant Target as 監視対象アカウント

    Note over Target: ❌ 失敗パターン(ロールが未作成)
    Target->>CI: 信頼ポリシーでCIのロールARNを参照
    CI-->>Target: そのロール、まだ存在しません
    Target->>Target: Invalid principal → ロールバック

    Note over CI,Target: ✅ 成功パターン(ロールが先に存在)
    CI->>CI: 1. CIアカウントにエージェントロールを作成
    Target->>CI: 2. 信頼ポリシーでCIのロールARNを参照
    CI-->>Target: ロールが存在する → 検証OK
    Target->>Target: 監視用ロール作成成功

ポイント: ここで気づいた重要な非対称性があります。

つまり、依存の方向が非対称なんです。CI側のポリシーは先に書けるけど、監視対象側の信頼ポリシーはCIのロールが先に存在しないと書けない。ここが今回の作業で一番の学びでした。

おまけ: 25ファイルの一括置換で二重置換が発生した話

ロール名を標準化した結果、そのロール名を参照しているファイルが25個ありました。設定ファイル、Jenkinsfile、Groovyスクリプト、ドキュメントなど。sed で一括置換しましたが、ここにも小さな罠がありました。

置換対象の文字列に新しいプレフィックスの一部が含まれていたため、二重置換が発生しました。

# 一括置換
find path/to/jobs -name "Jenkinsfile" -type f \
  -exec sed -i "s/old-role-name/new-role-name/g" {} \;

# 期待: old-role-name → new-role-name
# 実際: old-role-name → new-new-role-name(二重置換)

対処は力技で、二重置換された文字列をさらに sed で修正しました。

find path/to/jobs -name "Jenkinsfile" -type f \
  -exec sed -i "s/new-new-role-name/new-role-name/g" {} \;

本来は置換前に grep で対象を確認して、完全一致に近いパターンで書くべきでした。急いでいるときほど、こういう地味なところで時間を取られます。

まとめ

今回の学びを整理します。

特に2つ目の非対称性は、ドキュメントをちゃんと読めば書いてあることですが、実際にハマってみないと体感としては入ってこないものだなと思いました。どなたかの参考になれば。

参考ドキュメント

参考書籍

AWSの運用やインフラ自動化について、さらに学びたい方におすすめの書籍です。

[📦 商品リンク: moshimo-book-aws-operations]

[📦 商品リンク: moshimo-book-server-infra]