CDK Pipelines を使用した継続的な統合と配信 (CI/CD) - AWS Cloud Development Kit (AWS CDK) v2

これは AWS CDK v2 開発者ガイドです。古い CDK v1 は 2022 年 6 月 1 日にメンテナンスを開始し、2023 年 6 月 1 日にサポートを終了しました。

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

CDK Pipelines を使用した継続的な統合と配信 (CI/CD)

Construct Library の CDK Pipelines AWS モジュールを使用して、 AWS CDK アプリケーションの継続的な配信を設定します。CDK アプリケーションのソースコードを AWS CodeCommit、GitHub、または にコミットすると AWS CodeStar、CDK Pipelines は新しいバージョンを自動的に構築、テスト、デプロイできます。

CDK Pipelines は自己更新しています。アプリケーションステージまたはスタックを追加すると、パイプラインは自動的に新しいステージまたはスタックをデプロイするように再設定します。

注記

CDK Pipelines は 2 つの APIsをサポートしています。1 つは CDK Pipelines デベロッパープレビューで公開された元の API です。もう 1 つは、プレビューフェーズで受け取った CDK 顧客からのフィードバックが組み込まれた最新の API です。このトピックの例では、最新の API を使用しています。サポートされている 2 つの APIs、aws-cdk GitHubリポジトリCDK Pipelines の元の API を参照してください。

AWS 環境のブートストラップ

CDK Pipelines を使用する前に、スタックをデプロイする AWS 環境をブートストラップする必要があります。

CDK パイプラインには少なくとも 2 つの環境が含まれます。最初の環境は、パイプラインがプロビジョニングされる場所です。2 つ目の環境は、アプリケーションのスタックまたはステージを にデプロイする場所です (ステージは関連するスタックのグループです)。これらの環境は同じでもかまいませんが、ベストプラクティスとして、異なる環境でステージを互いに分離することをお勧めします。

注記

ブートストラップによって作成されるリソースの種類と、ブートストラップスタックをカスタマイズする方法の詳細については、ブートストラッピング「」を参照してください。

CDK Pipelines を使用した継続的デプロイでは、CDK Toolkit スタックに以下を含める必要があります。

  • 1 つの Amazon Simple Storage Service (Amazon S3) バケット

  • Amazon ECR リポジトリ。

  • パイプラインのさまざまな部分に必要なアクセス許可を付与する IAM ロール。

CDK Toolkit は、既存のブートストラップスタックをアップグレードするか、必要に応じて新しいブートストラップスタックを作成します。

AWS CDK パイプラインをプロビジョニングできる環境をブートストラップするには、次の例cdk bootstrapに示すように を呼び出します。npx コマンドを使用して AWS CDK Toolkit を呼び出すと、必要に応じて一時的にインストールされます。また、存在する場合は、現在のプロジェクトにインストールされている ツールキットのバージョンも使用します。

--cloudformation-execution-policies は、今後の CDK Pipelines デプロイが実行されるポリシーの ARN を指定します。デフォルトAdministratorAccessポリシーでは、パイプラインがすべてのタイプの AWS リソースをデプロイできるようにします。このポリシーを使用する場合は、 AWS CDK アプリを構成するすべてのコードと依存関係を信頼してください。

ほとんどの組織では、自動化によってデプロイできるリソースの種類をより厳密に制御する必要があります。組織内の適切な部門に問い合わせて、パイプラインで使用するポリシーを決定します。

デフォルトの AWS プロファイルに必要な認証設定と が含まれている場合は、 --profileオプションを省略できます AWS リージョン。

macOS/Linux
npx cdk bootstrap aws://ACCOUNT-NUMBER/REGION --profile ADMIN-PROFILE \ --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
Windows
npx cdk bootstrap aws://ACCOUNT-NUMBER/REGION --profile ADMIN-PROFILE ^ --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess

パイプラインによって AWS CDK アプリケーションがデプロイされる追加の環境をブートストラップするには、代わりに次のコマンドを使用します。--trust オプションは、この環境に AWS CDK アプリケーションをデプロイするアクセス許可を持つ他のアカウントを示します。このオプションでは、パイプラインの AWS アカウント ID を指定します。

デフォルトの AWS プロファイルに必要な認証設定と が含まれている場合は、ここでも --profileオプションを省略できます AWS リージョン。

macOS/Linux
npx cdk bootstrap aws://ACCOUNT-NUMBER/REGION --profile ADMIN-PROFILE \ --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \ --trust PIPELINE-ACCOUNT-NUMBER
Windows
npx cdk bootstrap aws://ACCOUNT-NUMBER/REGION --profile ADMIN-PROFILE ^ --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess ^ --trust PIPELINE-ACCOUNT-NUMBER
ヒント

管理認証情報は、ブートストラップと初期パイプラインのプロビジョニングにのみ使用します。その後、ローカルマシンではなくパイプライン自体を使用して変更をデプロイします。

従来のブートストラップ環境をアップグレードする場合、新しいバケットが作成されると、以前の Amazon S3 バケットは孤立します。Amazon S3 コンソールを使用して手動で削除します。

プロジェクトを初期化する

新しい空のGitHubプロジェクトを作成し、 my-pipeline ディレクトリのワークステーションにクローンを作成します。(このトピックのコード例では、 を使用します GitHub。 または を使用 AWS CodeStar することもできます) AWS CodeCommit。

