cdk-bootstrap-configuration について
AWS CDKのインフラ開発者向けの設定パターンで、デプロイ前の入力と実行時の出力を分離します。環境ファイルやCI変数からCDKへ入力を渡し、インフラの結果をSSM Parameter Storeに保存して、コンテキストキャッシュや循環依存の問題を避けます。
主な機能
- 01ブートストラップ設定と実行時設定の分離
- 02環境ファイルまたはCI変数から入力を読み込み
- 03Stack Props経由で設定を受け渡し
- 04デプロイ結果をSSM Parameter Storeに保存
- 05合成時の設定検証
活用シーン
- 01ドメインや証明書ARNを含むCDKデプロイの管理
- 02dev、staging、prod環境の設定
- 03CDKのコンテキストキャッシュや循環依存の回避
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ loxosceles-dev/ dev-skills --skill cdk-bootstrap-configurationインストール後、Claude Code で/cdk-bootstrap-configurationを入力して読み込まれたか確認します。
詳細
CDK ブートストラップ設定
これは参考パターンです。 このアプローチから学び、自分の状況に合わせて調整してください。逐語的にコピーしないでください。
対象者:CDK インフラストラクチャの開発者。設定を読み取るサービスを構築する場合は、SSM ランタイム設定を参照してください。
課題:ブートストラップ設定に CDK の valueFromLookup() を使用すると、コンテキストのキャッシュに関する問題が発生し、手動での対応が必要になったり、デプロイスクリプトとインフラストラクチャコードの間に循環依存が生じたりします。
解決策:ブートストラップ設定(デプロイ時の入力値)とランタイム設定(インフラストラクチャの出力値)を分離します。ブートストラップ設定は、synth 時に環境ファイルから CDK に渡します。ランタイムの出力値は、インフラストラクチャからサービスが利用する SSM Parameter Store に書き込みます。
パターン
アーキテクチャのフロー:
┌─────────────────────────────────────────────────────────────┐
│ BOOTSTRAP LAYER (Pre-Infrastructure) │
│ - Lives in: .env.{stage} files (local) or CI variables │
│ - Purpose: External constants needed to CREATE infra │
│ - Examples: domain, certificate ARN, account ID │
│ - Access: Read by CDK at synth time │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ CDK SYNTHESIS │
│ - Reads bootstrap from environment directly │
│ - No SSM lookup at synth time │
│ - Generates CloudFormation template │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ CDK DEPLOYMENT │
│ - Creates infrastructure resources │
│ - Writes outputs to SSM for runtime consumption │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ SSM PARAMETER STORE (Post-Infrastructure) │
│ - Contains: Stack outputs for runtime consumption │
│ - Purpose: Runtime config for services/lambdas/frontend │
│ - Access: Services read at runtime │
└─────────────────────────────────────────────────────────────┘
主要コンポーネント:
- ブートストラップ設定:インフラストラクチャの作成に必要な外部定数(ドメイン、証明書 ARN、アカウント ID)
- 環境マネージャー:ファイル(ローカル)または環境変数(CI/CD)からブートストラップ設定を読み込みます
- スタックの Props:ブートストラップ値を Props 経由で渡し、スタック間の依存を解消します
- SSM 出力:インフラストラクチャの結果を、サービスが利用する Parameter Store に書き込みます
基本原則:SSM に保存するのはデプロイの入力値ではなく、デプロイの結果です。
このパターンを使う理由
メリット:
- CDK のコンテキストキャッシュが不要:
cdk.context.jsonと手動対応を排除します - 明確な分離:ブートストラップ(入力値)と出力値(結果)を、アーキテクチャ上明確に区別できます
- CI/CD に適しています:ローカル環境と CI 環境の両方でシームレスに動作します
- 型安全:synth 時の検証により、設定の不足を早期に検出できます
- 循環依存がありません:デプロイスクリプトが、CDK の読み取る値を事前に設定する必要はありません
- Git との相性が良好:環境固有のコンテキストファイルを管理する必要がありません
ユースケース:
- 本番環境のドメインと証明書を使う CDK プロジェクト
- 複数環境へのデプロイ(dev、staging、prod)
- CI/CD パイプラインを使うプロジェクト
- CDK のコンテキストキャッシュの問題に直面しているチーム
実装
1. 環境マネージャー
// lib/core/environment-manager.ts
export class EnvironmentManager {
private loadEnv(stage: string): Record<string, string> {
const envPath = path.join(this.infraRootPath, `.env.${stage}`);
// Try file first (local), fall back to process.env (CI)
if (fs.existsSync(envPath)) {
const fileEnv = dotenv.parse(fs.readFileSync(envPath));
// Filter undefined from process.env
const processEnv: Record<string, string> = {};
Object.entries(process.env).forEach(([key, value]) => {
if (value !== undefined) processEnv[key] = value;
});
// CI variables override file values
return { ...fileEnv, ...processEnv };
}
// CI path: use process.env only
const processEnv: Record<string, string> = {};
Object.entries(process.env).forEach(([key, value]) => {
if (value !== undefined) processEnv[key] = value;
});
if (Object.keys(processEnv).length === 0) {
throw new Error(
`No configuration found for stage '${stage}'.\n` +
`Expected file: ${envPath}\n` +
`Or environment variables in CI/CD context.`
);
}
return processEnv;
}
public getStackEnv(stackType: string): IStackEnv {
const baseEnv = this.loadEnv(this.stage);
const isProd = this.stage === 'prod';
// Validate required bootstrap values
this.validateBootstrap(baseEnv, isProd);
return {
awsAccountId: baseEnv.CDK_DEFAULT_ACCOUNT,
awsRegionDefault: baseEnv.CDK_DEFAULT_REGION,
stage: this.stage,
// Add stage-specific bootstrap values
...(isProd && {
prodDomainName: baseEnv.PROD_DOMAIN_NAME,
certificateArn: baseEnv.CERTIFICATE_ARN
})
};
}
private validateBootstrap(env: Record<string, string>, isProd: boolean): void {
const required = ['CDK_DEFAULT_ACCOUNT', 'CDK_DEFAULT_REGION'];
if (isProd) {
required.push('PROD_DOMAIN_NAME', 'CERTIFICATE_ARN');
}
const missing = required.filter(key => !env[key]);
if (missing.length > 0) {
throw new Error(
`Missing required bootstrap configuration: ${missing.join(', ')}\n` +
`Check .env.${this.stage} or CI/CD environment variables`
);
}
}
}
重要な詳細:
- 型安全:使用前に
process.envからundefinedの値を除外します - 検証:synth 時に明確なエラーメッセージを表示して、即座に失敗させます
- マージ戦略:ファイルの値と process.env をマージします(CI の値がファイルの値を上書きします)
2. CDK アプリのエントリーポイント
// bin/app.ts
const envManager = new EnvironmentManager(config);
const stackEnv = envManager.getStackEnv('web');
new WebStack(app, `WebStack-${envManager.getStage()}`, {
env: {
account: stackEnv.awsAccountId,
region: stackEnv.awsRegionDefault
},
stackEnv: stackEnv // Pass bootstrap through props
});
Props を使う理由:スタックを EnvironmentManager から切り離し、テストしやすくします。
3. スタックの実装
// lib/stacks/web-stack.ts
export class WebStack extends Stack {
constructor(scope: Construct, id: string, props: WebStackProps) {
super(scope, id, props);
const isProd = props.stackEnv.stage === 'prod';
// Use bootstrap values from props (NOT from SSM lookup)
let domainName: string | undefined;
let certificateArn: string | undefined;
if (isProd) {
domainName = props.stackEnv.prodDomainName;
certificateArn = props.stackEnv.certificateArn;
}
// Create infrastructure...
const distribution = new cloudfront.Distribution(this, 'Distribution', {
domainNames: domainName ? [domainName] : undefined,
certificate: certificateArn
? acm.Certificate.fromCertificateArn(this, 'Cert', certificateArn)
: undefined,
// ... other config
});
// Write outputs to SSM for runtime consumption
new ssm.StringParameter(this, 'CloudFrontDomainOutput', {
parameterName: `/${projectId}/${stage}/outputs/CLOUDFRONT_DOMAIN`,
stringValue: distribution.distributionDomainName,
description: 'CloudFront distribution domain name',
tier: ssm.ParameterTier.STANDARD
});
}
}
重要:ブートストラップ値に対する ssm.StringParameter.valueFromLookup() の呼び出しをすべて削除してください。
4. スタックの Props インターフェース
// types/stack-env.ts
export interface IStackEnv {
awsAccountId: string;
awsRegionDefault: string;
stage: string;
// Stage-specific bootstrap values
prodDomainName?: string;
certificateArn?: string;
}
export interface WebStackProps extends StackProps {
stackEnv: IStackEnv;
}
5. ブートストラップ設定
ローカル開発(.env.{stage} — gitignore 対象):
# Bootstrap values for CDK synthesis
CDK_DEFAULT_ACCOUNT=123456789012
CDK_DEFAULT_REGION=us-east-1
PROD_DOMAIN_NAME=example.com
CERTIFICATE_ARN=arn:aws:acm:us-east-1:123456789012:certificate/abc123
CI/CD:リポジトリ変数として設定します(GitHub Actions Variables、CodeBuild の環境設定など)。
重要なトレードオフ:ブートストラップとランタイム
基本的な違い:
| 項目 | ブートストラップ(入力値) | ランタイム(出力値) |
|---|---|---|
| タイミング | インフラストラクチャが存在する前 | インフラストラクチャが存在した後 |
| 取得元 | .env ファイルまたは CI 変数 | SSM Parameter Store |
| 目的 | インフラストラクチャの作成 | サービスの設定 |
| 読み取り元 | synth 時の CDK | ランタイムのサービス |
| 例 | ドメイン、証明書 ARN、アカウント ID | CloudFront ドメイン、バケット名 |
重要である理由:
- ブートストラップに
valueFromLookup()を使うと、循環依存が生じます - CDK は検索結果を
cdk.context.jsonにキャッシュします(環境固有で、Git との相性がよくありません) - コンテキストが失われると、手動対応とデバッグに時間が必要になります
解決方法:
- ブートストラップ:環境から直接読み取ります(SSM の検索は不要です)
- ランタイム:デプロイ後に SSM に書き込みます(サービス向け)
使用しない場合
- 単一環境のシンプルなプロジェクト:環境が1つだけで、本番用ドメインもない場合、この分離は過剰な可能性があります
- CDK を使用しないプロジェクト:このパターンは AWS CDK のコンテキストキャッシュに関する問題に特化しています
- 外部依存関係のないプロジェクト:すべての設定が CDK 自体によって生成される場合は、よりシンプルな方法で十分な可能性があります
代替案:
- CDK コンテキスト値:本当に静的で、変更されることのない値に使用します(環境固有の設定には推奨されません)
- CloudFormation パラメーター:デプロイごとに変更される値に使用します(デプロイが複雑になります)
関連パターン
- SSM ランタイム設定 - サービスが SSM からランタイム設定を読み込む方法(このパターンでは SSM に書き込み、こちらのパターンでは SSM から読み込みます)
- 環境の検証 -
validateBootstrap()メソッドで使用される、失敗時に即座に終了する検証パターン - 環境デプロイ戦略 - 「dev」、「prod」などの意味を定義する、複数環境へのデプロイ方法
段階的な改善
開発者がこのスキルで防止すべき動作を修正した場合、今後同じ修正が必要にならないよう、このスキルに追加する具体的な改訂を提案してください。