🍺👨🏻‍💻🍳 pirosikick.com

CDK Pipelinesの新しいAPIでマルチアカウントのCI/CD

副業先でAWS CDKを使ってアプリケーションのプロビジョニングを実行している。最近、副業先でControlTowerを導入したので、マルチアカウント環境+AWS CDKでCI/CD環境を構築する方法について調べていたところ、以下の神記事を見つけた。

CDK Pipelines: AWS CDK アプリケーションの継続的デリバリ | Amazon Web Services ブログ

俺のやりたかったことが全て書いてあるぞ!と思ったが、実際に手元でコードを写経してみるとTypeScriptがエラーを吐く。調べてみると、上記記事は1年前くらいのもので、CDK Pipelinesの前世代のAPI(the original API)を使っているらしい。今は新しいAPI(the modern API)の使用が推奨されており、写経する上で記事のコードを少し変える必要があったので、変更箇所をまとめる。

※変更点しか書いていないので、上記の記事を先に読んだほうがよいと思います。

CodePipelineを使う

記事ではCdkPipelineコンストラクタを使っているが、新しいAPIではCodePipelineコンストラクタを使う。

// 旧
const pipeline = new CdkPipeline(this, 'Pipeline', { … });

// 新
const pipeline = new CodePipeline(this, 'Pipeline', { … });

CdkPipelineコンストラクタのsynthActionは、CodePipelineではsynthになっている。また、そこには、ShellStepクラスのインスタンスを渡す。

// 旧
const pipeline = new CdkPipeline(this, 'Pipeline', {
	…,
	synthAction: SimpleSynthAction.standardNpmSynth({
		sourceArtifact,
		cloudAssemblyArtifact,
		buildCommand: 'npm run build'
	})
	…,
});

// 新
const pipeline = new CodePipeline(this, 'Pipeline', {
	synth: new ShellStep("Synth", {
		input: …後述…,
		commands: ["npm ci", "npm run build", "npx cdk synth"],
	}),
});

ちなみに、ShellStepクラスは色んなとこで使う(例えば、pipeline.addStageのpre or postなど)。また、CDK Pipelinesは、ShellStepクラスのインスタンスごとにCodeBuildのプロジェクトを生成する。

GitHubとの接続

新しいAPIでGitHubとOAuthのアクセストークンを使って接続するには、CodePipelineSource.gitHub関数を使う。Artifactインスタンスが不要になったので、古いAPIより少しシンプルになった。

// 旧
const pipeline = new CdkPipeline(this, 'Pipeline', {
	…,
	sourceAction: new codepipeline_actions.GithubSourceAction({
		actionName: 'GitHub',
		output: sourceArtifact,
		oauthToke: SecretValue.secretsManager('github-token'),
		owner: 'OWNER',
		repo: 'REPO',
	}),
	…,
});

// 新
const pipeline = new CodePipeline(this, 'Pipeline', {
	synth: new ShellStep("Synth", {
		input: CodePipelineSource.gitHub('OWNER/REPO', 'ブランチ名', {
			authentication: SecretValue.secretsManager('github-token'), // optional
		}),
		commands: ["npm ci", "npm run build", "npx cdk synth"],
	}),
	…,
});

ドキュメントを読むと、AWSコンソールでGitHub(または、その他のリポジトリサービス)との接続を作成・利用する方式が推奨されている。接続の作成は、AWSコンソール>CodePipelineのコンソール>左メニューの設定>接続から行える。接続を作成するとARNがAWSコンソールに表示されるので、それをコピーして使う。

const pipeline = new CodePipeline(this, 'Pipeline', {
	synth: new ShellStep("Synth", {
		input: CodePipelineSource.connection('org/repo', 'branch', {
			connectionArn: '接続のARN',
		}),
		…,
	}),
	…,
});

ブートストラップ

CDK Pipelinesでは、新しいブートストラップスタックが必要。cdk.jsonに"@aws-cdk/core:newStyleStackSynthesis": trueがあり、かつ、cdk.jsonがあるディレクトリでcdk bootstrapを実行する場合は、自動で新しいブートストラップスタックを作成する。そうでない場合は、CDK_NEW_BOOTSTRAP=1を付けてcdk bootstrapを実行する必要がある。

$ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap …

ちなみに、新しいブートストラップスタックはAWS CDK v2ではデフォルトになるらしい。

古いブートストラップスタックからの移行はここらへんに書いてます。

バケットの暗号化

Pipelineのアカウントとデプロイ先アカウントが別の場合、Artifactを置くS3バケットの暗号化が必須になる。CodePipelineコンストラクタにcrossAccountKeys: trueを付けると、暗号化が有効になる。

const pipeline = new CodePipeline(this, 'Pipeline', {
	input: …,
	crossAccountKeys: true,
});

ステージの追加

ステージの追加は、addApplicationStageメソッドからaddStageメソッドに変更になっている。引数に渡すのはStageクラスのサブクラスという点は変わらない。

// 旧
pipeline.addApplicationStage(new CdkpipelinesDemoStage(this, 'PreProd', {
  env: { account: 'ACCOUNT1', region: 'REGION' }
}));

// 新
pipeline.addStage(new CdkpipelinesDemoStage(this, 'PreProd', {
  env: { account: 'ACCOUNT1', region: 'REGION' }
}));

検証の追加

記事では、API GatewayのエンドポイントのURLに対してcurlコマンドを実行する検証のステージをpipeline.addActionsメソッドで追加している。

// 旧
const preprod = new CdkpipelinesDemoStage(this, 'PreProd', {
  env: { account: 'ACCOUNT1', region: 'us-east-2' }
});
const preprodStage = pipeline.addApplicationStage(preprod);
preprodStage.addActions(new ShellScriptAction({
  actionName: 'TestService',
  useOutputs: {
    ENDPOINT_URL: pipeline.stackOutput(preprod.urlOutput),
  },
  commands: [
    'curl -Ssf $ENDPOINT_URL',
  ],
}));

新しいAPIでは、pipeline.addStageメソッドの第2引数にオプションとして渡せるようになっている。以下のコードでは、postでステージ実行後の処理(curlコマンドの実行)を記述している。

// 新
const prod = new CdkpipelinesDemoStage(this, "PreProd", {
  env: { account: 'ACCOUNT1', region: 'REGION' },
});

pipeline.addStage(prod, {
  post: [
    new ShellStep("PostPreProd", {
      envFromCfnOutputs: {
        ENDPOINT_URL: prod.urlOutput,
      },
      commands: ["curl -Ssf $ENDPOINT_URL"],
    }),
  ],
});

preを使うことで、ステージ実行前の処理を追加することもできる。preにManualApprovalStepクラスのインスタンスを渡すと、ステージ実行前に手動による承認を挟むことができ、便利そう。

おわり

新しいAPIのほうがシンプルで洗練されているし、ステージの並列実行もサポートしているらしい。CDK Pipelinesを使うとAWS CDKによるCI/CD環境を簡単に構築できて、最高〜。

参考資料