git clone GITHUB-CLONE-URL my-pipeline cd my-pipeline
注記

アプリのメインディレクトリmy-pipelineには、 以外の名前を使用できます。ただし、その場合は、このトピックの後半でファイル名とクラス名を微調整する必要があります。これは、 AWS CDK ツールキットが一部のファイル名とクラス名をメインディレクトリの名前に基づいているためです。

クローンを作成したら、通常どおりプロジェクトを初期化します。

TypeScript
cdk init app --language typescript
JavaScript
cdk init app --language javascript
Python
cdk init app --language python

アプリを作成したら、次の 2 つのコマンドも入力します。これにより、アプリケーションの Python 仮想環境がアクティブ化され、 AWS CDK コア依存関係がインストールされます。

source .venv/bin/activate python -m pip install -r requirements.txt
Java
cdk init app --language java

IDE を使用している場合は、プロジェクトを開くかインポートできます。例えば、Eclipse では、ファイル > インポート > Maven > 既存の Maven プロジェクト を選択します。プロジェクト設定が Java 8 (1.8) を使用するように設定されていることを確認します。

C#
cdk init app --language csharp

Visual Studio を使用している場合は、 src ディレクトリでソリューションファイルを開きます。

Go
cdk init app --language go

アプリが作成されたら、次のコマンドも入力して、アプリに必要な AWS 構成ライブラリモジュールをインストールします。

go get
重要

cdk.json および cdk.context.jsonファイルをソース管理にコミットしてください。コンテキスト情報 ( AWS アカウントから取得した機能フラグやキャッシュ値など) は、プロジェクトの状態の一部です。別の環境では値が異なる場合があり、その結果に予期しない変更が生じる可能性があります。詳細については、「ランタイムのコンテキスト」を参照してください。

パイプラインを定義する

CDK Pipelines アプリケーションには、パイプライン自体を表すスタックと、パイプラインを通じてデプロイされたアプリケーションを表すスタックの 2 つ以上のスタックが含まれます。スタックはステージ にグループ化することもできます。ステージ を使用して、インフラストラクチャスタックのコピーをさまざまな環境にデプロイできます。ここでは、パイプラインについて考え、後でデプロイするアプリケーションを詳しく説明します。

コンストラクトCodePipelineは、デプロイエンジン AWS CodePipeline として を使用する CDK パイプラインを表すコンストラクトです。スタックCodePipelineで をインスタンス化するときは、パイプラインのソースの場所 ( GitHub リポジトリなど) を定義します。また、アプリケーションを構築するためのコマンドも定義します。

例えば、次の例では、ソースが GitHub リポジトリに保存されているパイプラインを定義します。 TypeScript CDK アプリケーションのビルドステップも含まれています。示されている場合は、 GitHub リポジトリに関する情報を入力します。

注記

デフォルトでは、パイプラインは Secrets Manager に保存されている個人アクセストークンを という名前で GitHub 使用して を認証しますgithub-token

また、パイプラインスタックのインスタンス化を更新して、 AWS アカウントとリージョンを指定する必要があります。

TypeScript

lib/my-pipeline-stack.ts (プロジェクトフォルダの名前が でない場合、 は異なる場合がありますmy-pipeline)。

import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines'; export class MyPipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'MyPipeline', synth: new ShellStep('Synth', { input: CodePipelineSource.gitHub('OWNER/REPO', 'main'), commands: ['npm ci', 'npm run build', 'npx cdk synth'] }) }); } }

(プロジェクトフォルダの名前が でない場合bin/my-pipeline.ts、 は異なる場合がありますmy-pipeline)。

#!/usr/bin/env node import * as cdk from 'aws-cdk-lib'; import { MyPipelineStack } from '../lib/my-pipeline-stack'; const app = new cdk.App(); new MyPipelineStack(app, 'MyPipelineStack', { env: { account: '111111111111', region: 'eu-west-1', } }); app.synth();
JavaScript

lib/my-pipeline-stack.js (プロジェクトフォルダの名前が でない場合、 は異なる場合がありますmy-pipeline)。

const cdk = require('aws-cdk-lib'); const { CodePipeline, CodePipelineSource, ShellStep } = require('aws-cdk-lib/pipelines'); class MyPipelineStack extends cdk.Stack { constructor(scope, id, props) { super(scope, id, props); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'MyPipeline', synth: new ShellStep('Synth', { input: CodePipelineSource.gitHub('OWNER/REPO', 'main'), commands: ['npm ci', 'npm run build', 'npx cdk synth'] }) }); } } module.exports = { MyPipelineStack }

(プロジェクトフォルダの名前が でない場合bin/my-pipeline.js、 は異なる場合がありますmy-pipeline)。

#!/usr/bin/env node const cdk = require('aws-cdk-lib'); const { MyPipelineStack } = require('../lib/my-pipeline-stack'); const app = new cdk.App(); new MyPipelineStack(app, 'MyPipelineStack', { env: { account: '111111111111', region: 'eu-west-1', } }); app.synth();
Python

my-pipeline/my-pipeline-stack.py (プロジェクトフォルダの名前が でない場合、 は異なる場合がありますmy-pipeline)。

