AWS CDKでスタック間依存を解消する方法メモ
AWS CDKのExport/Importとは?
AWS CDKでは、あるスタックのリソース(例:VPCやEC2インスタンス)を別のスタックから参照することができます。
// NetworkStack.ts
export class NetworkStack extends cdk.Stack {
readonly vpc: ec2.Vpc;
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
this.vpc = new ec2.Vpc(this, 'AppVpc', { maxAzs: 2 });
}
}
// AppStack.ts
export class AppStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props: { vpc: ec2.Vpc }) {
super(scope, id, props);
new ec2.Instance(this, 'AppServer', {
vpc: props.vpc, // ← 他スタックのVPCを参照
instanceType: new ec2.InstanceType('t3.micro'),
machineImage: ec2.MachineImage.latestAmazonLinux(),
});
}
}
一見便利ですが、裏ではCDKが自動的に以下のようなCloudFormation設定を生成しています。
# NetworkStackのOutputs
Outputs:
VpcId:
Value: !Ref AppVpc
Export:
Name: NetworkStack:ExportsOutputRefAppVpc
# AppStackでの参照
Resources:
Instance:
Type: AWS::EC2::Instance
Properties:
SubnetId: !ImportValue NetworkStack:ExportsOutputRefAppVpc
つまり、CloudFormationのExport/ImportValueが裏で作られ依存関係を持つことになります。
依存関係のデメリット
この仕組みには便利さの裏に大きなデメリットがあります。
-
削除できない
Exportを持つスタックを削除しようとすると、Importしているスタックがある限り削除不可。 -
順序に依存
Producer(Export側)を更新するたびに、Consumer(Import側)も自動的にデプロイ対象になり、CI/CDが複雑化。 -
単体再デプロイが難しい
一部だけ壊れても、依存する全スタックを再構築しなければならない。
→ 結果として、強すぎる結合が運用負担を増やします。
SSM Parameter Storeを使った解決策
CloudFormationのExport/Importを使わずに値を共有するには、
AWS Systems Manager Parameter Store が有効です。
① Producer(値を提供する側)
// ProducerStack.ts
import * as ssm from 'aws-cdk-lib/aws-ssm';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as cdk from 'aws-cdk-lib';
export class ProducerStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const instance = new ec2.Instance(this, 'AppEc2', {
vpc: new ec2.Vpc(this, 'AppVpc', { maxAzs: 2 }),
instanceType: new ec2.InstanceType('t3.micro'),
machineImage: ec2.MachineImage.latestAmazonLinux(),
});
new ssm.StringParameter(this, 'Ec2IdParam', {
parameterName: '/myapp/ec2-id',
stringValue: instance.instanceId,
});
}
}
② Consumer(値を使う側)
// ConsumerStack.ts
import * as ssm from 'aws-cdk-lib/aws-ssm';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as cdk from 'aws-cdk-lib';
export class ConsumerStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const instanceId = ssm.StringParameter.valueFromLookup(this, '/myapp/ec2-id');
const imported = ec2.Instance.fromInstanceId(this, 'ImportedEc2', instanceId);
new ec2.SecurityGroup(this, 'ExampleSG', {
vpc: imported.instanceVpc,
description: `SecurityGroup for instance ${instanceId}`,
});
}
}
この構成では:
- CloudFormationの
Export/ImportValueは生成されません - Producerを単体でdestroy可能
- ConsumerはSSMを介して最新値を取得できます
cdk.context.jsonについて注意すべきこと
valueFromLookup() を使うと、CDKはSSMの値を合成時に問い合わせてキャッシュします。
このキャッシュは cdk.context.json に保存されます。
基本ルール
| 操作 | コマンド | 内容 |
|---|---|---|
| 全クリア | cdk context --clear |
全てのルックアップ結果を削除(再取得) |
| 部分クリア | cdk context --reset <key> |
指定キーのみ削除 |
| キー確認 | cat cdk.context.json |
キャッシュされたキー一覧を確認 |
キャッシュ更新運用例
# SSMパラメータの値だけ再取得したい場合
cdk context --reset "ssm:account=111111111111:region=ap-northeast-1:parameterName=/myapp/ec2-id"
# 全てのルックアップを更新したい場合
cdk context --clear
# 更新後に差分を確認
cdk diff
これで
cdk diffに最新SSM値の変更が反映されます。
結論
CDKのExport/Importは便利ですが、強すぎる依存関係を生みます。SSM Parameter Storeを使えば、スタックを疎結合に保ちながら差分検知も可能です。valueFromLookup + cdk context --clear の運用は、再現性と柔軟性を両立するベストプラクティスです。