2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AdministratorAccessが使えない環境でCDKをbootstrapする

2
Last updated at Posted at 2026-05-06

はじめに

こんにちは!CDKが好きな社会人二年目のエンジニアです。
皆さん AWS CDK (以下 CDK と呼びます)を普段使っていますでしょうか?

CDK は CloudFormation の難しい部分を開発者向けに抽象化してくれている Developer Tool ですが、その「抽象化」ゆえに、開発者が意識しないところで社内規則に反するリソースが裏側で作られてしまうことがあります。
その一例が、環境構築の際に実行する cdk bootstrap です。

環境構築時、cdk bootstrap を行うと裏で CDK がさまざまなリソースを作成しますが、その中の一つに AdministratorAccess 権限を持つロールが作成されます。
ただし、エンタープライズ環境や厳格な社内ルールが存在する環境では、この AdministratorAccess の利用自体が禁止・非推奨とされているケースも少なくありません。

  • セキュリティポリシーで AdministratorAccess の付与が禁止されている
  • 強い権限を持つ IAM Role に対して Permission Boundary のアタッチが必須化されている
  • SCP で特定のアクションがそもそも禁止されている

このような制約下でも CDK を導入したいというケースに向けて、本記事では AdministratorAccess の作成ができない、もしくは非推奨となっている環境で cdk bootstrap を成功させるための 3 つのアプローチ(実行ロールのポリシー指定、Permission Boundary の活用、テンプレートのカスタマイズ)について解説していきます。

対象読者と前提知識

本記事は以下のような方を対象としています。

  • CDK の基本的な使い方を理解している
  • IAM の Role、Policy、Permission Boundary の概念を把握している
  • 社内の権限制約のなかで CDK を導入しようとしている

当記事の内容は、2026年4月末の情報に基づいて記載しています。
最新情報は公式ドキュメントをご参照ください。

cdk bootstrap で何が作成されるのか

cdk bootstrap は、CDK アプリケーションをデプロイするために必要なリソースを、対象の AWS アカウント・リージョンにあらかじめ用意するコマンドです。

公式ドキュメントでも触れられている通り、cdk bootstrap を実行すると実際に以下のリソースが作成されます。

  • S3 バケット
    Lambda の関数コードやアセットなどの CDK プロジェクトファイルを保存するために使用されます。
  • ECR リポジトリ
    主に Docker イメージの保存に使用されます。
  • 5 つの IAM Role
    CDK のデプロイに利用されます。

このうち、特に重要なのが 5 つの IAM Role です。これらの Role についても公式ドキュメントで解説されており、実際に以下の IAM Role が作成されます。

  • CloudFormationExecutionRole
    CloudFormation が assume する Role
    cdk deploy でリソースを実際に作成・更新・削除するときに使われます。CDK アプリで作るあらゆるリソース(Lambda、DynamoDB、IAM Role など)の操作権限が必要なため、デフォルトでは AdministratorAccess がアタッチされます。本記事の主役です。

  • DeploymentActionRole
    CDK CLI が assume する Role
    cdk deploy の起点となる Role で、CloudFormation スタックの作成・更新や、CloudFormationExecutionRole を CloudFormation に渡す(PassRole)役割を担います。

  • FilePublishingRole
    CDK CLI が assume する Role
    Lambda のコード ZIP やアセットファイルを、Bootstrap で作られた S3 バケットにアップロードする際に使われます。権限は対象 S3 バケットへの操作に絞られています。

  • ImagePublishingRole
    CDK CLI が assume する Role
    コンテナイメージアセット(Docker イメージ)を、Bootstrap で作られた ECR リポジトリに Push する際に使われます。権限は対象 ECR リポジトリへの操作に絞られています。

  • LookupRole
    CDK CLI が assume する Role
    Vpc.fromLookup() などで既存リソースの情報を取得するときや、cdk diffなどの読み取り専用のスタック操作に使われます。読み取り専用の権限のみを持ちます。

上記の通り、5 つの IAM Role のうち、AdministratorAccess がアタッチされるのは CloudFormationExecutionRole だけです。残り 4 つは、それぞれの責務に応じた絞られた権限を持っています。
ではなぜ CloudFormationExecutionRole にだけ AdministratorAccess が必要なのでしょうか。答えはシンプルで、CDK アプリがどのような AWS リソースを作るか事前に分からないからです。

CDK で書かれたアプリは、Lambda、DynamoDB、VPC、IAM Role、KMS Key など、あらゆる AWS リソースを作成しうる存在です。CloudFormationExecutionRole は実際にそれらのリソースを作成する Role なので、「将来作られるかもしれない、すべてのリソース」に対する権限を持っている必要があります。