import aws_cdk as cdk from constructs import Construct from aws_cdk.pipelines import CodePipeline, CodePipelineSource, ShellStep class MyPipelineStack(cdk.Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) pipeline = CodePipeline(self, "Pipeline", pipeline_name="MyPipeline", synth=ShellStep("Synth", input=CodePipelineSource.git_hub("OWNER/REPO", "main"), commands=["npm install -g aws-cdk", "python -m pip install -r requirements.txt", "cdk synth"] ) )

Eclipse app.py:

#!/usr/bin/env python3 import aws_cdk as cdk from my_pipeline.my_pipeline_stack import MyPipelineStack app = cdk.App() MyPipelineStack(app, "MyPipelineStack", env=cdk.Environment(account="111111111111", region="eu-west-1") ) app.synth()
Java

src/main/java/com/myorg/MyPipelineStack.java (プロジェクトフォルダの名前が でない場合、 は異なる場合がありますmy-pipeline)。

package com.myorg; import java.util.Arrays; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; import software.amazon.awscdk.pipelines.CodePipeline; import software.amazon.awscdk.pipelines.CodePipelineSource; import software.amazon.awscdk.pipelines.ShellStep; public class MyPipelineStack extends Stack { public MyPipelineStack(final Construct scope, final String id) { this(scope, id, null); } public MyPipelineStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); CodePipeline pipeline = CodePipeline.Builder.create(this, "pipeline") .pipelineName("MyPipeline") .synth(ShellStep.Builder.create("Synth") .input(CodePipelineSource.gitHub("OWNER/REPO", "main")) .commands(Arrays.asList("npm install -g aws-cdk", "cdk synth")) .build()) .build(); } }

では src/main/java/com/myorg/MyPipelineApp.java (プロジェクトフォルダの名前が でない場合、異なる場合がありますmy-pipeline)。

package com.myorg; import software.amazon.awscdk.App; import software.amazon.awscdk.Environment; import software.amazon.awscdk.StackProps; public class MyPipelineApp { public static void main(final String[] args) { App app = new App(); new MyPipelineStack(app, "PipelineStack", StackProps.builder() .env(Environment.builder() .account("111111111111") .region("eu-west-1") .build()) .build()); app.synth(); } }
C#

src/MyPipeline/MyPipelineStack.cs (プロジェクトフォルダの名前が でない場合、 は異なる場合がありますmy-pipeline)。

using Amazon.CDK; using Amazon.CDK.Pipelines; namespace MyPipeline { public class MyPipelineStack : Stack { internal MyPipelineStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var pipeline = new CodePipeline(this, "pipeline", new CodePipelineProps { PipelineName = "MyPipeline", Synth = new ShellStep("Synth", new ShellStepProps { Input = CodePipelineSource.GitHub("OWNER/REPO", "main"), Commands = new string[] { "npm install -g aws-cdk", "cdk synth" } }) }); } } }

では src/MyPipeline/Program.cs (プロジェクトフォルダの名前が でない場合、異なる場合がありますmy-pipeline)。

using Amazon.CDK; namespace MyPipeline { sealed class Program { public static void Main(string[] args) { var app = new App(); new MyPipelineStack(app, "MyPipelineStack", new StackProps { Env = new Amazon.CDK.Environment { Account = "111111111111", Region = "eu-west-1" } }); app.Synth(); } } }

パイプラインは 1 回手動でデプロイする必要があります。その後、パイプラインはソースコードリポジトリから自身を最新の状態に保ちます。したがって、リポジトリ内のコードがデプロイするコードであることを確認してください。変更を確認して にプッシュし GitHub、以下をデプロイします。

git add --all git commit -m "initial commit" git push cdk deploy
ヒント

最初のデプロイが完了したので、ローカル AWS アカウントは管理アクセスを必要としなくなりました。これは、アプリケーションへのすべての変更がパイプラインを介してデプロイされるためです。必要なことは、 にプッシュすることだけです GitHub。

アプリケーションステージ

パイプラインに一度に追加できるマルチスタック AWS アプリケーションを定義するには、 のサブクラスを定義しますStage。(これは CDK Pipelines モジュールの とは異なりCdkStageます。)

ステージには、アプリケーションを構成するスタックが含まれています。スタック間に依存関係がある場合、スタックは自動的に正しい順序でパイプラインに追加されます。相互に依存しないスタックは並行してデプロイされます。を呼び出すことで、スタック間に依存関係を追加できますstack1.addDependency(stack2)

ステージはデフォルトの env引数を受け入れ、その中のスタックのデフォルト環境になります。(スタックには、引き続き独自の環境を指定できます)。

アプリケーションをパイプラインに追加するには、 のインスタンスaddStage()で を呼び出しますStage。DTAP またはマルチリージョンアプリケーションパイプラインのさまざまなステージを定義するために、ステージをインスタンス化してパイプラインに複数回追加できます。

シンプルな Lambda 関数を含むスタックを作成し、そのスタックをステージに配置します。次に、デプロイできるようにステージをパイプラインに追加します。

TypeScript

Lambda 関数を含むアプリケーションスタックlib/my-pipeline-lambda-stack.tsを保持する新しい ファイルを作成します。

import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { Function, InlineCode, Runtime } from 'aws-cdk-lib/aws-lambda'; export class MyLambdaStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); new Function(this, 'LambdaFunction', { runtime: Runtime.NODEJS_18_X, handler: 'index.handler', code: new InlineCode('exports.handler = _ => "Hello, CDK";') }); } }

ステージlib/my-pipeline-app-stage.tsを保持する新しい ファイルを作成します。

