CDKで複数スタックの並行デプロイを行ってみた

AWS CDKで複数スタックの並行デプロイ方法をご紹介します。
2023.04.03

こんにちは、データアナリティクス事業本部の八木です。

何かしらの処理を行うとき、並列・並行実行して高速化したい!と思うのはエンジニアの常ではないでしょうか?

今回はAWS CDKで複数スタックを並行デプロイする方法についてご紹介します。

先にまとめ

  • concurrencyオプションで複数スタックの並行デプロイができる
  • 並行デプロイ数は指定が可能
  • スタック間に依存関係がある場合は自動的に解決される

AWS CDKのconcurrencyオプションについて

2022年8月にconcurrencyオプションが追加されました。(v2.39.0)

ヘルプコマンドを実行すると、オプションの内容が確認できます。

$ cdk deploy --help
cdk deploy [STACKS..]

Deploys the stack(s) named STACKS into your AWS account

Options:
〜〜(中略)〜〜
      --concurrency          Maximum number of simultaneous deployments
                             (dependency permitting) to execute.
                                                           [number] [default: 1]
〜〜(後略)〜〜

これはCDKアプリケーション内に複数のスタックが含まれる場合、依存関係を解決しつつ、並行デプロイをしてくれるオプションです。
--concurrencyのあとに数字を指定し、最大並行数を指定できます。

従来は複数スタックのデプロイは直列のみ可能となっており、並列実行したい場合はコマンドを呼び出す側で対応する必要がありましたが、このオプションによりCDK単体での実行が可能になりました。

やってみた

実際に動かして動作を確認してみます。

前提

  • AWS CDK v2.68.0
  • 各フェーズごとにcdk destroyでリソースを削除している

スタックの直列デプロイ

まず、以下のような3つのスタックが存在する状態で、通常通りデプロイを行ってみます。

lib/cdk-multi-deploy-sample.ts

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as dynamo from "aws-cdk-lib/aws-dynamodb";

export class SampleStack1 extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new ec2.Vpc(this, "VPC_A", {
      ipAddresses: ec2.IpAddresses.cidr("10.1.0.0/16"),
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: "public",
          subnetType: ec2.SubnetType.PUBLIC,
        },
        {
          cidrMask: 24,
          name: "private",
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        },
      ],
    });
  }
}

export class SampleStack2 extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new dynamo.Table(this, "SampleTable1", {
      tableName: "SampleTable1",
      billingMode: dynamo.BillingMode.PAY_PER_REQUEST,
      partitionKey: {
        name: "id",
        type: dynamo.AttributeType.STRING,
      },
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
  }
}

export class SampleStack3 extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new dynamo.Table(this, "SampleTable2", {
      tableName: "SampleTable2",
      billingMode: dynamo.BillingMode.PAY_PER_REQUEST,
      partitionKey: {
        name: "id",
        type: dynamo.AttributeType.STRING,
      },
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
  }
}

bin/cdk-multi-deploy-sample.ts

#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import {
  SampleStack1,
  SampleStack2,
  SampleStack3,
} from "../lib/cdk-multi-deploy-sample-stack";

const app = new cdk.App();
new SampleStack1(app, "SampleStack1");
new SampleStack2(app, "SampleStack2");
new SampleStack3(app, "SampleStack3");
$ cdk deploy --all

✨  Synthesis time: 3.05s

SampleStack1: building assets...

[0%] start: Building db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
[100%] success: Built db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region

SampleStack1: assets built

SampleStack2: building assets...

[0%] start: Building 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
[100%] success: Built 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region

SampleStack2: assets built

SampleStack3: building assets...

[0%] start: Building 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Built 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region

SampleStack3: assets built

SampleStack1
SampleStack1: deploying... [1/3]
[0%] start: Publishing db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
[100%] success: Published db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
SampleStack1: creating CloudFormation changeset...

 ✅  SampleStack1

✨  Deployment time: 78.81s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack1/dd5a3a30-d1bf-11ed-8a0d-0a9717c6da5d

✨  Total time: 81.86s

SampleStack2
SampleStack2: deploying... [2/3]
[0%] start: Publishing 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
[100%] success: Published 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
SampleStack2: creating CloudFormation changeset...

 ✅  SampleStack2

✨  Deployment time: 38.22s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack2/0e9bc910-d1c0-11ed-a967-12981ec870d5

✨  Total time: 41.27s

SampleStack3
SampleStack3: deploying... [3/3]
[0%] start: Publishing 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Published 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
SampleStack3: creating CloudFormation changeset...

 ✅  SampleStack3

✨  Deployment time: 39.07s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack3/276bdde0-d1c0-11ed-b63f-0a3c80fd2065

✨  Total time: 42.12s