逆に言えば、CDK アプリで作るリソースの種類が限定されているなら、CloudFormationExecutionRole の権限はそれに合わせて絞れるということになります。これが本記事で話したいことの出発点になります。

CDK の開発において CloudFormationExecutionRole から AdministratorAccess を外し、権限を絞ることが常にベストプラクティスというわけではありません。

  • CDK アプリで作成しうるリソースは多岐にわたり、事前にすべてを予測することが難しい
  • 権限不足によるデプロイ失敗が頻発すると、開発者体験が著しく損なわれる
  • Bootstrap で作成される Role は CloudFormation や CDK CLI からのみ assume されるため、人間が直接悪用するリスクは比較的低い

これらの理由から、本記事で紹介するアプローチは「開発体験の最大化」を目的としたものではなく、あくまで社内ルールに従う必要がある場合の現実的な選択肢としてお読みください。

よくある社内ルールでの制約

実際の現場で cdk bootstrap がブロックされるパターンはさまざまですが、概ね以下のいずれかに分類できるのではないでしょうか。

  • AdministratorAccess の付与自体が禁止されている
    社内ルールとして、SCPやIAMガードレールにて「"*"を許可するポリシーは作成、アタッチ不可」と設定されているケースです。
  • 強い権限を持つ IAM Role にPermission Boundary のアタッチが必須化されている
    AdministratorAccessなどの強い権限の IAM Role に対して、特定の Permission Boundary をアタッチすることが SCP で強制されているケースです。このルールが設定されている場合、Bootstrap が作る IAM Role にも当然これが適用されます。
  • 特定 Action や特定リソースの作成自体が禁止されている
    例えば「iam:CreateUser は組織全体で禁止」「特定のリージョンでの操作は禁止」などが設定されているケースです。

cdk bootstrapにはこれらの制約に対応するための仕組みが用意されています。
以下に、代表的な3つのアプローチを示します。

アプローチ 概要 主な用途
--cloudformation-execution-policies CloudFormationExecutionRole にアタッチするマネージドポリシーを指定 AdministratorAccess の付与禁止対応
--custom-permissions-boundary Bootstrap が作る CloudFormationExecutionRole に Permission Boundary を付与 Permission Boundary 必須対応
Bootstrap テンプレートのカスタマイズ テンプレート自体を取得・修正してデプロイ 上記で解決できない複雑な制約

以下それぞれ詳しく見ていきます。

対策1 - --cloudformation-execution-policies で権限を絞る

最もシンプルで、AdministratorAccess の付与禁止というシナリオに直接対応する方法です。

$ cdk bootstrap --cloudformation-execution-policies "arn:aws:iam::aws:policy/AWSLambda_FullAccess,arn:aws:iam::aws:policy/AWSCodeDeployFullAccess"

--cloudformation-execution-policies に、CloudFormationExecutionRole へアタッチしたいポリシーの ARN をカンマ区切りで指定します。
これにより、デフォルトの AdministratorAccess の代わりに、指定したポリシーがアタッチされた状態で Bootstrap されます。

ポリシーの選び方には大きく 2 つの方向性があります。

ポリシーの選び方

カスタマー管理ポリシーの自作

最小権限を目指す場合、カスタマー管理ポリシーを自作し必要な権限を追加する必要があります。
S3バケットを作成する場合を例に、作成する管理ポリシーを具体的に見ていきましょう。

例1 - シンプルな S3 バケットを作成する

まず、最小構成の S3 バケットを CDK で作成してみます。

const bucket = new s3.Bucket(this, 'TestBucket', {
  removalPolicy: cdk.RemovalPolicy.DESTROY
});

直感的には s3:CreateBucket だけあれば足りそうですが、この権限のみをCloudFormationExecutionRoleが使用するポリシーに指定して実際にデプロイすると以下のエラーが返ってきます。

is not authorized to perform: ssm:GetParameters on resource

これは、CloudFormation が CDK Bootstrap のバージョンを SSM Parameter Store から確認するために発生するエラーです。S3 の権限だけでなく、SSM の ssm:GetParameters も必要になります。

最終的に、シンプルなバケットの作成には以下のポリシーが必要でした。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:CreateBucket",
        "ssm:GetParameters"
      ],
      "Resource": "*"
    }
  ]
}

例2 - S3 バケットの設定を増やす

次に、実運用で一般的な設定(バージョニング、暗号化、パブリックアクセスブロック)を追加してみます。