import * as cdk from 'aws-cdk-lib'; import { Construct } from "constructs"; import { MyLambdaStack } from './my-pipeline-lambda-stack'; export class MyPipelineAppStage extends cdk.Stage { constructor(scope: Construct, id: string, props?: cdk.StageProps) { super(scope, id, props); const lambdaStack = new MyLambdaStack(this, 'LambdaStack'); } }

を編集lib/my-pipeline-stack.tsしてパイプラインにステージを追加します。

import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines'; import { MyPipelineAppStage } from './my-pipeline-app-stage'; export class MyPipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'MyPipeline', synth: new ShellStep('Synth', { input: CodePipelineSource.gitHub('OWNER/REPO', 'main'), commands: ['npm ci', 'npm run build', 'npx cdk synth'] }) }); pipeline.addStage(new MyPipelineAppStage(this, "test", { env: { account: "111111111111", region: "eu-west-1" } })); } }
JavaScript

Lambda 関数を含むアプリケーションスタックlib/my-pipeline-lambda-stack.jsを保持する新しい ファイルを作成します。

const cdk = require('aws-cdk-lib'); const { Function, InlineCode, Runtime } = require('aws-cdk-lib/aws-lambda'); class MyLambdaStack extends cdk.Stack { constructor(scope, id, props) { super(scope, id, props); new Function(this, 'LambdaFunction', { runtime: Runtime.NODEJS_18_X, handler: 'index.handler', code: new InlineCode('exports.handler = _ => "Hello, CDK";') }); } } module.exports = { MyLambdaStack }

ステージlib/my-pipeline-app-stage.jsを保持する新しい ファイルを作成します。

const cdk = require('aws-cdk-lib'); const { MyLambdaStack } = require('./my-pipeline-lambda-stack'); class MyPipelineAppStage extends cdk.Stage { constructor(scope, id, props) { super(scope, id, props); const lambdaStack = new MyLambdaStack(this, 'LambdaStack'); } } module.exports = { MyPipelineAppStage };

を編集lib/my-pipeline-stack.tsしてパイプラインにステージを追加します。

const cdk = require('aws-cdk-lib'); const { CodePipeline, CodePipelineSource, ShellStep } = require('aws-cdk-lib/pipelines'); const { MyPipelineAppStage } = require('./my-pipeline-app-stage'); class MyPipelineStack extends cdk.Stack { constructor(scope, id, props) { super(scope, id, props); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'MyPipeline', synth: new ShellStep('Synth', { input: CodePipelineSource.gitHub('OWNER/REPO', 'main'), commands: ['npm ci', 'npm run build', 'npx cdk synth'] }) }); pipeline.addStage(new MyPipelineAppStage(this, "test", { env: { account: "111111111111", region: "eu-west-1" } })); } } module.exports = { MyPipelineStack }
Python

Lambda 関数を含むアプリケーションスタックmy_pipeline/my_pipeline_lambda_stack.pyを保持する新しい ファイルを作成します。

import aws_cdk as cdk from constructs import Construct from aws_cdk.aws_lambda import Function, InlineCode, Runtime class MyLambdaStack(cdk.Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) Function(self, "LambdaFunction", runtime=Runtime.NODEJS_18_X, handler="index.handler", code=InlineCode("exports.handler = _ => 'Hello, CDK';") )

ステージmy_pipeline/my_pipeline_app_stage.pyを保持する新しい ファイルを作成します。

import aws_cdk as cdk from constructs import Construct from my_pipeline.my_pipeline_lambda_stack import MyLambdaStack class MyPipelineAppStage(cdk.Stage): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) lambdaStack = MyLambdaStack(self, "LambdaStack")

を編集my_pipeline/my-pipeline-stack.pyしてパイプラインにステージを追加します。

import aws_cdk as cdk from constructs import Construct from aws_cdk.pipelines import CodePipeline, CodePipelineSource, ShellStep from my_pipeline.my_pipeline_app_stage import MyPipelineAppStage class MyPipelineStack(cdk.Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) pipeline = CodePipeline(self, "Pipeline", pipeline_name="MyPipeline", synth=ShellStep("Synth", input=CodePipelineSource.git_hub("OWNER/REPO", "main"), commands=["npm install -g aws-cdk", "python -m pip install -r requirements.txt", "cdk synth"])) pipeline.add_stage(MyPipelineAppStage(self, "test", env=cdk.Environment(account="111111111111", region="eu-west-1")))
Java

Lambda 関数を含むアプリケーションスタックsrc/main/java/com.myorg/MyPipelineLambdaStack.javaを保持する新しい ファイルを作成します。

package com.myorg; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; import software.amazon.awscdk.services.lambda.Function; import software.amazon.awscdk.services.lambda.Runtime; import software.amazon.awscdk.services.lambda.InlineCode; public class MyPipelineLambdaStack extends Stack { public MyPipelineLambdaStack(final Construct scope, final String id) { this(scope, id, null); } public MyPipelineLambdaStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); Function.Builder.create(this, "LambdaFunction") .runtime(Runtime.NODEJS_18_X) .handler("index.handler") .code(new InlineCode("exports.handler = _ => 'Hello, CDK';")) .build(); } }

ステージsrc/main/java/com.myorg/MyPipelineAppStage.javaを保持する新しい ファイルを作成します。

