AWS CDK スタックの概要 - AWS Cloud Development Kit (AWS CDK) v2

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

AWS CDK スタックの概要

AWS CDK スタックはデプロイの最小単位です。これは、CDK コンストラクトを使用して定義する AWS リソースのコレクションを表します。CDK アプリケーションをデプロイすると、CDK スタック内のリソースは AWS CloudFormation スタックとして一緒にデプロイされます。AWS CloudFormation スタックの詳細については、「AWS CloudFormation ユーザーガイド」の「AWS CloudFormation スタックを使用した AWS リソースの単一ユニットとしての管理」を参照してください。

スタックを定義するには、Stack コンストラクトを拡張または継承します。以下の例は、スタックファイルと呼ばれる別のファイルで CDK スタックを定義するための一般的なパターンです。ここでは、Stack クラスを拡張または継承し、idscopepropsを受け入れるコンストラクターを定義します。次に、受け取った scopeidprops を引数とする super で、基底となる Stack クラスのコンストラクターを呼び出します。

TypeScript
import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class MyCdkStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // Define your constructs here } }
JavaScript
const { Stack } = require('aws-cdk-lib'); class MyCdkStack extends Stack { constructor(scope, id, props) { super(scope, id, props); // Define your constructs here } } module.exports = { MyCdkStack }
Python
from aws_cdk import ( Stack, ) from constructs import Construct class MyCdkStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Define your constructs here
Java
package com.myorg; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; public class MyCdkStack extends Stack { public MyCdkStack(final Construct scope, final String id) { this(scope, id, null); } public MyCdkStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); // Define your constructs here } }
C#
using Amazon.CDK; using Constructs; namespace MyCdk { public class MyCdkStack : Stack { internal MyCdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { // Define your constructs here } } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type CdkDemoAppStackProps struct { awscdk.StackProps } func NewCdkDemoAppStack(scope constructs.Construct, id string, props *CdkDemoAppStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } stack := awscdk.NewStack(scope, &id, &sprops) // The code that defines your stack goes here return stack } func main() { defer jsii.Close() app := awscdk.NewApp(nil) NewCdkDemoAppStack(app, "CdkDemoAppStack", &CdkDemoAppStackProps{ awscdk.StackProps{ Env: env(), }, }) app.Synth(nil) } //...

前の例では、スタックのみを定義しています。スタックを作成するには、CDK アプリのコンテキスト内でインスタンス化する必要があります。一般的なパターンは、CDK アプリケーションを定義し、アプリケーションファイルと呼ばれる別のファイルでスタックを初期化することです。

以下は、MyCdkStack という名前の CDK スタックを作成する例です。ここでは、CDK アプリケーションが作成され、アプリケーションのコンテキストで MyCdkStack がインスタンス化されます。

TypeScript
#!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { MyCdkStack } from '../lib/my-cdk-stack'; const app = new cdk.App(); new MyCdkStack(app, 'MyCdkStack', { });
JavaScript
#!/usr/bin/env node const cdk = require('aws-cdk-lib'); const { MyCdkStack } = require('../lib/my-cdk-stack'); const app = new cdk.App(); new MyCdkStack(app, 'MyCdkStack', { });
Python

app.py にあります

#!/usr/bin/env python3 import os import aws_cdk as cdk from my_cdk.my_cdk_stack import MyCdkStack app = cdk.App() MyCdkStack(app, "MyCdkStack",) app.synth()
Java
package com.myorg; import software.amazon.awscdk.App; import software.amazon.awscdk.Environment; import software.amazon.awscdk.StackProps; import java.util.Arrays; public class MyCdkApp { public static void main(final String[] args) { App app = new App(); new MyCdkStack(app, "MyCdkStack", StackProps.builder() .build()); app.synth(); } }
C#
using Amazon.CDK; using System; using System.Collections.Generic; using System.Linq; namespace MyCdk { sealed class Program { public static void Main(string[] args) { var app = new App(); new MyCdkStack(app, "MyCdkStack", new StackProps {}); app.Synth(); } } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) // ... func main() { defer jsii.Close() app := awscdk.NewApp(nil) NewMyCdkStack(app, "MyCdkStack", &MyCdkStackProps{ awscdk.StackProps{ Env: env(), }, }) app.Synth(nil) } // ...

以下の例では、2 つのスタックを含む CDK アプリケーションを作成します。

TypeScript
const app = new App(); new MyFirstStack(app, 'stack1'); new MySecondStack(app, 'stack2'); app.synth();
JavaScript
const app = new App(); new MyFirstStack(app, 'stack1'); new MySecondStack(app, 'stack2'); app.synth();
Python
app = App() MyFirstStack(app, 'stack1') MySecondStack(app, 'stack2') app.synth()
Java
App app = new App(); new MyFirstStack(app, "stack1"); new MySecondStack(app, "stack2"); app.synth();
C#
var app = new App(); new MyFirstStack(app, "stack1"); new MySecondStack(app, "stack2"); app.Synth();
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type MyFirstStackProps struct { awscdk.StackProps } func NewMyFirstStack(scope constructs.Construct, id string, props *MyFirstStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } myFirstStack := awscdk.NewStack(scope, &id, &sprops) // The code that defines your stack goes here return myFirstStack } type MySecondStackProps struct { awscdk.StackProps } func NewMySecondStack(scope constructs.Construct, id string, props *MySecondStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } mySecondStack := awscdk.NewStack(scope, &id, &sprops) // The code that defines your stack goes here return mySecondStack } func main() { defer jsii.Close() app := awscdk.NewApp(nil) NewMyFirstStack(app, "MyFirstStack", &MyFirstStackProps{ awscdk.StackProps{ Env: env(), }, }) NewMySecondStack(app, "MySecondStack", &MySecondStackProps{ awscdk.StackProps{ Env: env(), }, }) app.Synth(nil) } // ...

スタック API について

Stack オブジェクトには、以下のような豊富な API が用意されています。

  • Stack.of(construct) – コンストラクトが定義されているスタックを返す静的メソッド。これは、再利用可能なコンストラクト内からスタックを操作する必要がある場合に役立ちます。スタックがスコープ内で見つからない場合、呼び出しは失敗します。

  • stack.stackName (Python: stack_name) — スタックの物理名を返します。前述のように、すべての AWS CDK スタックには、合成中に AWS CDK が解決できる物理名があります。

  • stack.region および stack.account — このスタックがデプロイされる AWS リージョンとアカウントをそれぞれ返します。これらのプロパティは、以下のいずれかの値を返します。

    • スタックが定義されたときに明示的に指定されたアカウントまたはリージョン

    • アカウントとリージョンの AWS CloudFormation 擬似パラメータに解決され、このスタックが環境に依存しないことを示す文字列エンコード形式のトークン

    スタックの環境の決定方法については、「AWS CDK の環境」を参照してください。

  • stack.addDependency(stack) (Python: stack.add_dependency(stack)) – 2 つのスタック間の依存関係の順序を明示的に定義するために使用できます。この順序は、複数のスタックを一度にデプロイするときに cdk deploy コマンドによって考慮されます。

  • stack.tags – スタックレベルのタグを追加または削除するために使用できる TagManager を返します。このタグマネージャーは、スタック内のすべてのリソースにタグを付け、AWS CloudFormation を通してスタックを作成した場合は、スタック自体にもタグを付けます。

  • stack.partitionstack.urlSuffix (Python: url_suffix)、stack.stackId (Python: stack_id)、および stack.notificationArn (Python: notification_arn) – { "Ref": "AWS::Partition" } など、それぞれの AWS CloudFormation 擬似パラメータに解決されるトークンを返します。これらのトークンは、AWS CDK フレームワークがクロススタック参照を識別できるように、特定のスタックオブジェクトに関連付けられます。

  • stack.availabilityZones (Python: availability_zones) – このスタックがデプロイされている環境で使用可能なアベイラビリティーゾーンのセットを返します。環境に依存しないスタックの場合、これは常に 2 つのアベイラビリティーゾーンを持つ配列を返します。環境固有のスタックの場合、AWS CDK は環境をクエリし、指定したリージョンで使用できるアベイラビリティーゾーンの正確なセットを返します。

  • stack.parseArn(arn) および stack.formatArn(comps) (Python: parse_arnformat_arn) — Amazon リソースネーム (ARN) の操作に使用できます。

  • stack.toJsonString(obj) (Python: to_json_string) – AWS CloudFormation テンプレートに埋め込むことができる JSON 文字列として任意のオブジェクトをフォーマットするために使用できます。オブジェクトにはトークン、属性、参照を含めることができ、これらはデプロイ中にのみ解決されます。

  • stack.templateOptions (Python: template_options) – スタックの変換、説明、メタデータなどの AWS CloudFormation テンプレートオプションを指定します。

スタックの操作

スタックは、AWS CloudFormation スタックとして AWS 環境にデプロイされます。この環境は、特定の AWS アカウント と AWS リージョン を対象としています。

複数のスタックを持つアプリケーションの cdk synth コマンドを実行すると、クラウドアセンブリにはスタックインスタンスごとに個別のテンプレートが含まれます。2 つのスタックが同じクラスのインスタンスであっても、AWS CDK は 2 つの個別のテンプレートとして出力します。

cdk synth コマンドでスタック名を指定することで、各テンプレートを合成できます。以下の例は、stack1 のテンプレートを合成します。

$ cdk synth stack1

このアプローチは、テンプレートを複数回デプロイし、AWS CloudFormation パラメータを介してパラメータ化できるという、AWS CloudFormation テンプレートの通常の使用方法とは概念的に異なります。AWS CloudFormation パラメータは AWS CDK でも定義できますが、AWS CloudFormation パラメータがデプロイ時にしか解決されないため、通常は推奨されません。これは、コード内ではパラメータの値を判断できないことを意味します。

たとえば、パラメータ値に基づいてアプリにリソースを条件付きで含めるには、AWS CloudFormation 条件を設定し、そのリソースにタグを付ける必要があります。AWS CDK は、合成時に具体的なテンプレートが解決されるアプローチを取ります。したがって、if ステートメントを使用して値をチェックし、リソースを定義するか、何らかの動作を適用するかを決定できます。

注記

AWS CDK は、合成時に可能な限り多くの解決を提供し、プログラミング言語のイディオマティックで自然な使用を可能にします。

他のコンストラクトと同様に、スタックはまとめてグループに構成できます。以下のコードは、コントロールプレーン、データプレーン、モニタリングスタックの 3 つのスタックで構成されるサービスの例を示しています。サービスコンストラクトは 2 回定義されます。ベータ環境用に 1 回、本番環境用に 1 回です。

TypeScript
import { App, Stack } from 'aws-cdk-lib'; import { Construct } from 'constructs'; interface EnvProps { prod: boolean; } // imagine these stacks declare a bunch of related resources class ControlPlane extends Stack {} class DataPlane extends Stack {} class Monitoring extends Stack {} class MyService extends Construct { constructor(scope: Construct, id: string, props?: EnvProps) { super(scope, id); // we might use the prod argument to change how the service is configured new ControlPlane(this, "cp"); new DataPlane(this, "data"); new Monitoring(this, "mon"); } } const app = new App(); new MyService(app, "beta"); new MyService(app, "prod", { prod: true }); app.synth();
JavaScript
const { App, Stack } = require('aws-cdk-lib'); const { Construct } = require('constructs'); // imagine these stacks declare a bunch of related resources class ControlPlane extends Stack {} class DataPlane extends Stack {} class Monitoring extends Stack {} class MyService extends Construct { constructor(scope, id, props) { super(scope, id); // we might use the prod argument to change how the service is configured new ControlPlane(this, "cp"); new DataPlane(this, "data"); new Monitoring(this, "mon"); } } const app = new App(); new MyService(app, "beta"); new MyService(app, "prod", { prod: true }); app.synth();
Python
from aws_cdk import App, Stack from constructs import Construct # imagine these stacks declare a bunch of related resources class ControlPlane(Stack): pass class DataPlane(Stack): pass class Monitoring(Stack): pass class MyService(Construct): def __init__(self, scope: Construct, id: str, *, prod=False): super().__init__(scope, id) # we might use the prod argument to change how the service is configured ControlPlane(self, "cp") DataPlane(self, "data") Monitoring(self, "mon") app = App(); MyService(app, "beta") MyService(app, "prod", prod=True) app.synth()
Java
package com.myorg; import software.amazon.awscdk.App; import software.amazon.awscdk.Stack; import software.constructs.Construct; public class MyApp { // imagine these stacks declare a bunch of related resources static class ControlPlane extends Stack { ControlPlane(Construct scope, String id) { super(scope, id); } } static class DataPlane extends Stack { DataPlane(Construct scope, String id) { super(scope, id); } } static class Monitoring extends Stack { Monitoring(Construct scope, String id) { super(scope, id); } } static class MyService extends Construct { MyService(Construct scope, String id) { this(scope, id, false); } MyService(Construct scope, String id, boolean prod) { super(scope, id); // we might use the prod argument to change how the service is configured new ControlPlane(this, "cp"); new DataPlane(this, "data"); new Monitoring(this, "mon"); } } public static void main(final String argv[]) { App app = new App(); new MyService(app, "beta"); new MyService(app, "prod", true); app.synth(); } }
C#
using Amazon.CDK; using Constructs; // imagine these stacks declare a bunch of related resources public class ControlPlane : Stack { public ControlPlane(Construct scope, string id=null) : base(scope, id) { } } public class DataPlane : Stack { public DataPlane(Construct scope, string id=null) : base(scope, id) { } } public class Monitoring : Stack { public Monitoring(Construct scope, string id=null) : base(scope, id) { } } public class MyService : Construct { public MyService(Construct scope, string id, Boolean prod=false) : base(scope, id) { // we might use the prod argument to change how the service is configured new ControlPlane(this, "cp"); new DataPlane(this, "data"); new Monitoring(this, "mon"); } } class Program { static void Main(string[] args) { var app = new App(); new MyService(app, "beta"); new MyService(app, "prod", prod: true); app.Synth(); } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type ControlPlaneStackProps struct { awscdk.StackProps } func NewControlPlaneStack(scope constructs.Construct, id string, props *ControlPlaneStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } ControlPlaneStack := awscdk.NewStack(scope, jsii.String(id), &sprops) // The code that defines your stack goes here return ControlPlaneStack } type DataPlaneStackProps struct { awscdk.StackProps } func NewDataPlaneStack(scope constructs.Construct, id string, props *DataPlaneStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } DataPlaneStack := awscdk.NewStack(scope, jsii.String(id), &sprops) // The code that defines your stack goes here return DataPlaneStack } type MonitoringStackProps struct { awscdk.StackProps } func NewMonitoringStack(scope constructs.Construct, id string, props *MonitoringStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } MonitoringStack := awscdk.NewStack(scope, jsii.String(id), &sprops) // The code that defines your stack goes here return MonitoringStack } type MyServiceStackProps struct { awscdk.StackProps Prod bool } func NewMyServiceStack(scope constructs.Construct, id string, props *MyServiceStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } MyServiceStack := awscdk.NewStack(scope, jsii.String(id), &sprops) NewControlPlaneStack(MyServiceStack, "cp", &ControlPlaneStackProps{ StackProps: sprops, }) NewDataPlaneStack(MyServiceStack, "data", &DataPlaneStackProps{ StackProps: sprops, }) NewMonitoringStack(MyServiceStack, "mon", &MonitoringStackProps{ StackProps: sprops, }) return MyServiceStack } func main() { defer jsii.Close() app := awscdk.NewApp(nil) betaProps := MyServiceStackProps{ StackProps: awscdk.StackProps{ Env: env(), }, Prod: false, } NewMyServiceStack(app, "beta", &betaProps) prodProps := MyServiceStackProps{ StackProps: awscdk.StackProps{ Env: env(), }, Prod: true, } NewMyServiceStack(app, "prod", &prodProps) app.Synth(nil) } // ...

この AWS CDK アプリは最終的に環境ごとに 3 つずつ、合計 6 つのスタックで構成されます。

$ cdk ls betacpDA8372D3 betadataE23DB2BA betamon632BD457 prodcp187264CE proddataF7378CE5 prodmon631A1083

AWS CloudFormation スタックの物理名は、ツリー内のスタックのコンストラクトパスに基づき、AWS CDK によって自動的に決定されます。デフォルトでは、スタックの名前は Stack オブジェクトのコンストラクト ID から取得されます。ただし、以下のように stackName prop (Python では stack_name) を使用して、明示的な名前を指定できます。

TypeScript
new MyStack(this, 'not:a:stack:name', { stackName: 'this-is-stack-name' });
JavaScript
new MyStack(this, 'not:a:stack:name', { stackName: 'this-is-stack-name' });
Python
MyStack(self, "not:a:stack:name", stack_name="this-is-stack-name")
Java
new MyStack(this, "not:a:stack:name", StackProps.builder() .StackName("this-is-stack-name").build());
C#
new MyStack(this, "not:a:stack:name", new StackProps { StackName = "this-is-stack-name" });

ネストされたスタックの操作

ネストされたスタックは、親スタックと呼ばれる別のスタック内に作成する CDK スタックです。ネストされたスタックは、NestedStack コンストラクトを使用して作成します。

ネストされたスタックを使用すると、複数のスタックにまたがってリソースを整理できます。ネストされたスタックは、スタックあたり 500 リソースという AWS CloudFormation の制限を回避する方法も提供します。ネストされたスタックは、それを含むスタック内の 1 つのリソースとしてカウントされます。そしてそれには、さらにネストされたスタックを含め、最大 500 個のリソースを含めることができます。

ネストされたスタックのスコープは、Stack または NestedStack コンストラクトである必要があります。ネストされたスタックは、親スタック内で字句的に宣言される必要はありません。ネストされたスタックをインスタンス化するときには、親スタックを最初のパラメータ (scope) として渡すだけで済みます。この制限とは別に、ネストされたスタック内のコンストラクトの定義は、通常のスタックとまったく同じように機能します。

合成時に、ネストされたスタックは独自の AWS CloudFormation テンプレートに合成され、デプロイ時に AWS CDK ステージングバケットにアップロードされます。ネストされたスタックは親スタックにバインドされ、独立したデプロイアーティファクトとしては扱われません。これらは cdk list で一覧表示されず、cdk deploy でデプロイすることもできません。

親スタックとネストされたスタック間の参照は、他のクロススタック参照と同様に、生成された AWS CloudFormation テンプレート内のスタックパラメータと出力に自動的に変換されます。

警告

セキュリティ体制の変更は、ネストされたスタックのデプロイ前には表示されません。この情報は、最上位スタックにのみ表示されます。