何もオプションを指定しない場合、3つのスタックは直列でデプロイされ、合計で約3分かかっています。

並行デプロイの場合(上限並行数>=スタック数)

続いて、同じリソースを並行デプロイしてみます。concurrencyオプションで並行デプロイスタック数の上限を3とします。

$ cdk deploy --all --concurrency 3

✨  Synthesis time: 2.91s

SampleStack1: building assets...

[0%] start: Building db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
[100%] success: Built db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region

SampleStack1: assets built

SampleStack2: building assets...

[0%] start: Building 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
[100%] success: Built 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region

SampleStack2: assets built

SampleStack3: building assets...

[0%] start: Building 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Built 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region

SampleStack3: assets built

SampleStack1
SampleStack2
SampleStack3
SampleStack3: deploying... [3/3]
SampleStack2: deploying... [2/3]
SampleStack1: deploying... [1/3]
[0%] start: Publishing 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[0%] start: Publishing 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
[0%] start: Publishing db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
[100%] success: Published 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Published 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
[100%] success: Published db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
SampleStack3: creating CloudFormation changeset...
SampleStack2: creating CloudFormation changeset...
SampleStack1: creating CloudFormation changeset...
SampleStack2 | 0/3 | 10:48:43 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack2 User Initiated
SampleStack2 | 0/3 | 10:48:51 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack2 User Initiated
SampleStack2 | 0/3 | 10:48:55 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack2 | 0/3 | 10:48:55 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable1 (SampleTable1E3EC8AE3)
SampleStack2 | 0/3 | 10:48:56 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack2 | 0/3 | 10:48:56 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable1 (SampleTable1E3EC8AE3) Resource creation Initiated
SampleStack2 | 1/3 | 10:48:56 | CREATE_COMPLETE      | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack3 | 0/3 | 10:48:43 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack3 User Initiated
SampleStack3 | 0/3 | 10:48:51 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack3 User Initiated
SampleStack3 | 0/3 | 10:48:55 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack3 | 0/3 | 10:48:55 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B)
SampleStack3 | 0/3 | 10:48:57 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B) Resource creation Initiated
SampleStack1 |  0/19 | 10:48:43 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack            | SampleStack1 User Initiated
SampleStack1 |  0/19 | 10:48:51 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack            | SampleStack1 User Initiated
SampleStack1 |  0/19 | 10:48:55 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5)
SampleStack1 |  0/19 | 10:48:55 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454)
SampleStack1 |  0/19 | 10:48:56 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata)
SampleStack1 |  0/19 | 10:48:57 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5) Resource creation Initiated
SampleStack3 | 0/3 | 10:48:57 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack3 | 1/3 | 10:48:57 | CREATE_COMPLETE      | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack1 |  0/19 | 10:48:57 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack1 |  0/19 | 10:48:57 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454) Resource creation Initiated
SampleStack1 |  1/19 | 10:48:57 | CREATE_COMPLETE      | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata)
SampleStack2 | 2/3 | 10:49:07 | CREATE_COMPLETE      | AWS::DynamoDB::Table | SampleTable1 (SampleTable1E3EC8AE3)
SampleStack2 | 3/3 | 10:49:08 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | SampleStack2
SampleStack3 | 2/3 | 10:49:08 | CREATE_COMPLETE      | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B)
SampleStack3 | 3/3 | 10:49:09 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | SampleStack3
SampleStack1 |  2/19 | 10:49:08 | CREATE_COMPLETE      | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5)

 ✅  SampleStack3

✨  Deployment time: 34.94s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack3/aa1af4a0-d1c1-11ed-910f-0ae4419fb0f9

✨  Total time: 37.85s


 ✅  SampleStack2

✨  Deployment time: 34.94s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack2/aa2247a0-d1c1-11ed-be03-0ee030fcd94b

✨  Total time: 37.85s

