AWS CDK スタック間でのOutput/Import完全ガイド
1. はじめに
AWS CDKを使っていると、あるスタックで作成したリソースを別のスタックから参照したいケースがよくあります。
例えば、VPCを作成するスタックと、そのVPCを利用してEC2やLambdaを作成するスタックを分けたい場合です。
そんなときに役立つのがOutputとFn.importValueです。
2. 基本の考え方
AWS CDKは内部的にCloudFormationテンプレートを生成します。
CloudFormationでは、スタック間で値をやり取りするために以下の流れを取ります。
Export/Importの流れ:
- Export(スタックAが値を外部公開)
- Import(スタックBがその値を参照)
CDKではこれをCfnOutput
とFn.importValue
で表現します。
3. 実装例
3.1 Export側(Stack A)
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';
export class NetworkStack extends cdk.Stack {
public readonly vpc: ec2.Vpc;
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
this.vpc = new ec2.Vpc(this, 'MyVpc', { maxAzs: 2 });
new cdk.CfnOutput(this, 'VpcIdOutput', {
value: this.vpc.vpcId,
exportName: 'MyVpcId', // 重要: 一意な名前
});
}
}
3.2 Import側(Stack B)
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';
export class AppStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const vpcId = cdk.Fn.importValue('MyVpcId');
const vpc = ec2.Vpc.fromLookup(this, 'ImportedVpc', { vpcId });
new ec2.Instance(this, 'MyInstance', {
instanceType: new ec2.InstanceType('t3.micro'),
machineImage: ec2.MachineImage.latestAmazonLinux2(),
vpc
});
}
}
4. アーキテクチャ図解
5. よくあるユースケース
ユースケース | Export側 | Import側 | Export名の例 |
---|---|---|---|
ネットワーク基盤 | VPC ID | EC2/Lambda等 | myapp-prod-vpc-id |
セキュリティグループ | Security Group ID | ALB/RDS等 | myapp-prod-sg-web |
データベース | RDS Endpoint | アプリケーション | myapp-prod-db-endpoint |
S3バケット | Bucket名 | Lambda/CodePipeline | myapp-prod-artifacts-bucket |
KMSキー | Key ARN | 暗号化サービス | myapp-prod-kms-key-arn |
6. 注意点とトラブルシューティング
6.1 重要な制約
項目 | 制約内容 | 対処法 |
---|---|---|
Export名の一意性 | リージョン内で一意である必要 | プレフィックスを使用(例:myapp-env- ) |
デプロイ順序 | Export側を先にデプロイ | cdk deploy NetworkStack AppStack |
削除制約 | Import中はExportを削除不可 | Import側を先に削除 |
リージョン制約 | 同一リージョン内のみ | クロスリージョンの場合はSSM Parameter Store等を使用 |
6.2 エラーパターンと対処法
7. ベストプラクティス
7.1 命名規則
レベル | 推奨パターン | 例 |
---|---|---|
基本 | {project}-{resource} |
myapp-vpc-id |
環境別 | {project}-{env}-{resource} |
myapp-prod-vpc-id |
詳細 | {project}-{env}-{service}-{resource} |
myapp-prod-web-sg-id |
7.2 使い分けガイド
7.3 推奨する実装パターン
Props渡しパターン(同一アプリ内):
// main.ts
const networkStack = new NetworkStack(app, 'NetworkStack');
const appStack = new AppStack(app, 'AppStack', {
vpc: networkStack.vpc // 直接渡す
});
Output/Importパターン(別アプリ・クロスアカウント):
// 環境別Export名
const exportName = `${props.projectName}-${props.env}-vpc-id`;
new cdk.CfnOutput(this, 'VpcIdOutput', {
value: this.vpc.vpcId,
exportName: exportName,
description: `VPC ID for ${props.projectName} ${props.env} environment`
});
8. 高度な活用例
8.1 複数値のExport
// 複数のサブネットIDをExport
this.vpc.privateSubnets.forEach((subnet, index) => {
new cdk.CfnOutput(this, `PrivateSubnet${index}Output`, {
value: subnet.subnetId,
exportName: `myapp-prod-private-subnet-${index}`
});
});
8.2 条件付きExport
// 本番環境のみExport
if (props.env === 'prod') {
new cdk.CfnOutput(this, 'DatabaseEndpointOutput', {
value: this.database.instanceEndpoint.hostname,
exportName: 'myapp-prod-db-endpoint'
});
}
まとめ
Output/Importは、スタック間の疎結合を実現する便利な仕組みです。
ただし、以下の点を意識して使用しましょう:
- 同一CDKアプリ内ではpropsで依存注入を優先
- Export名は環境・プロジェクト名を含めて一意に
- デプロイ順序と削除順序に注意
- クロスアカウント・別アプリ間では積極的に活用
適切に使い分けることで、保守性の高いインフラコードが書けるようになります!