package com.myorg; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.Stage; import software.amazon.awscdk.StageProps; public class MyPipelineAppStage extends Stage { public MyPipelineAppStage(final Construct scope, final String id) { this(scope, id, null); } public MyPipelineAppStage(final Construct scope, final String id, final StageProps props) { super(scope, id, props); Stack lambdaStack = new MyPipelineLambdaStack(this, "LambdaStack"); } }

を編集src/main/java/com.myorg/MyPipelineStack.javaしてパイプラインにステージを追加します。

package com.myorg; import java.util.Arrays; import software.constructs.Construct; import software.amazon.awscdk.Environment; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; import software.amazon.awscdk.StageProps; import software.amazon.awscdk.pipelines.CodePipeline; import software.amazon.awscdk.pipelines.CodePipelineSource; import software.amazon.awscdk.pipelines.ShellStep; public class MyPipelineStack extends Stack { public MyPipelineStack(final Construct scope, final String id) { this(scope, id, null); } public MyPipelineStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); final CodePipeline pipeline = CodePipeline.Builder.create(this, "pipeline") .pipelineName("MyPipeline") .synth(ShellStep.Builder.create("Synth") .input(CodePipelineSource.gitHub("OWNER/REPO", "main")) .commands(Arrays.asList("npm install -g aws-cdk", "cdk synth")) .build()) .build(); pipeline.addStage(new MyPipelineAppStage(this, "test", StageProps.builder() .env(Environment.builder() .account("111111111111") .region("eu-west-1") .build()) .build())); } }
C#

Lambda 関数を含むアプリケーションスタックsrc/MyPipeline/MyPipelineLambdaStack.csを保持する新しい ファイルを作成します。

using Amazon.CDK; using Constructs; using Amazon.CDK.AWS.Lambda; namespace MyPipeline { class MyPipelineLambdaStack : Stack { public MyPipelineLambdaStack(Construct scope, string id, StackProps props=null) : base(scope, id, props) { new Function(this, "LambdaFunction", new FunctionProps { Runtime = Runtime.NODEJS_18_X, Handler = "index.handler", Code = new InlineCode("exports.handler = _ => 'Hello, CDK';") }); } } }

ステージsrc/MyPipeline/MyPipelineAppStage.csを保持する新しい ファイルを作成します。

using Amazon.CDK; using Constructs; namespace MyPipeline { class MyPipelineAppStage : Stage { public MyPipelineAppStage(Construct scope, string id, StageProps props=null) : base(scope, id, props) { Stack lambdaStack = new MyPipelineLambdaStack(this, "LambdaStack"); } } }

を編集src/MyPipeline/MyPipelineStack.csしてパイプラインにステージを追加します。

using Amazon.CDK; using Constructs; using Amazon.CDK.Pipelines; namespace MyPipeline { public class MyPipelineStack : Stack { internal MyPipelineStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { var pipeline = new CodePipeline(this, "pipeline", new CodePipelineProps { PipelineName = "MyPipeline", Synth = new ShellStep("Synth", new ShellStepProps { Input = CodePipelineSource.GitHub("OWNER/REPO", "main"), Commands = new string[] { "npm install -g aws-cdk", "cdk synth" } }) }); pipeline.AddStage(new MyPipelineAppStage(this, "test", new StageProps { Env = new Environment { Account = "111111111111", Region = "eu-west-1" } })); } } }

によって追加されたすべてのアプリケーションステージaddStage()は、対応するパイプラインステージが追加され、 addStage()呼び出しによって返されるStageDeploymentインスタンスで表されます。デプロイ前またはデプロイ後のアクションをステージに追加するには、その addPre()または addPost()メソッドを呼び出します。

TypeScript
// import { ManualApprovalStep } from 'aws-cdk-lib/pipelines'; const testingStage = pipeline.addStage(new MyPipelineAppStage(this, 'testing', { env: { account: '111111111111', region: 'eu-west-1' } })); testingStage.addPost(new ManualApprovalStep('approval'));
JavaScript
// const { ManualApprovalStep } = require('aws-cdk-lib/pipelines'); const testingStage = pipeline.addStage(new MyPipelineAppStage(this, 'testing', { env: { account: '111111111111', region: 'eu-west-1' } })); testingStage.addPost(new ManualApprovalStep('approval'));
Python
# from aws_cdk.pipelines import ManualApprovalStep testing_stage = pipeline.add_stage(MyPipelineAppStage(self, "testing", env=cdk.Environment(account="111111111111", region="eu-west-1"))) testing_stage.add_post(ManualApprovalStep('approval'))
Java
// import software.amazon.awscdk.pipelines.StageDeployment; // import software.amazon.awscdk.pipelines.ManualApprovalStep; StageDeployment testingStage = pipeline.addStage(new MyPipelineAppStage(this, "test", StageProps.builder() .env(Environment.builder() .account("111111111111") .region("eu-west-1") .build()) .build())); testingStage.addPost(new ManualApprovalStep("approval"));
C#
var testingStage = pipeline.AddStage(new MyPipelineAppStage(this, "test", new StageProps { Env = new Environment { Account = "111111111111", Region = "eu-west-1" } })); testingStage.AddPost(new ManualApprovalStep("approval"));

Wave にステージを追加して、ステージを複数のアカウントまたはリージョンにhttps://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.Wave.htmlデプロイする場合などに、ステージを並行してデプロイできます。