SampleStack1 |  2/19 | 10:49:09 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72)
SampleStack1 |  2/19 | 10:49:10 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262)
SampleStack1 |  2/19 | 10:49:10 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94)
SampleStack1 |  2/19 | 10:49:10 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C)
SampleStack1 |  2/19 | 10:49:10 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86)
SampleStack1 |  2/19 | 10:49:10 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95)
SampleStack1 |  2/19 | 10:49:10 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96)
SampleStack1 |  2/19 | 10:49:10 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A)
SampleStack1 |  2/19 | 10:49:11 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72) Resource creation Initiated
SampleStack1 |  2/19 | 10:49:11 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262) Resource creation Initiated
SampleStack1 |  2/19 | 10:49:11 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86) Resource creation Initiated
SampleStack1 |  2/19 | 10:49:12 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C) Resource creation Initiated
SampleStack1 |  2/19 | 10:49:12 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95) Resource creation Initiated
SampleStack1 |  2/19 | 10:49:12 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A) Resource creation Initiated
SampleStack1 |  2/19 | 10:49:12 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94) Resource creation Initiated
SampleStack1 |  2/19 | 10:49:12 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96) Resource creation Initiated
SampleStack1 |  3/19 | 10:49:13 | CREATE_COMPLETE      | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454)
SampleStack1 |  3/19 | 10:49:14 | CREATE_IN_PROGRESS   | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208)
SampleStack1 |  4/19 | 10:49:14 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95)
SampleStack1 |  5/19 | 10:49:15 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A)
SampleStack1 |  5/19 | 10:49:15 | CREATE_IN_PROGRESS   | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208) Resource creation Initiated
SampleStack1 |  6/19 | 10:49:15 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94)
SampleStack1 |  7/19 | 10:49:15 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96)
SampleStack1 |  8/19 | 10:49:21 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72)
SampleStack1 |  9/19 | 10:49:22 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262)
SampleStack1 |  9/19 | 10:49:23 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203)
SampleStack1 |  9/19 | 10:49:23 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76)
SampleStack1 |  9/19 | 10:49:24 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203) Resource creation Initiated
SampleStack1 | 10/19 | 10:49:25 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203)
SampleStack1 | 10/19 | 10:49:25 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76) Resource creation Initiated
SampleStack1 | 11/19 | 10:49:25 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76)
SampleStack1 | 12/19 | 10:49:27 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C)
SampleStack1 | 13/19 | 10:49:27 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86)
SampleStack1 | 13/19 | 10:49:29 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE)
SampleStack1 | 13/19 | 10:49:29 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A)
SampleStack1 | 14/19 | 10:49:30 | CREATE_COMPLETE      | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208)
SampleStack1 | 14/19 | 10:49:31 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A) Resource creation Initiated
SampleStack1 | 14/19 | 10:49:31 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE) Resource creation Initiated
SampleStack1 | 15/19 | 10:49:31 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A)
SampleStack1 | 16/19 | 10:49:31 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE)
SampleStack1 | 16/19 | 10:49:32 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678)
SampleStack1 | 16/19 | 10:49:32 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF)
SampleStack1 | 16/19 | 10:49:32 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678) Resource creation Initiated
SampleStack1 | 16/19 | 10:49:32 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF) Resource creation Initiated
SampleStack1 | 17/19 | 10:49:48 | CREATE_COMPLETE      | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678)
SampleStack1 | 18/19 | 10:49:48 | CREATE_COMPLETE      | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF)
SampleStack1 | 19/19 | 10:49:49 | CREATE_COMPLETE      | AWS::CloudFormation::Stack            | SampleStack1

 ✅  SampleStack1

✨  Deployment time: 78.03s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack1/aa51e320-d1c1-11ed-bd1b-0e3aaf94238b

✨  Total time: 80.94s

3つのスタックが並行でデプロイされ、実行時間が1分半程度になりました。直列デプロイに比べてだいぶ早くなっています。

並行デプロイの場合(上限並行数<スタック数)

では最大並行デプロイ数を2にするとどうなるでしょうか?

$ cdk deploy --all --concurrency 2

✨  Synthesis time: 2.72s

SampleStack1: building assets...

[0%] start: Building db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
[100%] success: Built db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region

SampleStack1: assets built

SampleStack2: building assets...

[0%] start: Building 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
[100%] success: Built 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region

SampleStack2: assets built

SampleStack3: building assets...

[0%] start: Building 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Built 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region

SampleStack3: assets built

SampleStack1
SampleStack2
SampleStack2: deploying... [2/3]
SampleStack1: deploying... [1/3]
[0%] start: Publishing db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
[0%] start: Publishing 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
[100%] success: Published db492062b83e12c3bd93a1aa7dda4982867afc87bbe8660c282b2a141c6b9645:current_account-current_region
[100%] success: Published 9aa20d2393dc11946a175accab9a0e57c0028f4e2046a11b076dcadcf157e847:current_account-current_region
SampleStack1: creating CloudFormation changeset...
SampleStack2: creating CloudFormation changeset...
SampleStack1 |  0/19 | 11:56:42 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack            | SampleStack1 User Initiated
SampleStack1 |  0/19 | 11:56:51 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack            | SampleStack1 User Initiated
SampleStack1 |  0/19 | 11:56:55 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata)
SampleStack1 |  0/19 | 11:56:55 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5)
SampleStack1 |  0/19 | 11:56:55 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454)
SampleStack1 |  0/19 | 11:56:56 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack2 | 0/3 | 11:56:43 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack2 User Initiated
SampleStack2 | 0/3 | 11:56:51 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack2 User Initiated
SampleStack2 | 0/3 | 11:56:55 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack2 | 0/3 | 11:56:55 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable1 (SampleTable1E3EC8AE3)
SampleStack2 | 0/3 | 11:56:56 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack2 | 1/3 | 11:56:56 | CREATE_COMPLETE      | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack1 |  1/19 | 11:56:56 | CREATE_COMPLETE      | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata)
SampleStack1 |  1/19 | 11:56:57 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454) Resource creation Initiated
SampleStack1 |  1/19 | 11:56:57 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5) Resource creation Initiated
SampleStack2 | 1/3 | 11:56:57 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable1 (SampleTable1E3EC8AE3) Resource creation Initiated
SampleStack1 |  2/19 | 11:57:08 | CREATE_COMPLETE      | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5)
SampleStack2 | 2/3 | 11:57:07 | CREATE_COMPLETE      | AWS::DynamoDB::Table | SampleTable1 (SampleTable1E3EC8AE3)
SampleStack2 | 3/3 | 11:57:08 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | SampleStack2

 ✅  SampleStack2