const bucket = new s3.Bucket(this, 'TestBucket', {
  removalPolicy: cdk.RemovalPolicy.DESTROY,
  versioned: true,
  encryption: s3.BucketEncryption.S3_MANAGED,
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});

たった 3 行追加しただけですが、それぞれの設定に対応する Action が新たに必要になります。

追加した設定 不足していた権限
encryption: S3_MANAGED s3:PutEncryptionConfiguration
blockPublicAccess: BLOCK_ALL s3:PutBucketPublicAccessBlock
versioned: true s3:PutBucketVersioning

S3 の場合、設定項目ごとに独立した Action が対応しているため、設定する項目の数だけ権限が増える構造になっています。

例3 - Lambda と S3 イベント通知を組み合わせる

さらに実践的な構成として、S3 へのオブジェクト Put をトリガーに Lambda を起動する構成を追加してみます。すると、必要な権限はさらに多くなります。

不足していた権限 用途
iam:CreateRole Lambda 実行ロールの作成
iam:AttachRolePolicy 実行ロールへのマネージドポリシーアタッチ
iam:GetRole, iam:GetRolePolicy ロール情報の取得(Lambda 関数作成時に参照)
lambda:CreateFunction, iam:PutRolePolicy Lambda 関数の作成、インラインポリシーの設定
iam:PassRole Lambda に実行ロールを渡す
s3:GetObject Lambda コード ZIP を Bootstrap バケットから取得
lambda:GetFunction 関数作成後の状態確認
lambda:AddPermission S3 から Lambda を呼び出す権限の付与
logs:CreateLogGroup CloudWatch Logs グループの作成
logs:DeleteLogGroup 既存ロググループの削除(CDK による採用処理)
lambda:InvokeFunction S3 バケット通知設定用カスタムリソースの実行
logs:DescribeLogGroups, logs:PutRetentionPolicy ロググループの存在確認・保持期間設定

ロールバック時にも権限が必要

権限不足でデプロイが失敗すると、CloudFormation は自動的にロールバックを試みます。このロールバック処理でも CloudFormationExecutionRole が使われるため、作成時とは別の Action が必要になる場合があります
例えば iam:CreateRole の権限だけでは、ロールバック時の iam:DeleteRoleiam:DetachRolePolicy が不足してロールバック自体も失敗します(ROLLBACK_FAILED 状態)。権限を追加する際は、ロールバック時に必要な Action も合わせて考慮する必要があります。

ロールバック時に必要な権限も含めて、最終的に以下のポリシーでデプロイが完了しました。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iam:AttachRolePolicy",
        "iam:CreateRole",
        "iam:DeleteRole",
        "iam:DeleteRolePolicy",
        "iam:DetachRolePolicy",
        "iam:GetRole",
        "iam:GetRolePolicy",
        "iam:PassRole",
        "iam:PutRolePolicy",
        "lambda:AddPermission",
        "lambda:CreateFunction",
        "lambda:DeleteFunction",
        "lambda:GetFunction",
        "lambda:InvokeFunction",
        "lambda:RemovePermission",
        "logs:CreateLogGroup",
        "logs:DeleteLogGroup",
        "logs:DescribeLogGroups",
        "logs:PutRetentionPolicy",
        "s3:CreateBucket",
        "s3:GetObject",
        "s3:PutBucketPublicAccessBlock",
        "s3:PutBucketVersioning",
        "s3:PutEncryptionConfiguration",
        "ssm:GetParameters"
      ],
      "Resource": "*"
    }
  ]
}

本記事では簡略化のため Resource: "*" としていますが、本来はリソース ARN を特定のバケット・関数・ロールに絞ることで、より厳密な最小権限を実現できます。

なお、上記は筆者の検証構成でデプロイが完了した例であり、CDK のバージョンやスタックの状態によっては追加の Action が必要になる場合があります。

ここまで見てきたとおり、「シンプルな S3 バケット 1 つ」から「Lambda + S3 イベント通知」に拡張するだけで、必要な Action は 2 つから 25 個以上に膨れ上がります。実際のアーキテクチャでは、VPC、ECS、RDS、API Gateway など、より多くのサービスが絡みます。すべてを最小権限で管理しようとすると、ポリシーのメンテナンスが次第に難しくなっていきます。

そこで現実的な落としどころとして、次のアプローチが選択肢に入ってきます。

サービス単位のマネージドポリシーを組み合わせ、絞れるところは絞る

厳密な最小権限の維持が難しい場合、より現実的なアプローチとして「マネージドポリシーを組み合わせて土台を作り、明確に絞り込める対象だけ個別に絞る」という方針が有効です。