TypeScript
const wave = pipeline.addWave('wave'); wave.addStage(new MyApplicationStage(this, 'MyAppEU', { env: { account: '111111111111', region: 'eu-west-1' } })); wave.addStage(new MyApplicationStage(this, 'MyAppUS', { env: { account: '111111111111', region: 'us-west-1' } }));
JavaScript
const wave = pipeline.addWave('wave'); wave.addStage(new MyApplicationStage(this, 'MyAppEU', { env: { account: '111111111111', region: 'eu-west-1' } })); wave.addStage(new MyApplicationStage(this, 'MyAppUS', { env: { account: '111111111111', region: 'us-west-1' } }));
Python
wave = pipeline.add_wave("wave") wave.add_stage(MyApplicationStage(self, "MyAppEU", env=cdk.Environment(account="111111111111", region="eu-west-1"))) wave.add_stage(MyApplicationStage(self, "MyAppUS", env=cdk.Environment(account="111111111111", region="us-west-1")))
Java
// import software.amazon.awscdk.pipelines.Wave; final Wave wave = pipeline.addWave("wave"); wave.addStage(new MyPipelineAppStage(this, "MyAppEU", StageProps.builder() .env(Environment.builder() .account("111111111111") .region("eu-west-1") .build()) .build())); wave.addStage(new MyPipelineAppStage(this, "MyAppUS", StageProps.builder() .env(Environment.builder() .account("111111111111") .region("us-west-1") .build()) .build()));
C#
var wave = pipeline.AddWave("wave"); wave.AddStage(new MyPipelineAppStage(this, "MyAppEU", new StageProps { Env = new Environment { Account = "111111111111", Region = "eu-west-1" } })); wave.AddStage(new MyPipelineAppStage(this, "MyAppUS", new StageProps { Env = new Environment { Account = "111111111111", Region = "us-west-1" } }));

デプロイのテスト

CDK パイプラインにステップを追加して、実行しているデプロイを検証できます。例えば、CDK パイプラインライブラリの を使用して、次のようなタスクShellStepを実行できます。

  • Lambda 関数によってバックアップされた、新しくデプロイされた Amazon API Gateway にアクセスしようとする

  • AWS CLI コマンドを発行してデプロイされたリソースの設定を確認する

最も単純な形式では、検証アクションの追加は次のようになります。

TypeScript
// stage was returned by pipeline.addStage stage.addPost(new ShellStep("validate", { commands: ['../tests/validate.sh'], }));
JavaScript
// stage was returned by pipeline.addStage stage.addPost(new ShellStep("validate", { commands: ['../tests/validate.sh'], }));
Python
# stage was returned by pipeline.add_stage stage.add_post(ShellStep("validate", commands=[''../tests/validate.sh''] ))
Java
// stage was returned by pipeline.addStage stage.addPost(ShellStep.Builder.create("validate") .commands(Arrays.asList("'../tests/validate.sh'")) .build());
C#
// stage was returned by pipeline.addStage stage.AddPost(new ShellStep("validate", new ShellStepProps { Commands = new string[] { "'../tests/validate.sh'" } }));

多くの AWS CloudFormation デプロイでは、名前が予測不可能なリソースが生成されます。このため、CDK Pipelines はデプロイ後に AWS CloudFormation 出力を読み取る方法を提供します。これにより、ロードバランサーの生成された URL をテストアクションに渡す (例えば) ことができます。

出力を使用するには、関心のあるCfnOutputオブジェクトを公開します。次に、ステップの envFromCfnOutputsプロパティに渡して、そのステップ内の環境変数として利用できるようにします。

TypeScript
// given a stack lbStack that exposes a load balancer construct as loadBalancer this.loadBalancerAddress = new cdk.CfnOutput(lbStack, 'LbAddress', { value: `https://${lbStack.loadBalancer.loadBalancerDnsName}/` }); // pass the load balancer address to a shell step stage.addPost(new ShellStep("lbaddr", { envFromCfnOutputs: {lb_addr: lbStack.loadBalancerAddress}, commands: ['echo $lb_addr'] }));
JavaScript
// given a stack lbStack that exposes a load balancer construct as loadBalancer this.loadBalancerAddress = new cdk.CfnOutput(lbStack, 'LbAddress', { value: `https://${lbStack.loadBalancer.loadBalancerDnsName}/` }); // pass the load balancer address to a shell step stage.addPost(new ShellStep("lbaddr", { envFromCfnOutputs: {lb_addr: lbStack.loadBalancerAddress}, commands: ['echo $lb_addr'] }));
Python
# given a stack lb_stack that exposes a load balancer construct as load_balancer self.load_balancer_address = cdk.CfnOutput(lb_stack, "LbAddress", value=f"https://{lb_stack.load_balancer.load_balancer_dns_name}/") # pass the load balancer address to a shell step stage.add_post(ShellStep("lbaddr", env_from_cfn_outputs={"lb_addr": lb_stack.load_balancer_address} commands=["echo $lb_addr"]))
Java
// given a stack lbStack that exposes a load balancer construct as loadBalancer loadBalancerAddress = CfnOutput.Builder.create(lbStack, "LbAddress") .value(String.format("https://%s/", lbStack.loadBalancer.loadBalancerDnsName)) .build(); stage.addPost(ShellStep.Builder.create("lbaddr") .envFromCfnOutputs( // Map.of requires Java 9 or later java.util.Map.of("lbAddr", loadBalancerAddress)) .commands(Arrays.asList("echo $lbAddr")) .build());
C#
// given a stack lbStack that exposes a load balancer construct as loadBalancer loadBalancerAddress = new CfnOutput(lbStack, "LbAddress", new CfnOutputProps { Value = string.Format("https://{0}/", lbStack.loadBalancer.LoadBalancerDnsName) }); stage.AddPost(new ShellStep("lbaddr", new ShellStepProps { EnvFromCfnOutputs = new Dictionary<string, CfnOutput> { { "lbAddr", loadBalancerAddress } }, Commands = new string[] { "echo $lbAddr" } }));