✨  Deployment time: 34.95s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack2/29c55d40-d1cb-11ed-bbb2-129b30686da3

✨  Total time: 37.67s

SampleStack3
SampleStack3: deploying... [3/3]
SampleStack1 |  2/19 | 11:57:10 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72)
SampleStack1 |  2/19 | 11:57:10 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86)
SampleStack1 |  2/19 | 11:57:10 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C)
SampleStack1 |  2/19 | 11:57:10 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262)
SampleStack1 |  2/19 | 11:57:10 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94)
SampleStack1 |  2/19 | 11:57:10 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96)
SampleStack1 |  2/19 | 11:57:10 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A)
SampleStack1 |  2/19 | 11:57:10 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95)
SampleStack1 |  2/19 | 11:57:11 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72) Resource creation Initiated
SampleStack1 |  2/19 | 11:57:11 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86) Resource creation Initiated
SampleStack1 |  2/19 | 11:57:11 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C) Resource creation Initiated
SampleStack1 |  2/19 | 11:57:11 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262) Resource creation Initiated
SampleStack1 |  2/19 | 11:57:12 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95) Resource creation Initiated
SampleStack1 |  2/19 | 11:57:12 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94) Resource creation Initiated
SampleStack1 |  2/19 | 11:57:12 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A) Resource creation Initiated
SampleStack1 |  3/19 | 11:57:12 | CREATE_COMPLETE      | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454)
SampleStack1 |  3/19 | 11:57:12 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96) Resource creation Initiated
SampleStack1 |  3/19 | 11:57:14 | CREATE_IN_PROGRESS   | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208)
[0%] start: Publishing 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Published 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
SampleStack3: creating CloudFormation changeset...
SampleStack1 |  4/19 | 11:57:14 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95)
SampleStack1 |  4/19 | 11:57:14 | CREATE_IN_PROGRESS   | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208) Resource creation Initiated
SampleStack1 |  5/19 | 11:57:15 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94)
SampleStack1 |  6/19 | 11:57:15 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96)
SampleStack1 |  7/19 | 11:57:15 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A)
SampleStack1 |  8/19 | 11:57:22 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C)
SampleStack1 |  9/19 | 11:57:22 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86)
SampleStack1 | 10/19 | 11:57:22 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262)
SampleStack1 | 10/19 | 11:57:23 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE)
SampleStack1 | 10/19 | 11:57:23 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A)
SampleStack1 | 10/19 | 11:57:23 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76)
SampleStack1 | 10/19 | 11:57:24 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE) Resource creation Initiated
SampleStack1 | 10/19 | 11:57:24 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A) Resource creation Initiated
SampleStack1 | 11/19 | 11:57:25 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A)
SampleStack1 | 12/19 | 11:57:25 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE)
SampleStack1 | 12/19 | 11:57:25 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76) Resource creation Initiated
SampleStack1 | 13/19 | 11:57:26 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76)
SampleStack1 | 14/19 | 11:57:27 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72)
SampleStack1 | 14/19 | 11:57:29 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203)
SampleStack1 | 15/19 | 11:57:30 | CREATE_COMPLETE      | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208)
SampleStack1 | 15/19 | 11:57:31 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203) Resource creation Initiated
SampleStack1 | 16/19 | 11:57:31 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203)
SampleStack1 | 16/19 | 11:57:31 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF)
SampleStack1 | 16/19 | 11:57:31 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678)
SampleStack1 | 16/19 | 11:57:32 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF) Resource creation Initiated
SampleStack1 | 16/19 | 11:57:32 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678) Resource creation Initiated
SampleStack3 | 0/3 | 11:57:20 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack3 User Initiated
SampleStack3 | 0/3 | 11:57:28 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack3 User Initiated
SampleStack3 | 0/3 | 11:57:32 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack3 | 0/3 | 11:57:32 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B)
SampleStack3 | 0/3 | 11:57:34 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B) Resource creation Initiated
SampleStack3 | 0/3 | 11:57:34 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack3 | 1/3 | 11:57:34 | CREATE_COMPLETE      | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack3 | 2/3 | 11:57:44 | CREATE_COMPLETE      | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B)
SampleStack3 | 3/3 | 11:57:46 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | SampleStack3

 ✅  SampleStack3

