概要
CloudFormationまたはCDKでのAWS環境構築は、スタックと呼ばれるAWSリソースの集合を作成することによって行われます。
スタック作成の粒度は開発者によって様々ですが、別スタックで作成したリソースの情報を利用したい場合があります。
例:
・スタックAでS3バケットを作成
・スタックBで作成するLambdaの環境変数に、スタックAで作成したバケット名を設定
この場合、スタックAで作成したバケット名をスタックBで使用するためには以下のような方法が考えられます。
-
エクスポート値を使用しない-1
CloudFormationのAWSコンソールで、スタックAのリソース名を確認。取得したバケット名をスタックB作成のテンプレートにべた書きしてデプロイ -
エクスポート値を使用しない-2
CloudFormationのAWSコンソールで、スタックAのリソース名を確認。取得したバケット名をスタックB作成時に外から注入してデプロイ -
エクスポート値を使用する
スタックA作成時にエクスポート値としてS3バケット名を出力。スタックB作成時にエクスポート値をインポート(クロススタック参照)してデプロイ -
パラメータストアを利用する
スタックA作成時パラメータストアにS3バケット名を登録。スタックB作成時にパラメータストアからバケット名を取得してデプロイ
今回はこれらを実際に試していきます。
試してみた
では、それぞれCDKで試してみます。
エクスポート値を使用しない-1
Stack APPのCDKは以下になります。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { StackA } from '../lib/stack_a';
import { StackB } from '../lib/stack_b';
const app = new cdk.App();
new StackA(app, 'StackA', {
});
new StackB(app, 'StackB', {
});
まず、スタックAでS3バケットを作成します。
CDKは以下になります。
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
export class StackA extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.CfnBucket(this, 'CreateBucketA', {
bucketName: 'bucket-export-test'
});
}
}
スタックAをデプロイします。
cdk deploy StackA
スタックが作成されたので、リソース名をAWSコンソールで確認します。
次に、確認したS3バケット名をスタックBに入力してLambdaを作成します。
CDKは以下になります。
import * as cdk from '@aws-cdk/core';
import { Role, ServicePrincipal, ManagedPolicy } from '@aws-cdk/aws-iam';
import { Function, AssetCode, Runtime } from '@aws-cdk/aws-lambda';
export class StackB extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const executionLambdaRole = new Role(this, 'LambdaRole', {
roleName: 'lambdaRole',
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
]
});
new Function(this, 'ExportTestFunction', {
functionName: 'export-test-function',
runtime: Runtime.NODEJS_14_X,
code: AssetCode.fromAsset('src'),
handler: 'index.handler',
role: executionLambdaRole,
environment: {
BUCKET_NAME: "bucket-export-test"
}
});
}
}
スタックBをデプロイします。
cdk deploy StackB
AWSコンソールでLambdaを確認すると、環境変数が設定されていることが確認できます。
エクスポート値を使用しない-2
次に、バケット名をデプロイ時に外から注入する方法を紹介します。
※スタックAのCDKは先ほどと同様なため省略します。
Stack APPのCDKの変更点は以下になります。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { StackA } from '../lib/stack_a';
import { StackB } from '../lib/stack_b';
const app = new cdk.App();
new StackA(app, 'StackA', {
});
new StackB(app, 'StackB', {
+ bucket: app.node.tryGetContext(`bucket`)
});
Lambdaを作成するCDKの変更点は以下になります。
import * as cdk from '@aws-cdk/core';
import { Role, ServicePrincipal, ManagedPolicy } from '@aws-cdk/aws-iam';
import { Function, AssetCode, Runtime } from '@aws-cdk/aws-lambda';
export class StackB extends cdk.Stack {
- constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
+ constructor(scope: cdk.Construct, id: string, props: StackBProps) {
super(scope, id, props);
const executionLambdaRole = new Role(this, 'LambdaRole', {
roleName: 'lambdaRole',
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
]
});
new Function(this, 'ExportTestFunction', {
functionName: 'export-test-function',
runtime: Runtime.NODEJS_14_X,
code: AssetCode.fromAsset('src'),
handler: 'index.handler',
role: executionLambdaRole,
environment: {
- BUCKET_NAME: "bucket-export-test"
+ BUCKET_NAME: props.bucket
}
});
}
}
+export interface StackBProps extends cdk.StackProps {
+ readonly bucket: string;
+}
スタックAをデプロイします。
cdk deploy StackA
スタックBをデプロイします。
引数で渡したバケット名が環境変数に設定されます。
cdk deploy StackB --context bucket=bucket-export-test
エクスポート値を使用する
次に、スタックAの作成時にバケット名をエクスポートし、スタックB作成時にインポートする方法を紹介します。
※スタックBのCDKは先ほどと同様なため省略します。
Stack APPのCDKの変更点は以下になります。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { StackA } from '../lib/stack_a';
import { StackB } from '../lib/stack_b';
const app = new cdk.App();
new StackA(app, 'StackA', {
});
new StackB(app, 'StackB', {
- bucket: app.node.tryGetContext(`bucket`)
+ bucket: cdk.Fn.importValue(`bucketName`)
});
S3バケットを作成するCDKの変更点は以下になります。
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
export class StackA extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.CfnBucket(this, 'CreateBucketA', {
bucketName: 'bucket-export-test'
});
+ new cdk.CfnOutput(this, 'BucketName', {
+ value: 'bucket-export-test',
+ exportName: 'bucketName'
+ });
}
}
スタックAをデプロイします。
cdk deploy StackA
スタックBをデプロイします。
cdk deploy StackB
パラメータストアを利用する
最後に、スタックAの作成時にバケット名をパラメータストアに登録し、スタックB作成時にパラメータストアから取得する方法を紹介します。
Stack APPのCDKファイルは以下になります。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { StackA } from '../lib/stack_a';
import { StackB } from '../lib/stack_b';
const app = new cdk.App();
new StackA(app, 'StackA', {
});
new StackB(app, 'StackB', {
- bucket: cdk.Fn.importValue(`bucketName`)
});
S3バケットを作成するCDKの変更点は以下になります。
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
+import * as ssm from '@aws-cdk/aws-ssm';
export class StackA extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.CfnBucket(this, 'CreateBucketA', {
bucketName: 'bucket-export-test'
});
- new cdk.CfnOutput(this, 'BucketName', {
- value: 'bucket-export-test',
- exportName: 'bucketName'
- });
+ const param = new ssm.StringParameter(this, 'BucketName', {
+ stringValue: 'bucket-export-test',
+ parameterName: '/bucket/test',
+ });
}
}
Lambdaを作成するCDKの変更点は以下になります。
import * as cdk from '@aws-cdk/core';
import { Role, ServicePrincipal, ManagedPolicy } from '@aws-cdk/aws-iam';
import { Function, AssetCode, Runtime } from '@aws-cdk/aws-lambda';
+import * as ssm from '@aws-cdk/aws-ssm';
export class StackB extends cdk.Stack {
- constructor(scope: cdk.Construct, id: string, props: StackBProps) {
+ constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const executionLambdaRole = new Role(this, 'LambdaRole', {
roleName: 'lambdaSecureExecutionRole',
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
]
});
new Function(this, 'ExportTestFunction', {
functionName: 'export-test-function',
runtime: Runtime.NODEJS_14_X,
code: AssetCode.fromAsset('src'),
handler: 'index.handler',
role: executionLambdaRole,
environment: {
- BUCKET_NAME: props.bucket
+ BUCKET_NAME: ssm.StringParameter.valueForStringParameter(this, '/bucket/test')
}
});
}
}
-export interface StackBProps extends cdk.StackProps {
- readonly bucket: string;
-}
スタックAをデプロイします。
cdk deploy StackA
スタックBをデプロイします。
cdk deploy StackB
おまけ
AWS CLIコマンドでもエクスポート値を取得することが出来るので、以下に記載します。
- 対象のスタック名の全てのエクスポート値を取得
# StackAのエクスポート値
aws cloudformation describe-stacks \
--stack-name StackA \
--query "Stacks[0].Outputs[]"
[
{
"OutputKey": "BucketA",
"OutputValue": "bucketa",
"ExportName": "bucketNameA"
},
{
"OutputKey": "BucketB",
"OutputValue": "bucketb",
"ExportName": "bucketNameB"
}
]
- 対象のスタック名の指定したエクスポート値を取得
# StackAのエクスポート名:bucketNameAを取得
aws cloudformation describe-stacks \
--stack-name StackA \
--query "Stacks[0].Outputs[?ExportName=='bucketNameA'].OutputValue" \
--output text
bucketa
- 指定したエクスポート値を取得
エクスポート名:bucketNameの値を取得
aws cloudformation list-exports \
--query "Exports[?Name=='bucketNameA'].Value[]" \
--output text
bucketa
おわりに
今回はスタック間のAWSリソース情報の受け渡しについて説明させて頂きました。
CloudFormationやCDKを利用する際はよく使うので、ご参考になれば幸いです。