簡単な検証テストは で適切に記述できますがShellStep、テストが数行を超えると、このアプローチは実行しにくくなります。より複雑なテストでは、 inputsプロパティShellStepを介して追加のファイル (完全なシェルスクリプトや他の言語のプログラムなど) を に取り込むことができます。入力は、ソース ( GitHub リポジトリなど) や別の など、出力を持つ任意のステップにすることができますShellStep

テストでファイルが直接使用できる場合 (たとえば、ファイル自体が実行可能である場合)、ソースリポジトリからファイルを取り込むのが適切です。この例では、 GitHub リポジトリを source ( の一部としてインラインでインスタンス化するのではなく) として宣言しますCodePipeline。次に、このファイルセットをパイプラインと検証テストの両方に渡します。

TypeScript
const source = CodePipelineSource.gitHub('OWNER/REPO', 'main'); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'MyPipeline', synth: new ShellStep('Synth', { input: source, commands: ['npm ci', 'npm run build', 'npx cdk synth'] }) }); const stage = pipeline.addStage(new MyPipelineAppStage(this, 'test', { env: { account: '111111111111', region: 'eu-west-1' } })); stage.addPost(new ShellStep('validate', { input: source, commands: ['sh ../tests/validate.sh'] }));
JavaScript
const source = CodePipelineSource.gitHub('OWNER/REPO', 'main'); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'MyPipeline', synth: new ShellStep('Synth', { input: source, commands: ['npm ci', 'npm run build', 'npx cdk synth'] }) }); const stage = pipeline.addStage(new MyPipelineAppStage(this, 'test', { env: { account: '111111111111', region: 'eu-west-1' } })); stage.addPost(new ShellStep('validate', { input: source, commands: ['sh ../tests/validate.sh'] }));
Python
source = CodePipelineSource.git_hub("OWNER/REPO", "main") pipeline = CodePipeline(self, "Pipeline", pipeline_name="MyPipeline", synth=ShellStep("Synth", input=source, commands=["npm install -g aws-cdk", "python -m pip install -r requirements.txt", "cdk synth"])) stage = pipeline.add_stage(MyApplicationStage(self, "test", env=cdk.Environment(account="111111111111", region="eu-west-1"))) stage.add_post(ShellStep("validate", input=source, commands=["sh ../tests/validate.sh"], ))
Java
final CodePipelineSource source = CodePipelineSource.gitHub("OWNER/REPO", "main"); final CodePipeline pipeline = CodePipeline.Builder.create(this, "pipeline") .pipelineName("MyPipeline") .synth(ShellStep.Builder.create("Synth") .input(source) .commands(Arrays.asList("npm install -g aws-cdk", "cdk synth")) .build()) .build(); final StageDeployment stage = pipeline.addStage(new MyPipelineAppStage(this, "test", StageProps.builder() .env(Environment.builder() .account("111111111111") .region("eu-west-1") .build()) .build())); stage.addPost(ShellStep.Builder.create("validate") .input(source) .commands(Arrays.asList("sh ../tests/validate.sh")) .build());
C#
var source = CodePipelineSource.GitHub("OWNER/REPO", "main"); var pipeline = new CodePipeline(this, "pipeline", new CodePipelineProps { PipelineName = "MyPipeline", Synth = new ShellStep("Synth", new ShellStepProps { Input = source, Commands = new string[] { "npm install -g aws-cdk", "cdk synth" } }) }); var stage = pipeline.AddStage(new MyPipelineAppStage(this, "test", new StageProps { Env = new Environment { Account = "111111111111", Region = "eu-west-1" } })); stage.AddPost(new ShellStep("validate", new ShellStepProps { Input = source, Commands = new string[] { "sh ../tests/validate.sh" } }));

テストをコンパイルする必要がある場合は、合成ステップから追加のファイルを取得することをお勧めします。これは合成の一部として行われます。