✨  Deployment time: 34.74s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack3/403e89c0-d1cb-11ed-b6af-0ebdd50288cf

✨  Total time: 37.46s

SampleStack1 | 17/19 | 11:57:47 | CREATE_COMPLETE      | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF)
SampleStack1 | 18/19 | 11:57:48 | CREATE_COMPLETE      | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678)
SampleStack1 | 19/19 | 11:57:49 | CREATE_COMPLETE      | AWS::CloudFormation::Stack            | SampleStack1

 ✅  SampleStack1

✨  Deployment time: 76.54s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack1/29a244e0-d1cb-11ed-8102-0a7a32ebd56b

✨  Total time: 79.26s

ログからは分かりづらいですが、2つのスタック(SampleStack1, SampleStack2)が先にデプロイが開始され、どちらかのスタック(今回はSampleStack2)のデプロイ完了後に3つ目のスタック(SampleStack3)のデプロイが始まっています。

多数のリソースが作成される場合、レートリミットを考慮して上限を低めに設定すると良いでしょう。

依存関係のある複数スタック並行デプロイの場合

最後にスタック間の依存関係がある場合です。

SampleStack2がSampleStack1に依存するよう、以下のようにCDKのコードを変更します。

lib/cdk-multi-deploy-sample.ts

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as dynamo from "aws-cdk-lib/aws-dynamodb";

export class SampleStack1 extends cdk.Stack {
  public readonly vpc: ec2.Vpc;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    this.vpc = new ec2.Vpc(this, "VPC_A", {
      ipAddresses: ec2.IpAddresses.cidr("10.1.0.0/16"),
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: "public",
          subnetType: ec2.SubnetType.PUBLIC,
        },
        {
          cidrMask: 24,
          name: "private",
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        },
      ],
    });
  }
}

interface SampleStack2Props extends cdk.StackProps {
  vpc: ec2.Vpc;
}

export class SampleStack2 extends cdk.Stack {
  constructor(scope: Construct, id: string, props: SampleStack2Props) {
    super(scope, id, props);

    new dynamo.Table(this, "SampleTable1", {
      tableName: "SampleTable1",
      billingMode: dynamo.BillingMode.PAY_PER_REQUEST,
      partitionKey: {
        name: "id",
        type: dynamo.AttributeType.STRING,
      },
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });

    new ec2.Instance(this, "SampleEC2Instance", {
      vpc: props.vpc,
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T3,
        ec2.InstanceSize.MICRO
      ),
      machineImage: new ec2.AmazonLinuxImage(),
    });
  }
}

export class SampleStack3 extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new dynamo.Table(this, "SampleTable2", {
      tableName: "SampleTable2",
      billingMode: dynamo.BillingMode.PAY_PER_REQUEST,
      partitionKey: {
        name: "id",
        type: dynamo.AttributeType.STRING,
      },
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
  }
}

bin/cdk-multi-deploy-sample.ts

#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import {
  SampleStack1,
  SampleStack2,
  SampleStack3,
} from "../lib/cdk-multi-deploy-sample-stack";

const app = new cdk.App();
const stack1 = new SampleStack1(app, "SampleStack1");
new SampleStack2(app, "SampleStack2", { vpc: stack1.vpc });
new SampleStack3(app, "SampleStack3");

この場合、SampleStack1がデプロイされたあとにSampleStack2がデプロイされる必要があります。
デプロイを行って、想定通りの順序でデプロイが行われるか確認します。

EC2(にアタッチするセキュリティグループ)の作成の承認を無視するため、今回のみ--require-approvalオプションを指定しています。(このオプションを指定しない場合はエラーとなり、デプロイできません。)

cdk deploy --all --require-approval never --concurrency 3

✨  Synthesis time: 3.04s

SampleStack1: building assets...

[0%] start: Building 91c7ea692437eca46b346f15e31203a58655905896d5613f847a767698081f24:current_account-current_region
[100%] success: Built 91c7ea692437eca46b346f15e31203a58655905896d5613f847a767698081f24:current_account-current_region

SampleStack1: assets built

SampleStack3: building assets...

[0%] start: Building 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Built 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region

SampleStack3: assets built

SampleStack2: building assets...