このアプローチの基本方針は次の通りです。

  • CDK アプリで使う各サービスは、AWS 管理のマネージドポリシー(AmazonS3FullAccess 等)を組み合わせて土台にする
  • ARN や Condition で明確に絞り込める対象については、カスタムポリシーで個別に置き換える

まず、CDK アプリで使う各サービスに対応するマネージドポリシーを --cloudformation-execution-policies に列挙します。

cdk bootstrap \
  --cloudformation-execution-policies \
    "arn:aws:iam::aws:policy/AmazonS3FullAccess,\
     arn:aws:iam::aws:policy/AWSLambda_FullAccess,\
     arn:aws:iam::aws:policy/CloudWatchLogsFullAccess,\
     arn:aws:iam::<account-id>:policy/MyCdkCustomPolicy"

ここにおける MyCdkCustomPolicy は、後述する絞り込みが可能な対象(SSM、IAM)をまとめたカスタマー管理ポリシーです。IAMFullAccessAmazonSSMFullAccess をそのまま使う代わりに、これらは個別に絞ったポリシーで置き換えます。

SSM はこの方針における最も絞りやすい対象です。CDK Bootstrap が必要とする SSM 操作は「/cdk-bootstrap/<qualifier>/* 配下のパラメータを読み取る」だけであり、Action・Resource ともに明確に特定できます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ssm:GetParameters",
      "Resource": "arn:aws:ssm:<region>:<account-id>:parameter/cdk-bootstrap/<qualifier>/*"
    }
  ]
}

IAMは他の権限を作成することができる権限であるため、この方針において最も絞る価値が高い対象です。
絞り込みの基本は、CDK アプリが作る Role の ARN パターンに限定することです。
CloudFormation 経由で作られる Role の名前にはスタック名がプレフィックスとして含まれるため、組織でスタック名の命名規則を統一していれば、これを利用して絞り込めます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ManageRolesForCdkStack",
      "Effect": "Allow",
      "Action": [
        "iam:CreateRole",
        "iam:DeleteRole",
        "iam:GetRole",
        "iam:UpdateAssumeRolePolicy",
        "iam:CreatePolicy",
        "iam:PutRolePolicy",
        "iam:DeleteRolePolicy",
        "iam:GetRolePolicy",
        "iam:TagRole",
        "iam:UntagRole"
      ],
      "Resource": "arn:aws:iam::<account-id>:role/MyApp-*"
    },
    {
      "Sid": "AttachOnlyAllowedManagedPolicies",
      "Effect": "Allow",
      "Action": [
        "iam:AttachRolePolicy",
        "iam:DetachRolePolicy"
      ],
      "Resource": "arn:aws:iam::<account-id>:role/MyApp-*",
      "Condition": {
        "ArnLike": {
          "iam:PolicyARN": [
            "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
          ]
        }
      }
    },
    {
      "Sid": "PassRoleToLambdaOnly",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::<account-id>:role/MyApp-*",
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": "lambda.amazonaws.com"
        }
      }
    }
  ]
}

完全な最小権限ではないものの、運用可能な範囲で絞れるところまで絞るという現実的な落としどころとして、社内ルールが厳しい多くの現場で採用しやすい構成です。

対策2 - --custom-permissions-boundaryCloudFormationExecutionRole に境界を設ける

AdministratorAccess をアタッチするが、Permission Boundaryを用いてDenyガードレールを引くアプローチです。
--custom-permissions-boundary オプションに、あらかじめ作成しておいた IAM Managed Policy の名前を渡します。

# 事前に Permission Boundary 用ポリシーを作成しておく
aws iam create-policy \
  --policy-name CdkBootstrapPermissionBoundary \
  --policy-document file://permission-boundary.json

# Boundary を指定して bootstrap
cdk bootstrap --custom-permissions-boundary CdkBootstrapPermissionBoundary

対策1(--cloudformation-execution-policies)は ARN を指定するのに対し、こちらは ポリシー名 を指定します。

Permission Boundary ポリシーの選び方

対策1(--cloudformation-execution-policies)との違いは、権限の指定方向です。

対策1 対策2
方向 必要なものを Allow 禁じるものを Deny
CloudFormationExecutionRole の identity policy 絞り込んだポリシー AdministratorAccess のまま
メンテコスト CDK アプリの変更のたびに修正 組織のガードレールとして機能

Permission Boundary を最小権限の Allow リストで書こうとすると対策1と同じ課題が生じるため、Denyベースのガードレールを作成します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAll",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    },
    {
      "Sid": "DenyDangerousActions",
      "Effect": "Deny",
      "Action": [
        "iam:CreateUser",
        "iam:CreateVirtualMFADevice",
        "organizations:LeaveOrganization",
        "account:CloseAccount",
        "cloudtrail:DeleteTrail",
        "cloudtrail:StopLogging",
        "guardduty:DeleteDetector",
        "config:DeleteConfigurationRecorder",
        "config:DeleteDeliveryChannel"
      ],
      "Resource": "*"
    }
  ]
}

CloudFormationExecutionRole の identity policy は AdministratorAccess のままで、実効権限を絞ることができます。

Boundary が適用される範囲

実際に bootstrap を実行してみると、CLI には以下のようなメッセージが表示されます。

Adding new permissions boundary CdkBootstrapPermissionBoundary

bootstrap 後、作成された 5 つの IAM Role に対して PermissionsBoundary の有無を確認すると、--custom-permissions-boundary が Boundary をアタッチするのは CloudFormationExecutionRole のみであることだけがわかります。

Bootstrap テンプレート(後述)を確認すると、PermissionsBoundary プロパティが記述されているのは CloudFormationExecutionRole のみとなっているためです。

CloudFormationExecutionRole:
  Type: AWS::IAM::Role
  Properties:
    # ...
    PermissionsBoundary:
      Fn::If:
        - PermissionsBoundarySet          # --custom-permissions-boundary が指定されているか
        - Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/${InputPermissionsBoundary}
        - Ref: AWS::NoValue

他の 4 ロールには同等の記述がないため、Boundary は付与されません。
他の IAM Role にも Boundary を付与したい場合、次に説明する対策3のアプローチを行う必要があります。

対策3 - Bootstrap テンプレートのカスタマイズ

これまで述べてきた対策1・2は cdk bootstrap のフラグでテンプレートのパラメータを外部から注入するアプローチでした。
対策3としては、 Bootstrap テンプレートについて解説しつつ、リソース定義そのものを書き換えていく方針を見ていきます。

Bootstrap テンプレートのカスタマイズが必要になる代表的なケースは以下のとおりです。

  • CloudFormationExecutionRole だけでなく、5 つすべての IAM ロールに Permission Boundary を付けたい(対策2の応用)
  • Bootstrap リソースの設計そのものを組織のポリシーに合わせて管理したい

まず、テンプレートのカスタマイズを理解するのに必要な3つの概念(Bootstrap テンプレート、Synthesizer、Bootstrap Contract)を見ていきます。

Bootstrap テンプレートとは

cdk bootstrap が AWS 環境にデプロイする CloudFormation テンプレートです(スタック名: CDKToolkit)。
以下のコマンドでその内容を YAML ファイルとして取り出せます。

npx cdk bootstrap --show-template > bootstrap-template.yaml

テンプレートでは、以下のリソースを定義されています。

リソース論理 ID タイプ 説明
FileAssetsBucketEncryptionKey AWS::KMS::Key S3 バケット暗号化用 KMS キー(CreateNewKey 条件が true のときのみ)
FileAssetsBucketEncryptionKeyAlias AWS::KMS::Alias 上記 KMS キーのエイリアス
StagingBucket AWS::S3::Bucket Lambda コードなどのファイルアセット保存用 S3 バケット
StagingBucketPolicy AWS::S3::BucketPolicy バケットポリシー
ContainerAssetsRepository AWS::ECR::Repository Docker イメージ保存用 ECR リポジトリ
FilePublishingRole AWS::IAM::Role CDK CLI がファイルアセットを S3 にアップロードするときに AssumeRole するロール
ImagePublishingRole AWS::IAM::Role CDK CLI が Docker イメージを ECR にプッシュするときに AssumeRole するロール
LookupRole AWS::IAM::Role cdk synth 時のコンテキストルックアップ(Availability Zone 一覧取得など)に使うロール
FilePublishingRoleDefaultPolicy AWS::IAM::Policy FilePublishingRole にアタッチするポリシー
ImagePublishingRoleDefaultPolicy AWS::IAM::Policy ImagePublishingRole にアタッチするポリシー
DeploymentActionRole AWS::IAM::Role CDK CLI が cloudformation:CreateChangeSet などを呼ぶときに AssumeRole するロール
CloudFormationExecutionRole AWS::IAM::Role CloudFormation がスタックリソースを実際に作成・変更するときに使うロール
CdkBoostrapPermissionsBoundaryPolicy AWS::IAM::ManagedPolicy --example-permissions-boundary 指定時に作成されるサンプル用 Permission Boundary ポリシー
CdkBootstrapVersion AWS::SSM::Parameter Bootstrap テンプレートのバージョン番号を記録する SSM パラメータ

対策1・2との関係

対策1(--cloudformation-execution-policies)と対策2(--custom-permissions-boundary)は、このテンプレートの Parameters ブロックにある値をフラグ経由で注入するアプローチであり、テンプレートのリソース定義自体は変えていません。対策3では Resources 定義を直接編集します。

Synthesizer とは

CDK アプリを cdk deploy で動かすには、大きく 3 つの処理が必要です。

  1. CDK のコードを CloudFormation テンプレート(JSON)に変換する
  2. Lambda のコードなどのファイルアセットを S3 にアップロードする
  3. CloudFormation テンプレートをデプロイする

Synthesizer は 1 と 2 の仕様を決定するコンポーネントです。
どの S3 バケットを使うか、どの IAM ロールを AssumeRole するか、生成される CloudFormation テンプレートに何を埋め込むかを制御します。

Synthesizerには、レガシーなものを除いて2つの種類があります。

DefaultStackSynthesizer(デフォルト)

cdk synth を実行すると、Synthesizer は cdk.out/ 以下に 2 種類のファイルを生成します。

1 CloudFormation テンプレート(cdk.out/CdkBootstrapTestStack.template.json

テンプレートには以下が埋め込まれます。

"Parameters": {
  "BootstrapVersion": {
    "Type": "AWS::SSM::Parameter::Value<String>",
    "Default": "/cdk-bootstrap/<qualifier>/version"
  }
},
"Rules": {
  "CheckBootstrapVersion": {
    "Assertions": [{
      "Assert": {
        "Fn::Not": [{ "Fn::Contains": [["1","2","3","4","5"], {"Ref": "BootstrapVersion"}] }]
      },
      "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
    }]
  }
}

Parameters.BootstrapVersion の型が AWS::SSM::Parameter::Value<String> になっていますが、これはCloudFormation がスタックのデプロイ時に自動的に SSM パラメータの値を解決する型となります。
CloudFormation は /cdk-bootstrap/<qualifier>/version に記録されているバージョン番号を読み取り、その値に対して Rules.CheckBootstrapVersion のアサーションを実行します。バージョンが 1〜5( CDK v1 時代の Bootstrapで利用されていた値)であればデプロイを拒否します。

Lambda コードが格納される S3 バケットの参照もテンプレートに埋め込まれます。

"Code": {
  "S3Bucket": {
    "Fn::Sub": "cdk-<qualifier>-assets-${AWS::AccountId}-${AWS::Region}"
  }
}

2 アセットマニフェスト(cdk.out/CdkBootstrapTestStack.assets.json

ファイルアセットのアップロード方法を記述するマニフェストです。

"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-<qualifier>-file-publishing-role-${AWS::AccountId}-${AWS::Region}",
"bucketName": "cdk-<qualifier>-assets-${AWS::AccountId}-${AWS::Region}"

cdk deploy を実行すると、CDK CLI はこのマニフェストを読んで cdk-<qualifier>-file-publishing-role-... を AssumeRole し、Lambda コードを cdk-<qualifier>-assets-... バケットにアップロードします。アップロード完了後、今度は cdk-<qualifier>-deploy-role-... を AssumeRole して CloudFormation の API を呼び出します。

qualifier がバケット名・ロール名・SSM パラメータパスの全てに埋め込まれています。これが後述する Bootstrap Contract の重要な要素になります。

CliCredentialsStackSynthesizer

デフォルトで使用される DefaultStackSynthesizer の他に、CliCredentialsStackSynthesizer という Synthesizer があります。

同じプロジェクトに対して Synthesizer を切り替えると、生成物が変わります。

new CdkBootstrapTestStack(app, 'CdkBootstrapTestStack', {
  synthesizer: new cdk.CliCredentialsStackSynthesizer({ qualifier: '<qualifier>' })
});

このまま cdk synth を実行すると、生成された CloudFormation テンプレートから Parameters.BootstrapVersionRules.CheckBootstrapVersion が消えます。

-"Parameters": {
-  "BootstrapVersion": {
-    "Type": "AWS::SSM::Parameter::Value<String>",
-    "Default": "/cdk-bootstrap/<qualifier>/version"
-  }
-},
-"Rules": {
-  "CheckBootstrapVersion": {
-    "Assertions": [{ "AssertDescription": "CDK bootstrap stack version 6 required..." }]
-  }
-}

また assets.jsonassumeRoleArn も変わります。

DefaultStackSynthesizer CliCredentialsStackSynthesizer
assumeRoleArn(アップロード用) cdk-<qualifier>-file-publishing-role-... null
bucketName cdk-<qualifier>-assets-... cdk-<qualifier>-assets-...(同じ)

assumeRoleArnnull になったため、CDK CLI はアセットのアップロード時に IAM ロールを AssumeRole しません。aws login などで取得した現在のセッション認証情報をそのまま使って S3 に書き込みます。CloudFormation の呼び出しについても同様で、deploy-role を AssumeRole せず、現在の認証情報でそのまま呼び出します。

S3 バケット名には引き続き qualifier が使われるため、バケット自体は Bootstrap が作る必要がありますが、5つのIAM Roleが不要になります。

IAM Role を完全に制御したい場合、CliCredentialsStackSynthesizerを使用することも方法の一つですが、デフォルトの DefaultStackSynthesizer と比較した際の制約を理解しておく必要があります。

CliCredentialsStackSynthesizer の制約

  • IAM ロールの AssumeRole を行わないため、cross-account デプロイには対応できません
  • CDK Pipelines は内部で deploy-role への AssumeRole を前提としているため、CDK Pipelines とは一緒に使えません
  • 実行者の IAM 権限が S3 書き込み・CloudFormation 操作を含むすべての操作をカバーする必要があります

Bootstrap Contract とは

Bootstrap Contract は、Synthesizer が生成する成果物(テンプレート・assets.json)に埋め込まれた Bootstrap リソースへの参照が、実際に AWS 上に存在し、かつアクセス可能でなければならないという前提条件を指します。

公式ドキュメントには、以下のように書かれています。

CDK アプリケーションを適切にデプロイするには、合成中に生成される CloudFormation テンプレートが、ブートストラップ中に作成されるリソースを正しく指定している必要があります。これらのリソースは、一般的にブートストラップリソースと呼ばれます。ブートストラップは、デプロイの実行とアプリケーションアセットの管理に AWS CDK が使用するリソースを AWS 環境に作成します。合成は、アプリケーション内の各 CDK スタックから CloudFormation テンプレートを生成します。これらのテンプレートは、アプリケーションからプロビジョニングされる AWS リソースを定義するだけではありません。これらは、デプロイ中に使用するブートストラップリソースも指定します。

cdk deploy の実行フローに沿って、何が要求されているかを順番に確認します。

1.アセットアップロード時の要求

CDK CLI は assets.jsonassumeRoleArn に記載されたロール(cdk-<qualifier>-file-publishing-role-...)を AssumeRole して S3 にアップロードします。

  • 要求: cdk-<qualifier>-file-publishing-role-{accountId}-{region} という名前の IAM ロールが存在すること
  • Bootstrap が満たす方法: FilePublishingRole リソースをこの命名規則で作成する

2.CloudFormation 呼び出し時の要求

アップロード完了後、CDK CLI は cdk-<qualifier>-deploy-role-... を AssumeRole して cloudformation:CreateChangeSet などを呼び出します。

  • 要求: cdk-<qualifier>-deploy-role-{accountId}-{region} という名前の IAM ロールが存在すること
  • Bootstrap が満たす方法: DeploymentActionRole リソースをこの命名規則で作成する

3.Lambda コード参照の解決

CloudFormation テンプレートの Code.S3Bucket には cdk-<qualifier>-assets-{accountId}-{region} が書かれています。CloudFormation がスタックを作成するとき、Lambda リソースのコードを読みに行くバケットとして使います。

  • 要求: cdk-<qualifier>-assets-{accountId}-{region} という名前の S3 バケットが存在すること(かつその中に対象の zip ファイルが存在すること)
  • Bootstrap が満たす方法: StagingBucket リソースをこの命名規則で作成する。ファイル自体は CDK CLI が 1 のアップロードで事前に配置する

4.SSM パラメータの解決と権限

CloudFormation テンプレートには Parameters.BootstrapVersion が型 AWS::SSM::Parameter::Value<String> として定義されています。この型を CloudFormation が処理するとき、スタックのデプロイ処理の中で CloudFormation 自身が SSM の GetParameters API を呼んでパラメータの値を取得します。この API 呼び出しは CloudFormationExecutionRole の権限で行われます。

  • 要求: /cdk-bootstrap/<qualifier>/version という SSM パラメータが存在すること
  • Bootstrap が満たす方法: CdkBootstrapVersion リソースをこのパス・qualifier で作成する

qualifier が Bootstrap Contract の橋渡しになっている

qualifier とは、同一アカウント・リージョン内に複数の Bootstrap 環境を共存させるための識別子です。デフォルト値は hnb659fds(CDK が自動生成した固定文字列)ですが、cdk bootstrap --qualifier myqualifier で任意の値に変更できます。また、cdk.json@aws-cdk/core:bootstrapQualifier に設定するとアプリ全体に適用されます。

qualifier の主な役割は Bootstrap リソースの命名の衝突を防ぐことです。同じアカウント内で複数チームが独立した Bootstrap 環境を持つ場合、それぞれ異なる qualifier を使うことでリソース名が重複しません。

Synthesizer が生成する成果物に埋め込まれるリソース名は、すべて qualifier を軸に組み立てられます。Bootstrap テンプレートもまったく同じ qualifier を使って同名のリソースを作ります。この命名が一致することが、Bootstrap Contract が成立するということとイコールになります。

カスタムテンプレートの作成

では実際に、カスタムテンプレートを作成していきます。

テンプレートの取得

以下のコマンドで、実際のテンプレートをyamlファイルで落としてくることができます。

npx cdk bootstrap --show-template > bootstrap-template.yaml

このファイルをベースに変更を加えていきます。

まずBootstrapVariantを修正する

Bootstrap テンプレートには BootstrapVariant というパラメータがあります。

BootstrapVariant:
  Type: String
  Default: "AWS CDK: Default Resources"
  Description: Describe the provenance of the resources in this bootstrap stack.
    Change this when you customize the template. To prevent accidents, the CDK CLI
    will not overwrite bootstrap stacks with a different variant.

cdk bootstrap が CDKToolkit スタックをデプロイするとき、この BootstrapVariantDefault 値が CloudFormation スタックの Parameters として記録されます。

cdk bootstrap --template custom.yaml を実行するとき、CDK CLI は以下の手順で処理します。

  1. 現在 AWS にデプロイされている CDKToolkit スタックの BootstrapVariant パラメータ値を読む
  2. 新しいテンプレート(custom.yaml)の Parameters.BootstrapVariant.Default 値を読む
  3. 両者が異なり、かつ新しいテンプレートの値が "AWS CDK: Default Resources" 以外であれば、デプロイをブロックして --force を求める

カスタムテンプレートの BootstrapVariant.Default"AWS CDK: Default Resources" のままにしてデプロイすると、チームの誰かが cdk bootstrap(--template なし)を実行したとき、両者の BootstrapVariant が一致してしまうため CDK CLI は上書きを許可してしまい、カスタマイズがデフォルトのテンプレートで上書きされます。

BootstrapVariant を独自の値に変更しておくことで、少なくとも意図しないカスタムテンプレートへの上書きを --force オプションなしではできなくなります。

-Default: "AWS CDK: Default Resources"
+Default: "AWS CDK: Custom Secured Bootstrap"

cdk bootstrapで作成される、全 5 つ IAM Role への Permission Boundary 追加

対策2で確認したとおり、デフォルトの Bootstrap テンプレートには CloudFormationExecutionRole にしか PermissionsBoundary プロパティが記述されていません。

CloudFormationExecutionRole:
  Type: AWS::IAM::Role
  Properties:
    ...
    PermissionsBoundary:              # これは CloudFormationExecutionRole のみ
      Fn::If:
        - PermissionsBoundarySet
        - Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/${InputPermissionsBoundary}
        - Ref: AWS::NoValue

FilePublishingRoleImagePublishingRoleLookupRoleDeploymentActionRole の 4 ロールには同じ記述がないため、--custom-permissions-boundary を指定しても Boundary が付きません。

テンプレートをカスタマイズして、4 ロールの Properties ブロックにも同じ記述を追加します。

FilePublishingRole:
  Type: AWS::IAM::Role
  Properties:
    ...
    RoleName:
      Fn::Sub: cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}
    PermissionsBoundary:              # 追加
      Fn::If:
        - PermissionsBoundarySet
        - Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/${InputPermissionsBoundary}
        - Ref: AWS::NoValue

上記 2 つの変更を加えたテンプレートを custom-bootstrap.yaml として保存してデプロイします。

npx cdk bootstrap \
  --template custom-bootstrap.yaml \
  --qualifier <qualifier> \
  --custom-permissions-boundary CdkBootstrapPermissionBoundary \
  --force

デプロイが完了し、実際に 5 つの IAM Role すべてに CdkBootstrapPermissionBoundary が Permission Boundary として付いていることが確認できます。

まとめ

CDK には多くの選択肢が用意されており、その組織ごとのルールに準拠した環境構築を行うことができます。

当記事の設定を行わずに開発できることが一番ですが、社内ルールで CDK の利用を諦める前に是非試していただけると幸いです。

最後に、ここまで読んでくださった皆様、誠にありがとうございました!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?