はじめに AWS CDK(Cloud Development Kit)を使って、異なるAWSアカウント間(クロスアカウント)でリソースをデプロイする方法を解説します。 対象読者 :AWSちょっとわかるレベル 所要時間 :約60〜90分 実行環境 :Windows PowerShell このハンズオンで学べること CDKプロジェクトの初期化とスタック構成 cdk bootstrap の仕組みと役割 クロスアカウントデプロイに必要なIAM設定 --cloudformation-execution-policies による最小権限の実現 cdk destroy を使ったリソースの後片付け アカウント構成 本ハンズオンでは2つのAWSアカウントを使用します。 事前準備 必要なツール Node.js(v18以上推奨) AWS CDK CLI( npm install -g aws-cdk ) AWS CLI v2 PowerShell AWSプロファイルの設定 本ハンズオンでは以下の2つのプロファイルを使用します。 プロファイル名 対象アカウント 用途 test-111111111111 アカウントA(111111111111) デプロイ先の操作 test-A アカウントB(222222222222)のIAMユーザー CDKデプロイの実行元 ~/.aws/credentials および ~/.aws/config に各プロファイルを設定しておいてください。 Step 1:CDKプロジェクトの作成 プロジェクトディレクトリの作成 mkdir C:\github\cdk-cross mkdir C:\github\cdk - cross cd C:\github\cdk-cross cd C:\github\cdk - cross CDKアプリの初期化 cdk init app --language typescript cdk init app -- language typescript 実行すると以下のようなプロジェクト構成が生成されます。 cdk-cross/├── bin/│ └── cdk-cross.ts # エントリーポイント├── lib/│ └── cdk-cross-stack.ts # スタック定義├── cdk.json # CDK設定ファイル└── package.json cdk-cross/ ├── bin/ │ └── cdk-cross.ts # エントリーポイント ├── lib/ │ └── cdk-cross-stack.ts # スタック定義 ├── cdk.json # CDK設定ファイル └── package.json Note : git config の設定がない場合、 Unable to initialize git repository という警告が出ますが、ハンズオンの進行には影響ありません。 Step 2:スタックの実装 エントリーポイントの編集 bin/cdk-cross.ts を以下のように編集します。デプロイ先のアカウントIDとリージョンを明示的に指定します。 import * as cdk from 'aws-cdk-lib';import { CdkCrossStack } from '../lib/cdk-cross-stack';const app = new cdk.App();new CdkCrossStack(app, 'CdkCrossStack', { env: { account: "111111111111", <em>// デプロイ先アカウントA</em> region: "ap-northeast-1" }}); import * as cdk from ' aws-cdk-lib ' ; import { CdkCrossStack } from ' ../lib/cdk-cross-stack ' ; const app = new cdk . App () ; new CdkCrossStack ( app , ' CdkCrossStack ' , { env : { account : " 111111111111 " , <em> // デプロイ先アカウントA</em> region : " ap-northeast-1 " } } ) ; ポイント :クロスアカウントデプロイでは env の account と region を 必ず明示 する必要があります。省略すると CDK が環境を解決できずエラーになります。 スタック定義の編集 lib/cdk-cross-stack.ts を以下のように編集します。今回はシンプルなS3バケットを1つ作成します。 import * as cdk from 'aws-cdk-lib';import { Bucket } from 'aws-cdk-lib/aws-s3';export class CdkCrossStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); new Bucket(this, 'TestBucket', { removalPolicy: cdk.RemovalPolicy.DESTROY, autoDeleteObjects: true }); }} import * as cdk from ' aws-cdk-lib ' ; import { Bucket } from ' aws-cdk-lib/aws-s3 ' ; export class CdkCrossStack extends cdk . Stack { constructor ( scope : Construct , id : string , props ?: cdk . StackProps ) { super ( scope , id , props ) ; new Bucket ( this , ' TestBucket ' , { removalPolicy : cdk . RemovalPolicy . DESTROY , autoDeleteObjects : true } ) ; } } removalPolicy: DESTROY : cdk destroy 実行時にバケットを削除する設定 autoDeleteObjects: true :バケット内にオブジェクトが残っていても削除できるようにする設定(ハンズオン向けの設定です。本番環境では慎重に検討してください) CloudFormationテンプレートの確認 実際にデプロイする前に、CDKが生成するCloudFormationテンプレートを確認しましょう。 cdk synth cdk synth S3バケット・バケットポリシー・Lambda関数(autoDeleteObjects用)・IAMロールが出力されれば成功です。 Step 3:bootstrap(1回目) bootstrapとは CDKでデプロイを行うには、事前に対象アカウント・リージョンに CDKToolkit というCloudFormationスタックを作成する必要があります。これを cdk bootstrap と呼びます。 bootstrapのメリット デプロイの自動化 :アセット(Lambda ZIPやDockerイメージ)のアップロード先(S3・ECR)が自動で用意されるため、手動でバケットやリポジトリを作成する必要がない IAMロールの一元管理 :デプロイに必要なIAMロールがまとめて作成・管理されるため、個別にロールを設定する手間が省ける クロスアカウント対応 : --trust オプションで他アカウントからのデプロイを安全に許可できる 権限の最小化 : --cloudformation-execution-policies でCloudFormationが使う権限を絞り込める 冪等性 (何度実行しても同じ結果になる性質):すでにbootstrap済みの環境に再実行しても、差分のみUpdateとして適用されるため安全に再実行できる bootstrapはアカウントとリージョンに紐づく bootstrapは 「AWSアカウント × リージョン」の組み合わせごとに1回実行 する必要があります。 アカウントAの ap-northeast-1 にデプロイ → aws://111111111111/ap-northeast-1 にbootstrap アカウントAの us-east-1 にもデプロイしたい → aws://111111111111/us-east-1 に 別途 bootstrap アカウントBの ap-northeast-1 にもデプロイしたい → aws://222222222222/ap-northeast-1 に 別途 bootstrap 同じアカウントでも リージョンが異なれば別のbootstrapが必要 です。 bootstrapで作成されるリソース bootstrapによって以下のリソースが作成されます。 S3バケット (StagingBucket):デプロイ用アセットの保存場所 ECRリポジトリ :Dockerイメージのアセット保存場所 IAMロール群 :CDKがデプロイ操作を行うための各種ロール DeploymentActionRole :デプロイ操作を担うロール CloudFormationExecutionRole :CloudFormationがリソースを作成する際に使うロール FilePublishingRole / ImagePublishingRole :アセットをS3/ECRにアップロードするロール LookupRole :コンテキスト情報の参照に使うロール 組織のIAMポリシー制限がある場合 組織(AWS Organizations)のSCP(Service Control Policy)やセキュリティポリシーによって、 各種リソースの作成が制限されている環境 では、bootstrapをそのまま実行できないケースがあります。 その場合は以下のいずれかの対応を取ります。 既存のbootstrap済み環境を使う :組織の管理者がすでにCDKToolkitをセットアップしている場合は、そのまま利用する(追加のbootstrapは不要) 別途ロールを手動作成して利用する :セキュリティチームが承認した最小権限のIAMロールを事前に作成し、 --role-arn オプションでCDKに指定する cdk deploy ` --role-arn arn:aws:iam::111111111111:role/MyCustomDeployRole ` --profile test-111111111111 cdk deploy ` -- role - arn arn:aws:iam:: 111111111111 :role / MyCustomDeployRole ` -- profile test-111111111111 アカウントAにbootstrapを実行(1回目) 確認ポイント :デプロイ先アカウントにすでに CDKToolkit という名前のCloudFormationスタックが存在する場合は、bootstrapは 実行済み です。その場合は本Stepをスキップしてください。 まずはシンプルにアカウントAへbootstrapします。 cdk bootstrap aws://111111111111/ap-northeast-1 ` --profile test-111111111111 cdk bootstrap aws: // 111111111111 / ap - northeast - 1 ` -- profile test-111111111111 成功すると ✅ Environment aws://111111111111/ap-northeast-1 bootstrapped. と表示されます。 Step 4:アカウントAへのデプロイ(同一アカウント) まず、アカウントAのプロファイルで直接デプロイできることを確認します。 cdk deploy --profile test-111111111111 cdk deploy -- profile test-111111111111 IAMの変更内容が表示されるので確認し、 y を入力します。 Do you wish to deploy these changes (y/n)? y Do you wish to deploy these changes ( y / n ) ? y ✅ CdkCrossStack と表示されればデプロイ成功です。 Step 5:クロスアカウントデプロイの試行(失敗) 次に、アカウントB( test-A プロファイル)からデプロイを試みます。 cdk deploy --profile test-A cdk deploy -- profile test-A 以下のエラーが発生します。 Could not assume role in target account using current credentials(which are for account 222222222222)User: arn:aws:iam::222222222222:user/test1 is not authorized to perform:sts:AssumeRole on resource:arn:aws:iam::111111111111:role/cdk-hnb659fds-deploy-role-111111111111-ap-northeast-1 Could not assume role in target account using current credentials ( which are for account 222222222222 ) User: arn:aws:iam:: 222222222222 :user / test1 is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam:: 111111111111 :role / cdk - hnb659fds - deploy-role - 111111111111 - ap - northeast - 1 Note :エラーメッセージ中の cdk-hnb659fds はCDK bootstrapが生成するデフォルトの qualifier(識別子) です。 cdk bootstrap --qualifier <任意の値> で変更可能なため、組織の設定によっては異なる文字列になる場合があります。 本ハンズオンではデフォルト値( hnb659fds )を使用します。 なぜ失敗するのか CDKのクロスアカウントデプロイでは、操作元アカウントBのユーザーが、デプロイ先アカウントAの DeploymentActionRole を AssumeRole(一時的に引き受ける) することでデプロイを行います。 しかし現時点では、 アカウントAの DeploymentActionRole がアカウントBからのAssumeRoleを 許可していない アカウントBの test1 ユーザーが sts:AssumeRole を実行する 権限を持っていない この2つを解決する必要があります。 Step 6:最小権限ポリシーの作成 --cloudformation-execution-policies とは cdk bootstrap の --cloudformation-execution-policies オプションは、 CloudFormationがリソースを作成・更新・削除する際に使用するIAMポリシー を指定するものです。 デフォルト(AdministratorAccess)の問題点 指定しない場合は AdministratorAccess (全権限)が使われます。これには以下のリスクがあります。 CDKスタックのコードに誤りがあった場合、 意図しないリソースを削除・変更 してしまう可能性がある 最小権限の原則(Principle of Least Privilege)に反する 組織のセキュリティポリシーに違反するケースがある 最小権限ポリシーを使うべき理由 CloudFormationが操作できるリソースを スタックで必要なものだけ に限定できる 万が一のミスや不正アクセス時の 被害範囲を最小化 できる 組織のコンプライアンス要件を満たしやすくなる 今回のスタックで必要な権限は以下の3つです。 S3 :バケットの作成・削除・ポリシー設定 IAM :Lambda実行ロールの作成・削除・ポリシーアタッチ Lambda :autoDeleteObjects用Lambda関数の作成・削除 ポリシーJSONの作成 @"{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:*"], "Resource": "*" }, { "Effect": "Allow", "Action": [ "iam:PassRole", "iam:CreateRole", "iam:DeleteRole", "iam:AttachRolePolicy", "iam:DetachRolePolicy" ], "Resource": "*" }, { "Effect": "Allow", "Action": ["lambda:*"], "Resource": "*" } ]}"@ | Set-Content cdk-minimal-policy.json @ " { " Version " : " 2012-10-17 " , " Statement " : [ { " Effect " : " Allow " , " Action " : [ " s 3 :* " ], " Resource " : " * " }, { " Effect " : " Allow " , " Action " : [ " iam:PassRole " , " iam:CreateRole " , " iam:DeleteRole " , " iam:AttachRolePolicy " , " iam:DetachRolePolicy " ], " Resource " : " * " }, { " Effect " : " Allow " , " Action " : [ " lambda:* " ], " Resource " : " * " } ] } " @ | Set-Content cdk-minimal-policy.json ポリシーをアカウントAに作成 aws iam create-policy ` --policy-name CdkMinimalPolicy ` --policy-document file://cdk-minimal-policy.json ` --profile test-111111111111 aws iam create - policy ` -- policy - name CdkMinimalPolicy ` -- policy - document file: // cdk - minimal - policy.json ` -- profile test-111111111111 作成されたポリシーのARN( arn:aws:iam::111111111111:policy/CdkMinimalPolicy )を控えておきます。 Step 7:bootstrap(2回目)―クロスアカウント対応 なぜ2回bootstrapするのか 1回目のbootstrapは「CDKToolkitを作成する」ための最低限の実行でした。この時点では: --trust 未指定 → アカウントBからのAssumeRoleが 許可されていない --cloudformation-execution-policies 未指定 → AdministratorAccess が使われている 2回目のbootstrapでは、これらを正しく設定し直します。CDKToolkitスタックは Updateとして適用 されるため、既存リソースを削除せずに設定を変更できます。 Tips :最初からクロスアカウントデプロイを想定している場合は、1回目のbootstrapから --trust と --cloudformation-execution-policies を指定することで、2回に分ける必要はありません。 クロスアカウント対応のbootstrapを実行 cdk bootstrap aws://111111111111/ap-northeast-1 ` --profile test-111111111111 ` --trust 222222222222 ` --cloudformation-execution-policies arn:aws:iam::111111111111:policy/CdkMinimalPolicy cdk bootstrap aws: // 111111111111 / ap - northeast - 1 ` -- profile test-111111111111 ` -- trust 222222222222 ` -- cloudformation - execution - policies arn:aws:iam:: 111111111111 :policy / CdkMinimalPolicy 各オプションの意味: オプション 意味 --trust 222222222222 アカウントB(222222222222)からのAssumeRoleを許可する --cloudformation-execution-policies CloudFormationが使うIAMポリシーをAdministratorAccessから最小権限に変更する ✅ Environment aws://111111111111/ap-northeast-1 bootstrapped. と表示されれば成功です。 補足 :bootstrap再実行によって CloudFormationExecutionRole に紐づくポリシーが AdministratorAccess から CdkMinimalPolicy に切り替わります。 ただし、Step 4でデプロイ済みの CdkCrossStack に対して新ポリシーが適用されるのは、 次回 cdk deploy を実行したタイミング です。 既存スタックのリソース自体はそのまま維持されます。 DeploymentActionRoleの信頼ポリシーを確認 bootstrap後、 DeploymentActionRole の信頼ポリシーにアカウントBが追加されていることを確認します。 aws iam get-role ` --role-name cdk-hnb659fds-deploy-role-111111111111-ap-northeast-1 ` --query "Role.AssumeRolePolicyDocument" ` --profile test-111111111111 ` --no-cli-pager aws iam get-role ` -- role - name cdk - hnb659fds - deploy-role - 111111111111 - ap - northeast - 1 ` -- query " Role.AssumeRolePolicyDocument " ` -- profile test-111111111111 ` -- no - cli - pager レスポンスにアカウントB( arn:aws:iam::222222222222:root )の sts:AssumeRole が含まれていれば正しく設定されています。 Step 8:アカウントBのIAMユーザーにAssumeRole権限を付与 アカウントAのロールを引き受けられるようになりましたが、アカウントBの test1 ユーザー自身にも sts:AssumeRole の権限が必要です。 インラインポリシーのJSONを作成 @"{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::111111111111:role/cdk-hnb659fds-deploy-role-111111111111-ap-northeast-1" } ]}"@ | Set-Content assume-role.json @ " { " Version " : " 2012-10-17 " , " Statement " : [ { " Effect " : " Allow " , " Action " : " sts:AssumeRole " , " Resource " : " arn:aws:iam:: 111111111111 :role/cdk-hnb 659 fds-deploy-role -111111111111 -ap-northeast -1 " } ] } " @ | Set-Content assume-role.json test1ユーザーにポリシーをアタッチ aws iam put-user-policy ` --user-name test1 ` --policy-name AllowAssumeCdkDeployRole ` --policy-document file://assume-role.json ` --profile test-A aws iam put - user - policy ` -- user - name test1 ` -- policy - name AllowAssumeCdkDeployRole ` -- policy - document file: // assume - role.json ` -- profile test-A Step 9:AssumeRoleの動作確認 デプロイ前に、実際にAssumeRoleが成功するかを確認します。 aws sts assume-role ` --role-arn arn:aws:iam::111111111111:role/cdk-hnb659fds-deploy-role-111111111111-ap-northeast-1 ` --role-session-name test ` --profile test-A aws sts assume - role ` -- role - arn arn:aws:iam:: 111111111111 :role / cdk - hnb659fds - deploy-role - 111111111111 - ap - northeast - 1 ` -- role - session - name test ` -- profile test-A 以下のように一時クレデンシャルが返れば成功です。 { "Credentials": { "AccessKeyId": "ASIAXXXXXXXXXXXXXXXX", "SecretAccessKey": "****", "SessionToken": "****", "Expiration": "2026-03-24T19:05:06+00:00" }, "AssumedRoleUser": { "AssumedRoleId": "AROAXXXXXXXXXXXXXXXXX:test", "Arn": "arn:aws:sts::111111111111:assumed-role/cdk-hnb659fds-deploy-role-111111111111-ap-northeast-1/test" }} { " Credentials " : { " AccessKeyId " : " ASIAXXXXXXXXXXXXXXXX " , " SecretAccessKey " : " **** " , " SessionToken " : " **** " , " Expiration " : " 2026-03-24T19:05:06+00:00 " }, " AssumedRoleUser " : { " AssumedRoleId " : " AROAXXXXXXXXXXXXXXXXX:test " , " Arn " : " arn:aws:sts::111111111111:assumed-role/cdk-hnb659fds-deploy-role-111111111111-ap-northeast-1/test " } } Step 10:クロスアカウントデプロイの実行 再度アカウントBからアカウントAへのクロスアカウントデプロイを実行します。 cdk deploy --profile test-A cdk deploy -- profile test-A ✅ CdkCrossStack (no changes) と表示されれば成功です。(Step 4で既にデプロイ済みのため変更なしと表示されます) Step 11:後片付け [!WARNING] この手順は 本ハンズオン用に作成した検証環境を前提 としています。 業務システムや他の CDK プロジェクトでも同一アカウント・リージョンで CDK を利用している場合、以下の操作を実行すると 他のスタックやデプロイ処理に影響を与える可能性 があります。 cdk destroy によるリソース削除 CDKToolkit スタック(bootstrap 環境)の削除 IAM ユーザー/ポリシー/アクセスキーの削除 検証環境以外で実施する場合は、 削除対象が本ハンズオンで作成したものに限定されていること を 事前に十分確認したうえで実行してください。 スタックの削除 クロスアカウント操作の確認ができたら、作成したリソースを削除します。 cdk destroy --profile test-A cdk destroy -- profile test-A Are you sure you want to delete: CdkCrossStack (y/n)? に y を入力します。 ✅ CdkCrossStack: destroyed と表示されれば削除完了です。 スタックが削除されたことを確認 aws cloudformation describe-stacks ` --stack-name CdkCrossStack ` --profile test-111111111111 aws cloudformation describe - stacks ` -- stack - name CdkCrossStack ` -- profile test-111111111111 Stack with id CdkCrossStack does not exist というエラーが返れば正しく削除されています。 CDKToolkitスタックの削除 aws cloudformation delete-stack ` --stack-name CDKToolkit ` --profile test-111111111111 aws cloudformation delete - stack ` -- stack - name CDKToolkit ` -- profile test-111111111111 test1ユーザーのインラインポリシーを削除 aws iam delete-user-policy ` --user-name test1 ` --policy-name AllowAssumeCdkDeployRole ` --profile test-A aws iam delete - user - policy ` -- user - name test1 ` -- policy - name AllowAssumeCdkDeployRole ` -- profile test-A test1ユーザーのアクセスキーを削除 まずキーIDを確認します。 aws iam list-access-keys --user-name test1 --profile test-A aws iam list - access - keys -- user - name test1 -- profile test-A 確認したキーIDを指定して削除します。 aws iam delete-access-key ` --user-name test1 ` --access-key-id AKIAXXXXXXXXXXXXXXXX ` --profile test-A aws iam delete - access - key ` -- user - name test1 ` -- access - key - id AKIAXXXXXXXXXXXXXXXX ` -- profile test-A AWSプロファイルの削除 エディタで test-111111111111 および test-A のセクションを削除して保存します。 削除後のプロファイル確認 aws configure list-profiles aws configure list - profiles 削除したプロファイルが表示されなければ完了です。 まとめ cdk bootstrap は アカウントとリージョンの組み合わせごと に実行が必要 すでにbootstrap済みの環境では 再実行不要 。既存のCDKToolkitをそのまま利用する 組織のIAMポリシー制限がある場合は、 別途ロールを手動作成 して --role-arn で指定する cdk bootstrap --trust でデプロイ先アカウントが操作元アカウントを 信頼する 設定を行う --cloudformation-execution-policies で AdministratorAccess を避け最小権限 を使う 操作元アカウントのIAMユーザーにも sts:AssumeRole 権限 が必要 bin/*.ts の env.account は 必ず明示的に指定 する 参考資料 AWS CDK ブートストラップ AWS CDK で使用する環境をブートストラップする