[0%] start: Building 7c33633358050e3ecfbd71ca8f40d12c364bb6fcd71338a52f100d27ae81d00b:current_account-current_region
[100%] success: Built 7c33633358050e3ecfbd71ca8f40d12c364bb6fcd71338a52f100d27ae81d00b:current_account-current_region

SampleStack2: assets built

SampleStack1
SampleStack1: deploying... [1/3]
SampleStack3
SampleStack3: deploying... [2/3]
[0%] start: Publishing 91c7ea692437eca46b346f15e31203a58655905896d5613f847a767698081f24:current_account-current_region
[0%] start: Publishing 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Published 6cd9bcd505a1a584297e82e46762cabbc84dfbcf30c4d5455d2c8ac9e2e18dfb:current_account-current_region
[100%] success: Published 91c7ea692437eca46b346f15e31203a58655905896d5613f847a767698081f24:current_account-current_region
SampleStack3: creating CloudFormation changeset...
SampleStack1: creating CloudFormation changeset...
SampleStack3 | 0/3 | 16:13:53 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack3 User Initiated
SampleStack3 | 0/3 | 16:14:00 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack3 User Initiated
SampleStack3 | 0/3 | 16:14:05 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B)
SampleStack3 | 0/3 | 16:14:05 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack1 |  0/19 | 16:13:53 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack            | SampleStack1 User Initiated
SampleStack1 |  0/19 | 16:14:01 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack            | SampleStack1 User Initiated
SampleStack1 |  0/19 | 16:14:05 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5)
SampleStack1 |  0/19 | 16:14:05 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata)
SampleStack1 |  0/19 | 16:14:05 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454)
SampleStack1 |  0/19 | 16:14:06 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack1 |  1/19 | 16:14:07 | CREATE_COMPLETE      | AWS::CDK::Metadata                    | CDKMetadata/Default (CDKMetadata)
SampleStack3 | 0/3 | 16:14:06 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B) Resource creation Initiated
SampleStack3 | 0/3 | 16:14:07 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack3 | 1/3 | 16:14:07 | CREATE_COMPLETE      | AWS::CDK::Metadata   | CDKMetadata/Default (CDKMetadata)
SampleStack1 |  1/19 | 16:14:07 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454) Resource creation Initiated
SampleStack1 |  1/19 | 16:14:07 | CREATE_IN_PROGRESS   | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5) Resource creation Initiated
SampleStack3 | 2/3 | 16:14:17 | CREATE_COMPLETE      | AWS::DynamoDB::Table | SampleTable2 (SampleTable283919C1B)
SampleStack3 | 3/3 | 16:14:18 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | SampleStack3

 ✅  SampleStack3

✨  Deployment time: 34.69s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack3/16cf59b0-d1ef-11ed-80af-129f65a22c75

✨  Total time: 37.73s

