こんにちは、小倉です。インフラ寄りの仕事が多いアプリケーションエンジニアです。
アプリのデプロイにはAWS CDKでCodePipelineを作成しています。
CodePipelineでは、ビルドIDなどを次のアクションで使いたい場合のために、アクション間で変数の受け渡しをすることができます。
また、AWS CDKを作成するときに間違えやすいところもあったため、そちらもご共有しようと思います。
なお、AWSコンソール上から変数を受け渡しする方法は、以前こちらでご紹介しています。
tech-blog.yayoi-kk.co.jp
今回の作成するCodePipeline
今回はDockerビルドするだけの単純なデプロイPipelineを作成していきます。
デプロイ元のソースにはS3バケットを使用し、バケット内のファイルが更新された時、ビルド処理が実行されるようにします。
Dockerビルドのタグには一意の文字列を設定し、後続のアクションに受け渡しします。

Pipelineを作成するCDKコード
前述のPipeline Stackを作成するCDKコードは以下になります。
TypeScriptで作成しています。
import { Stack, StackProps, RemovalPolicy } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as logs from "aws-cdk-lib/aws-logs";
import * as codebuild from "aws-cdk-lib/aws-codebuild";
import * as codepipeline from "aws-cdk-lib/aws-codepipeline";
import * as codepipeline_actions from "aws-cdk-lib/aws-codepipeline-actions";
export class CdkPipelineStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const pipeline = new codepipeline.Pipeline(this, "MyFirstPipeline", {
pipelineName: "environmentPipeline",
crossAccountKeys: false,
});
const sourceBucket = new s3.Bucket(this, "sourceBucket", {
bucketName: "(bucket name)",
versioned: true,
removalPolicy: RemovalPolicy.DESTROY,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});
const sourceOutput = new codepipeline.Artifact();
const sourceAction = new codepipeline_actions.S3SourceAction({
actionName: "S3Source",
bucket: sourceBucket,
bucketKey: "s3Source.zip",
output: sourceOutput,
});
pipeline.addStage({
stageName: "Source",
actions: [sourceAction],
});
const buildProject = new codebuild.PipelineProject(this, `buildProject`, {
projectName: "buildProject",
buildSpec: codebuild.BuildSpec.fromSourceFilename(
"./build/buildspec.yml"
),
logging: {
cloudWatch: {
logGroup: new logs.LogGroup(this, `buildProjectLogs`),
},
},
environment: {
buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_3,
privileged: true,
},
});
const buildAction = new codepipeline_actions.CodeBuildAction({
actionName: "buildAction",
project: buildProject,
input: sourceOutput,
});
pipeline.addStage({
stageName: "Build",
actions: [buildAction],
});
const deployProject = new codebuild.PipelineProject(this, `deployProject`, {
projectName: "deployProject",
buildSpec: codebuild.BuildSpec.fromSourceFilename(
"./deploy/buildspec.yml"
),
logging: {
cloudWatch: {
logGroup: new logs.LogGroup(this, `deployProjectLogs`),
},
},
environment: {
buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_3,
privileged: true,
},
});
const deployAction = new codepipeline_actions.CodeBuildAction({
actionName: "deployAction",
project: deployProject,
input: sourceOutput,
environmentVariables: {
EXPORTED_UUID: {
value: buildAction.variable("EXPORTED_UUID"),
},
},
});
pipeline.addStage({
stageName: "Deploy",
actions: [deployAction],
});
}
}
Buildspec
Buildで実行されるBuildspec
version: 0.2
env:
exported-variables:
- EXPORTED_UUID
phases:
pre_build:
commands:
- export EXPORTED_UUID=$(uuidgen)
build:
commands:
- docker build -t test-image:${EXPORTED_UUID} -f ./Dockerfile .
Deployで実行されるBuildspec
version: 0.2
phases:
build:
commands:
- echo ${EXPORTED_UUID}
変数の受け渡し方法
CDKで変数を受け渡すポイントとしては、以下になります。
- 渡す側はBuildspecのexported-variablesに渡したい変数を指定する
- 受ける側はCDKのアクション定義で渡す側の変数定義を指定する
この例ではビルド時にUUIDでDockerイメージのタグ値を生成し、デプロイ時にタグ値を変数で受け取ってデプロイ対象イメージを指定しようとしています。
渡す側
exported-variables に EXPORTED_UUID
を指定します。
version: 0.2
env:
+ exported-variables:
+ - EXPORTED_UUID
phases:
pre_build:
commands:
- export EXPORTED_UUID=$(uuidgen)
build:
commands:
- docker build -t test-image:${EXPORTED_UUID} -f ./Dockerfile .
受ける側
受け渡したいアクションの CodeBuildAction に、buildAction.variable("変数名")
で設定します。
const buildAction = new codepipeline_actions.CodeBuildAction({
actionName: "buildAction",
project: buildProject,
input: sourceOutput,
});
~~ 省略 ~~
const deployAction = new codepipeline_actions.CodeBuildAction({
actionName: "deployAction",
project: deployProject,
input: sourceOutput,
environmentVariables: {
+ EXPORTED_UUID: {
+ value: buildAction.variable("EXPORTED_UUID"),
+ },
},
});
CDKデプロイ
CDKコードをデプロイ後に、AWSコンソール上から確認してみます。
渡す側のアクションには変数の名前空間がセットされています。