TypeScript
const synthStep = new ShellStep('Synth', { input: CodePipelineSource.gitHub('OWNER/REPO', 'main'), commands: ['npm ci', 'npm run build', 'npx cdk synth'], }); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'MyPipeline', synth: synthStep }); const stage = pipeline.addStage(new MyPipelineAppStage(this, 'test', { env: { account: '111111111111', region: 'eu-west-1' } })); // run a script that was transpiled from TypeScript during synthesis stage.addPost(new ShellStep('validate', { input: synthStep, commands: ['node tests/validate.js'] }));
JavaScript
const synthStep = new ShellStep('Synth', { input: CodePipelineSource.gitHub('OWNER/REPO', 'main'), commands: ['npm ci', 'npm run build', 'npx cdk synth'], }); const pipeline = new CodePipeline(this, 'Pipeline', { pipelineName: 'MyPipeline', synth: synthStep }); const stage = pipeline.addStage(new MyPipelineAppStage(this, "test", { env: { account: "111111111111", region: "eu-west-1" } })); // run a script that was transpiled from TypeScript during synthesis stage.addPost(new ShellStep('validate', { input: synthStep, commands: ['node tests/validate.js'] }));
Python
synth_step = ShellStep("Synth", input=CodePipelineSource.git_hub("OWNER/REPO", "main"), commands=["npm install -g aws-cdk", "python -m pip install -r requirements.txt", "cdk synth"]) pipeline = CodePipeline(self, "Pipeline", pipeline_name="MyPipeline", synth=synth_step) stage = pipeline.add_stage(MyApplicationStage(self, "test", env=cdk.Environment(account="111111111111", region="eu-west-1"))) # run a script that was compiled during synthesis stage.add_post(ShellStep("validate", input=synth_step, commands=["node test/validate.js"], ))
Java
final ShellStep synth = ShellStep.Builder.create("Synth") .input(CodePipelineSource.gitHub("OWNER/REPO", "main")) .commands(Arrays.asList("npm install -g aws-cdk", "cdk synth")) .build(); final CodePipeline pipeline = CodePipeline.Builder.create(this, "pipeline") .pipelineName("MyPipeline") .synth(synth) .build(); final StageDeployment stage = pipeline.addStage(new MyPipelineAppStage(this, "test", StageProps.builder() .env(Environment.builder() .account("111111111111") .region("eu-west-1") .build()) .build())); stage.addPost(ShellStep.Builder.create("validate") .input(synth) .commands(Arrays.asList("node ./tests/validate.js")) .build());
C#
var synth = new ShellStep("Synth", new ShellStepProps { Input = CodePipelineSource.GitHub("OWNER/REPO", "main"), Commands = new string[] { "npm install -g aws-cdk", "cdk synth" } }); var pipeline = new CodePipeline(this, "pipeline", new CodePipelineProps { PipelineName = "MyPipeline", Synth = synth }); var stage = pipeline.AddStage(new MyPipelineAppStage(this, "test", new StageProps { Env = new Environment { Account = "111111111111", Region = "eu-west-1" } })); stage.AddPost(new ShellStep("validate", new ShellStepProps { Input = synth, Commands = new string[] { "node ./tests/validate.js" } }));

セキュリティ上の考慮事項

どのような形式の継続的デリバリーでも、固有のセキュリティリスクがあります。 AWS 責任共有モデル では、 AWS クラウド内の情報のセキュリティはお客様の責任となります。CDK Pipelines ライブラリでは、安全なデフォルトとモデリングのベストプラクティスを組み込むことで、最初から始めることができます。

ただし、その性質上、目的を達成するために高レベルのアクセスを必要とするライブラリは、完全なセキュリティを保証することはできません。 AWS と組織の外部には、多くの攻撃ベクトルがあります。

特に、次の点に注意してください。

  • 依存するソフトウェアに注意してください。パイプラインで実行しているすべてのサードパーティーソフトウェアを検証します。これは、デプロイされるインフラストラクチャが変更される可能性があるためです。

  • 依存関係ロックを使用して、誤ってアップグレードされないようにします。CDK Pipelines は package-lock.jsonと を優先yarn.lockして、依存関係が期待どおりであることを確認します。

  • CDK Pipelines は、自分のアカウントで作成されたリソースで実行され、それらのリソースの設定は、パイプラインを介してコードを送信するデベロッパーによって制御されます。したがって、CDK Pipelines 自体は、悪意のあるデベロッパーがコンプライアンスチェックを回避しようとするのを防ぐことはできません。脅威モデルに CDK コードを記述するデベロッパーが含まれている場合は、 AWS CloudFormation 実行ロールに無効にするアクセス許可がないAWS CloudFormation フック (予防) や AWS Config (事後対応) などの外部コンプライアンスメカニズムを設定する必要があります。

  • 実稼働環境の認証情報は有効期間が短い必要があります。ブートストラップと初期プロビジョニングの後、デベロッパーが アカウントの認証情報をまったく持っている必要はありません。変更はパイプラインを通じてデプロイできます。認証情報を最初に必要としないことで、認証情報が漏洩する可能性が低くなります。

トラブルシューティング

CDK Pipelines の使用開始時によく発生する問題は次のとおりです。

パイプライン: 内部障害
CREATE_FAILED  | AWS::CodePipeline::Pipeline | Pipeline/Pipeline
Internal Failure

GitHub アクセストークンを確認します。リポジトリがないか、リポジトリへのアクセス許可がない可能性があります。

キー: ポリシーには、1 つ以上の無効なプリンシパルを含むステートメントが含まれています
CREATE_FAILED | AWS::KMS::Key | Pipeline/Pipeline/ArtifactsBucketEncryptionKey
Policy contains a statement with one or more invalid principals.

ターゲット環境の 1 つが、新しいブートストラップスタックでブートストラップされていません。すべてのターゲット環境がブートストラップされていることを確認します。

スタックは ROLLBACK_COMPLETE 状態であり、更新できません。
Stack STACK_NAME is in ROLLBACK_COMPLETE state and can not be updated. (Service:
AmazonCloudFormation; Status Code: 400; Error Code: ValidationError; Request
ID: ...)

スタックは以前のデプロイに失敗し、再試行不可能な状態です。コンソールからスタックを削除し、 AWS CloudFormation デプロイを再試行してください。