SampleStack1 |  2/19 | 16:14:19 | CREATE_COMPLETE      | AWS::EC2::VPC                         | VPC_A (VPCAEF14CDB5)
SampleStack1 |  2/19 | 16:14:20 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86)
SampleStack1 |  2/19 | 16:14:20 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72)
SampleStack1 |  2/19 | 16:14:20 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262)
SampleStack1 |  2/19 | 16:14:20 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95)
SampleStack1 |  2/19 | 16:14:20 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C)
SampleStack1 |  2/19 | 16:14:21 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94)
SampleStack1 |  2/19 | 16:14:21 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A)
SampleStack1 |  2/19 | 16:14:21 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96)
SampleStack1 |  2/19 | 16:14:21 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86) Resource creation Initiated
SampleStack1 |  2/19 | 16:14:22 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262) Resource creation Initiated
SampleStack1 |  2/19 | 16:14:22 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72) Resource creation Initiated
SampleStack1 |  2/19 | 16:14:22 | CREATE_IN_PROGRESS   | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C) Resource creation Initiated
SampleStack1 |  2/19 | 16:14:22 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95) Resource creation Initiated
SampleStack1 |  2/19 | 16:14:22 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96) Resource creation Initiated
SampleStack1 |  3/19 | 16:14:23 | CREATE_COMPLETE      | AWS::EC2::InternetGateway             | VPC_A/IGW (VPCAIGW75D87454)
SampleStack1 |  3/19 | 16:14:23 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A) Resource creation Initiated
SampleStack1 |  3/19 | 16:14:23 | CREATE_IN_PROGRESS   | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94) Resource creation Initiated
SampleStack1 |  3/19 | 16:14:24 | CREATE_IN_PROGRESS   | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208)
SampleStack1 |  3/19 | 16:14:24 | CREATE_IN_PROGRESS   | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208) Resource creation Initiated
SampleStack1 |  4/19 | 16:14:25 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/privateSubnet2/Subnet (VPCAprivateSubnet2SubnetEF69CD95)
SampleStack1 |  5/19 | 16:14:25 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/privateSubnet1/Subnet (VPCAprivateSubnet1Subnet5428165A)
SampleStack1 |  6/19 | 16:14:26 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/publicSubnet1/Subnet (VPCApublicSubnet1SubnetCAE91A96)
SampleStack1 |  7/19 | 16:14:26 | CREATE_COMPLETE      | AWS::EC2::Subnet                      | VPC_A/publicSubnet2/Subnet (VPCApublicSubnet2Subnet8E975B94)
SampleStack1 |  8/19 | 16:14:32 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/privateSubnet2/RouteTable (VPCAprivateSubnet2RouteTable796E6A86)
SampleStack1 |  9/19 | 16:14:32 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/publicSubnet1/RouteTable (VPCApublicSubnet1RouteTable2C204262)
SampleStack1 | 10/19 | 16:14:32 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/privateSubnet1/RouteTable (VPCAprivateSubnet1RouteTableE655FC72)
SampleStack1 | 11/19 | 16:14:32 | CREATE_COMPLETE      | AWS::EC2::RouteTable                  | VPC_A/publicSubnet2/RouteTable (VPCApublicSubnet2RouteTable2008F73C)
SampleStack1 | 11/19 | 16:14:33 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE)
SampleStack1 | 11/19 | 16:14:34 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76)
SampleStack1 | 11/19 | 16:14:34 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A)
SampleStack1 | 11/19 | 16:14:34 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203)
SampleStack1 | 11/19 | 16:14:35 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE) Resource creation Initiated
SampleStack1 | 11/19 | 16:14:35 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76) Resource creation Initiated
SampleStack1 | 11/19 | 16:14:35 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A) Resource creation Initiated
SampleStack1 | 12/19 | 16:14:36 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet1/RouteTableAssociation (VPCApublicSubnet1RouteTableAssociation9C6FBB76)
SampleStack1 | 13/19 | 16:14:36 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/publicSubnet2/RouteTableAssociation (VPCApublicSubnet2RouteTableAssociationBD5B656A)
SampleStack1 | 13/19 | 16:14:36 | CREATE_IN_PROGRESS   | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203) Resource creation Initiated
SampleStack1 | 14/19 | 16:14:36 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet1/RouteTableAssociation (VPCAprivateSubnet1RouteTableAssociation833A7203)
SampleStack1 | 15/19 | 16:14:40 | CREATE_COMPLETE      | AWS::EC2::VPCGatewayAttachment        | VPC_A/VPCGW (VPCAVPCGWF4B02208)
SampleStack1 | 15/19 | 16:14:41 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678)
SampleStack1 | 15/19 | 16:14:42 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF)
SampleStack1 | 15/19 | 16:14:42 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678) Resource creation Initiated
SampleStack1 | 15/19 | 16:14:42 | CREATE_IN_PROGRESS   | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF) Resource creation Initiated
SampleStack1 | 16/19 | 16:14:45 | CREATE_COMPLETE      | AWS::EC2::SubnetRouteTableAssociation | VPC_A/privateSubnet2/RouteTableAssociation (VPCAprivateSubnet2RouteTableAssociation9DBD3CEE)
SampleStack1 | 17/19 | 16:14:58 | CREATE_COMPLETE      | AWS::EC2::Route                       | VPC_A/publicSubnet2/DefaultRoute (VPCApublicSubnet2DefaultRoute0A550678)
SampleStack1 | 18/19 | 16:14:58 | CREATE_COMPLETE      | AWS::EC2::Route                       | VPC_A/publicSubnet1/DefaultRoute (VPCApublicSubnet1DefaultRouteA342A3EF)
SampleStack1 | 19/19 | 16:15:00 | CREATE_COMPLETE      | AWS::CloudFormation::Stack            | SampleStack1

 ✅  SampleStack1

✨  Deployment time: 77.12s

Outputs:
SampleStack1.ExportsOutputRefVPCAEF14CDB53F68D3CF = vpc-068222553cacd6cf7
SampleStack1.ExportsOutputRefVPCAprivateSubnet1Subnet5428165ACB45B59D = subnet-00e76d51e20427a7d
Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack1/16d96bd0-d1ef-11ed-a4fb-12b5f657b2a1

✨  Total time: 80.16s