受ける側のアクションには環境変数がセットされています。

また、Pipelineを実行したときのログを見てみると、UUIDが受け渡しできていることがわかります。
...
[Container] 2022/07/19 09:15:06 Phase context status code: Message:
[Container] 2022/07/19 09:15:06 Entering phase BUILD
[Container] 2022/07/19 09:15:06 Running command echo ${EXPORTED_UUID}
+ 576854ed-8750-4cc8-bc6b-6da942c9c92b
...
NGの例
環境変数は PipelineProject の environmentVariables
にも設定できますが、こちらでは環境変数を受け渡すことはできません。
// Deploy
const deployProject = new codebuild.PipelineProject(this, `deployProject`, {
projectName: "deployProject",
buildSpec: codebuild.BuildSpec.fromSourceFilename(
"./deploy/buildspec.yml"
),
logging: {
cloudWatch: {
logGroup: new logs.LogGroup(this, `deployProjectLogs`),
},
},
environment: {
buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_3,
privileged: true,
+ environmentVariables: {
+ EXPORTED_UUID: {
+ value: buildAction.variable("EXPORTED_UUID"),
+ },
+ },
},
});
const deployAction = new codepipeline_actions.CodeBuildAction({
actionName: "deployAction",
project: deployProject,
input: sourceOutput,
});
このCDKコードをデプロイすると、「変数の名前空間」は自動で設定され、

CodeBuildの環境変数にそれっぽく設定されてしまいます。

しかし、実行してたときのログは、以下のようにただの文字列としか認識していません。
...
[Container] 2022/07/19 09:08:03 Phase context status code: Message:
[Container] 2022/07/19 09:08:03 Entering phase BUILD
[Container] 2022/07/19 09:08:03 Running command echo ${EXPORTED_UUID}
+ #{Build_buildAction_NS.EXPORTED_UUID}
...
最後に
CDKで変数を受け渡すとき、注意ポイントをまとめると
- 渡す側はBuildspecファイルに書く
- 受ける側はCDKのCodeBuildActionの環境変数に書く
- 受ける側を誤ってCDKのPipelineProjectの環境変数に書くと変数展開されず期待通り動かない
私はNGの例のように、PipelineProject の environmentVariables
に設定していることになかなか気づけませんでした...
(そもそも、CodePipeline自体に環境変数設定できることさえ知りませんでした...)
これからAWS CDK で CodePipeline構築する方はお気をつけください。
一緒に働く仲間を募集しています
herp.careers