SampleStack2
SampleStack2: deploying... [3/3]
[0%] start: Publishing 7c33633358050e3ecfbd71ca8f40d12c364bb6fcd71338a52f100d27ae81d00b:current_account-current_region
[100%] success: Published 7c33633358050e3ecfbd71ca8f40d12c364bb6fcd71338a52f100d27ae81d00b:current_account-current_region
SampleStack2: creating CloudFormation changeset...
SampleStack2 | 0/7 | 16:15:13 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack2 User Initiated
SampleStack2 | 0/7 | 16:15:21 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | SampleStack2 User Initiated
SampleStack2 | 0/7 | 16:15:24 | CREATE_IN_PROGRESS   | AWS::IAM::Role            | SampleEC2Instance/InstanceRole (SampleEC2InstanceInstanceRoleF9B4605D)
SampleStack2 | 0/7 | 16:15:25 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table      | SampleTable1 (SampleTable1E3EC8AE3)
SampleStack2 | 0/7 | 16:15:25 | CREATE_IN_PROGRESS   | AWS::IAM::Role            | SampleEC2Instance/InstanceRole (SampleEC2InstanceInstanceRoleF9B4605D) Resource creation Initiated
SampleStack2 | 0/7 | 16:15:25 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata        | CDKMetadata/Default (CDKMetadata)
SampleStack2 | 0/7 | 16:15:25 | CREATE_IN_PROGRESS   | AWS::EC2::SecurityGroup   | SampleEC2Instance/InstanceSecurityGroup (SampleEC2InstanceInstanceSecurityGroupF6680F06)
SampleStack2 | 0/7 | 16:15:26 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata        | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
SampleStack2 | 1/7 | 16:15:26 | CREATE_COMPLETE      | AWS::CDK::Metadata        | CDKMetadata/Default (CDKMetadata)
SampleStack2 | 1/7 | 16:15:26 | CREATE_IN_PROGRESS   | AWS::DynamoDB::Table      | SampleTable1 (SampleTable1E3EC8AE3) Resource creation Initiated
SampleStack2 | 1/7 | 16:15:30 | CREATE_IN_PROGRESS   | AWS::EC2::SecurityGroup   | SampleEC2Instance/InstanceSecurityGroup (SampleEC2InstanceInstanceSecurityGroupF6680F06) Resource creation Initiated
SampleStack2 | 2/7 | 16:15:31 | CREATE_COMPLETE      | AWS::EC2::SecurityGroup   | SampleEC2Instance/InstanceSecurityGroup (SampleEC2InstanceInstanceSecurityGroupF6680F06)
SampleStack2 | 3/7 | 16:15:38 | CREATE_COMPLETE      | AWS::DynamoDB::Table      | SampleTable1 (SampleTable1E3EC8AE3)
SampleStack2 | 4/7 | 16:15:38 | CREATE_COMPLETE      | AWS::IAM::Role            | SampleEC2Instance/InstanceRole (SampleEC2InstanceInstanceRoleF9B4605D)
SampleStack2 | 4/7 | 16:15:40 | CREATE_IN_PROGRESS   | AWS::IAM::InstanceProfile | SampleEC2Instance/InstanceProfile (SampleEC2InstanceInstanceProfileA3EB8717)
SampleStack2 | 4/7 | 16:15:41 | CREATE_IN_PROGRESS   | AWS::IAM::InstanceProfile | SampleEC2Instance/InstanceProfile (SampleEC2InstanceInstanceProfileA3EB8717) Resource creation Initiated
4/7 Currently in progress: SampleStack2, SampleEC2InstanceInstanceProfileA3EB8717
SampleStack2 | 5/7 | 16:17:52 | CREATE_COMPLETE      | AWS::IAM::InstanceProfile | SampleEC2Instance/InstanceProfile (SampleEC2InstanceInstanceProfileA3EB8717)
SampleStack2 | 5/7 | 16:17:54 | CREATE_IN_PROGRESS   | AWS::EC2::Instance        | SampleEC2Instance (SampleEC2InstanceDE2B2B81)
SampleStack2 | 5/7 | 16:17:56 | CREATE_IN_PROGRESS   | AWS::EC2::Instance        | SampleEC2Instance (SampleEC2InstanceDE2B2B81) Resource creation Initiated
SampleStack2 | 6/7 | 16:18:03 | CREATE_COMPLETE      | AWS::EC2::Instance        | SampleEC2Instance (SampleEC2InstanceDE2B2B81)
SampleStack2 | 7/7 | 16:18:05 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | SampleStack2

 ✅  SampleStack2

✨  Deployment time: 186.27s

Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack2/9e2dd9a0-d1ee-11ed-ae15-12ef5f00c7e7

✨  Total time: 189.3s

concurrencyオプションで並行実行数は3としましたが、
1. SampleStack1,3をデプロイ
2. SampleStack1のデプロイ完了後にSampleStack2をデプロイ
という動作になっています。

自動的に依存関係が解決され、可能な部分で並行実行されていることがわかります。

さいごに

今回は並行デプロイ方法についてご紹介しました。
複数スタックを直列デプロイされている方、この機会に並行デプロイを試してみてはいかがでしょうか?

以上、八木でした!