CDK BootstrapのModern templateで何が変わるのか

2022.02.14

cdk bootstrapとは

cdk appを新しい環境(AWSアカウント x リージョン)でデプロイする際に、最初の一回だけ実行が必要なコマンドです。もう少し厳密に言うと、各環境(AWSアカウント x リージョン)にて以下いずれかに当てはまるappを初めてデプロイする際に必要になります。

  • アセットを必要とするスタックがある場合
  • 50キロバイトを超えるCFnテンプレートが生成される場合
  • DefaultSynthesizerを使用するスタックがある場合

cdk bootstrapコマンドを実行すると、CFn(CloudFormation)スタックが作成され、cdk appのデプロイに必要な周辺リソースが作成されます。

modern (vs legacy)

cdk bootstrapで作成されるCFnスタックで使われるCFnテンプレートには大別して2種類存在します。modern templateとlegacy templateです。もともとはlegacyのみ存在していて後にmodernが登場しました。2022年2月現在はどちらも利用可能です。

この2種類のtemplateにどういう違いがあるのか、特にlegacyからmodernに変わって何が改良されたのか興味が湧いたので調べてみました。

とりあえず modern/legacy両方やってみた

cdk bootstrap --show-templateでbootstrapコマンドで作成されるCFn(CloudFormation) スタックのテンプレートが出力できます。modern/legacy版両方で出力して差分を調べてみました。cdkのバージョンは 1.142.0です。

modern版への切り替え方

CDK v1では v1.25.0以降からmodern版が利用可能ですが、legacy版がデフォルトです。(v2ではmodern版しか利用できません)

v1でmodern版を利用したい場合、以下いずれかの方法を使います。

環境変数を使う

環境変数 CDK_NEW_BOOTSTRAPに1をセットした状態でbootstrapコマンドを実行します。

% export CDK_NEW_BOOTSTRAP=1
% cdk bootstrap --show-template > modern-bootstrap.yaml

CDK_NEW_BOOTSTRAP set, using new-style bootstrapping

cdk.json内のフィーチャーフラグをセットする

bootstrapコマンドをcdkのappのディレクトリ上で実行する場合は、そのappのcdk.json内のフィーチャーフラグを@aws-cdk/core:newStyleStackSynthesistrueにセットする方法でもいけます。

cdk.json

{
  "app": "npx ts-node --prefer-ts-exts bin/modern.ts",
  "context": {
    "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
    "@aws-cdk/core:enableStackNameDuplicates": "true",
    "@aws-cdk/core:newStyleStackSynthesis": true,
    "aws-cdk:enableDiffNoFail": "true",
    "@aws-cdk/core:stackRelativeExports": "true",
    "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true,
    "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true,
    "@aws-cdk/aws-kms:defaultKeyPolicies": true,
    "@aws-cdk/aws-s3:grantWriteWithoutAcl": true,
    "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true,
    "@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
    "@aws-cdk/aws-efs:defaultEncryptionAtRest": true,
    "@aws-cdk/aws-lambda:recognizeVersionProps": true,
    "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true
  }
}
% npx cdk bootstrap --show-template > modern-bootstrap.yaml 

'@aws-cdk/core:newStyleStackSynthesis' context set, using new-style bootstrapping

というわけで両方のテンプレートを出力してみました。

legacyのテンプレート

Description: The CDK Toolkit Stack. It was created by `cdk bootstrap` and manages resources necessary for managing your Cloud Applications with AWS CDK.
Conditions:
  UsePublicAccessBlockConfiguration:
    Fn::Equals:
      - "true"
      - "true"
Resources:
  StagingBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: null
      AccessControl: Private
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
              KMSMasterKeyID: null
      PublicAccessBlockConfiguration:
        Fn::If:
          - UsePublicAccessBlockConfiguration
          - BlockPublicAcls: true
            BlockPublicPolicy: true
            IgnorePublicAcls: true
            RestrictPublicBuckets: true
          - Ref: AWS::NoValue
  StagingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: StagingBucket
      PolicyDocument:
        Id: AccessControl
        Version: "2012-10-17"
        Statement:
          - Sid: AllowSSLRequestsOnly
            Action: s3:*
            Effect: Deny
            Resource:
              - Fn::Sub: ${StagingBucket.Arn}
              - Fn::Sub: ${StagingBucket.Arn}/*
            Condition:
              Bool:
                aws:SecureTransport: "false"
            Principal: "*"
Outputs:
  BucketName:
    Description: The name of the S3 bucket owned by the CDK toolkit stack
    Value:
      Ref: StagingBucket
  BucketDomainName:
    Description: The domain name of the S3 bucket owned by the CDK toolkit stack
    Value:
      Fn::GetAtt:
        - StagingBucket
        - RegionalDomainName

modernのテンプレート

Description: This stack includes resources needed to deploy AWS CDK apps into this environment
Parameters:
  TrustedAccounts:
    Description: List of AWS accounts that are trusted to publish assets and deploy stacks to this environment
    Default: ""
    Type: CommaDelimitedList
  TrustedAccountsForLookup:
    Description: List of AWS accounts that are trusted to look up values in this environment
    Default: ""
    Type: CommaDelimitedList
  CloudFormationExecutionPolicies:
    Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation deployment role
    Default: ""
    Type: CommaDelimitedList
  FileAssetsBucketName:
    Description: The name of the S3 bucket used for file assets
    Default: ""
    Type: String
  FileAssetsBucketKmsKeyId:
    Description: Empty to create a new key (default), 'AWS_MANAGED_KEY' to use a managed S3 key, or the ID/ARN of an existing key.
    Default: ""
    Type: String
  ContainerAssetsRepositoryName:
    Description: A user-provided custom name to use for the container assets ECR repository
    Default: ""
    Type: String
  Qualifier:
    Description: An identifier to distinguish multiple bootstrap stacks in the same environment
    Default: hnb659fds
    Type: String
    AllowedPattern: "[A-Za-z0-9_-]{1,10}"
    ConstraintDescription: Qualifier must be an alphanumeric identifier of at most 10 characters
  PublicAccessBlockConfiguration:
    Description: Whether or not to enable S3 Staging Bucket Public Access Block Configuration
    Default: "true"
    Type: String
    AllowedValues:
      - "true"
      - "false"
Conditions:
  HasTrustedAccounts:
    Fn::Not:
      - Fn::Equals:
          - ""
          - Fn::Join:
              - ""
              - Ref: TrustedAccounts
  HasTrustedAccountsForLookup:
    Fn::Not:
      - Fn::Equals:
          - ""
          - Fn::Join:
              - ""
              - Ref: TrustedAccountsForLookup
  HasCloudFormationExecutionPolicies:
    Fn::Not:
      - Fn::Equals:
          - ""
          - Fn::Join:
              - ""
              - Ref: CloudFormationExecutionPolicies
  HasCustomFileAssetsBucketName:
    Fn::Not:
      - Fn::Equals:
          - ""
          - Ref: FileAssetsBucketName
  CreateNewKey:
    Fn::Equals:
      - ""
      - Ref: FileAssetsBucketKmsKeyId
  UseAwsManagedKey:
    Fn::Equals:
      - AWS_MANAGED_KEY
      - Ref: FileAssetsBucketKmsKeyId
  HasCustomContainerAssetsRepositoryName:
    Fn::Not:
      - Fn::Equals:
          - ""
          - Ref: ContainerAssetsRepositoryName
  UsePublicAccessBlockConfiguration:
    Fn::Equals:
      - "true"
      - Ref: PublicAccessBlockConfiguration
Resources:
  FileAssetsBucketEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      KeyPolicy:
        Statement:
          - Action:
              - kms:Create*
              - kms:Describe*
              - kms:Enable*
              - kms:List*
              - kms:Put*
              - kms:Update*
              - kms:Revoke*
              - kms:Disable*
              - kms:Get*
              - kms:Delete*
              - kms:ScheduleKeyDeletion
              - kms:CancelKeyDeletion
              - kms:GenerateDataKey
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
            Resource: "*"
          - Action:
              - kms:Decrypt
              - kms:DescribeKey
              - kms:Encrypt
              - kms:ReEncrypt*
              - kms:GenerateDataKey*
            Effect: Allow
            Principal:
              AWS: "*"
            Resource: "*"
            Condition:
              StringEquals:
                kms:CallerAccount:
                  Ref: AWS::AccountId
                kms:ViaService:
                  - Fn::Sub: s3.${AWS::Region}.amazonaws.com
          - Action:
              - kms:Decrypt
              - kms:DescribeKey
              - kms:Encrypt
              - kms:ReEncrypt*
              - kms:GenerateDataKey*
            Effect: Allow
            Principal:
              AWS:
                Fn::Sub: ${FilePublishingRole.Arn}
            Resource: "*"
    Condition: CreateNewKey
  FileAssetsBucketEncryptionKeyAlias:
    Condition: CreateNewKey
    Type: AWS::KMS::Alias
    Properties:
      AliasName:
        Fn::Sub: alias/cdk-${Qualifier}-assets-key
      TargetKeyId:
        Ref: FileAssetsBucketEncryptionKey
  StagingBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName:
        Fn::If:
          - HasCustomFileAssetsBucketName
          - Fn::Sub: ${FileAssetsBucketName}
          - Fn::Sub: cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region}
      AccessControl: Private
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
              KMSMasterKeyID:
                Fn::If:
                  - CreateNewKey
                  - Fn::Sub: ${FileAssetsBucketEncryptionKey.Arn}
                  - Fn::If:
                      - UseAwsManagedKey
                      - Ref: AWS::NoValue
                      - Fn::Sub: ${FileAssetsBucketKmsKeyId}
      PublicAccessBlockConfiguration:
        Fn::If:
          - UsePublicAccessBlockConfiguration
          - BlockPublicAcls: true
            BlockPublicPolicy: true
            IgnorePublicAcls: true
            RestrictPublicBuckets: true
          - Ref: AWS::NoValue
      VersioningConfiguration:
        Status: Enabled
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
  StagingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: StagingBucket
      PolicyDocument:
        Id: AccessControl
        Version: "2012-10-17"
        Statement:
          - Sid: AllowSSLRequestsOnly
            Action: s3:*
            Effect: Deny
            Resource:
              - Fn::Sub: ${StagingBucket.Arn}
              - Fn::Sub: ${StagingBucket.Arn}/*
            Condition:
              Bool:
                aws:SecureTransport: "false"
            Principal: "*"
  ContainerAssetsRepository:
    Type: AWS::ECR::Repository
    Properties:
      ImageScanningConfiguration:
        ScanOnPush: true
      RepositoryName:
        Fn::If:
          - HasCustomContainerAssetsRepositoryName
          - Fn::Sub: ${ContainerAssetsRepositoryName}
          - Fn::Sub: cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region}
  FilePublishingRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
          - Fn::If:
              - HasTrustedAccounts
              - Action: sts:AssumeRole
                Effect: Allow
                Principal:
                  AWS:
                    Ref: TrustedAccounts
              - Ref: AWS::NoValue
      RoleName:
        Fn::Sub: cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}
      Tags:
        - Key: aws-cdk:bootstrap-role
          Value: file-publishing
  ImagePublishingRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
          - Fn::If:
              - HasTrustedAccounts
              - Action: sts:AssumeRole
                Effect: Allow
                Principal:
                  AWS:
                    Ref: TrustedAccounts
              - Ref: AWS::NoValue
      RoleName:
        Fn::Sub: cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region}
      Tags:
        - Key: aws-cdk:bootstrap-role
          Value: image-publishing
  LookupRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
          - Fn::If:
              - HasTrustedAccountsForLookup
              - Action: sts:AssumeRole
                Effect: Allow
                Principal:
                  AWS:
                    Ref: TrustedAccountsForLookup
              - Ref: AWS::NoValue
          - Fn::If:
              - HasTrustedAccounts
              - Action: sts:AssumeRole
                Effect: Allow
                Principal:
                  AWS:
                    Ref: TrustedAccounts
              - Ref: AWS::NoValue
      RoleName:
        Fn::Sub: cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}
      ManagedPolicyArns:
        - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess
      Policies:
        - PolicyDocument:
            Statement:
              - Sid: DontReadSecrets
                Effect: Deny
                Action:
                  - kms:Decrypt
                Resource: "*"
            Version: "2012-10-17"
          PolicyName: LookupRolePolicy
      Tags:
        - Key: aws-cdk:bootstrap-role
          Value: lookup
  FilePublishingRoleDefaultPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action:
              - s3:GetObject*
              - s3:GetBucket*
              - FilePublishingRoleDefaultPolicy
              - s3:List*
              - s3:DeleteObject*
              - s3:PutObject*
              - s3:Abort*
            Resource:
              - Fn::Sub: ${StagingBucket.Arn}
              - Fn::Sub: ${StagingBucket.Arn}/*
            Effect: Allow
          - Action:
              - kms:Decrypt
              - kms:DescribeKey
              - kms:Encrypt
              - kms:ReEncrypt*
              - kms:GenerateDataKey*
            Effect: Allow
            Resource:
              Fn::If:
                - CreateNewKey
                - Fn::Sub: ${FileAssetsBucketEncryptionKey.Arn}
                - Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${FileAssetsBucketKmsKeyId}
        Version: "2012-10-17"
      Roles:
        - Ref: FilePublishingRole
      PolicyName:
        Fn::Sub: cdk-${Qualifier}-file-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region}
  ImagePublishingRoleDefaultPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action:
              - ecr:PutImage
              - ecr:InitiateLayerUpload
              - ecr:UploadLayerPart
              - ecr:CompleteLayerUpload
              - ecr:BatchCheckLayerAvailability
              - ecr:DescribeRepositories
              - ecr:DescribeImages
              - ecr:BatchGetImage
              - ecr:GetDownloadUrlForLayer
            Resource:
              Fn::Sub: ${ContainerAssetsRepository.Arn}
            Effect: Allow
          - Action:
              - ecr:GetAuthorizationToken
            Resource: "*"
            Effect: Allow
        Version: "2012-10-17"
      Roles:
        - Ref: ImagePublishingRole
      PolicyName:
        Fn::Sub: cdk-${Qualifier}-image-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region}
  DeploymentActionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
          - Fn::If:
              - HasTrustedAccounts
              - Action: sts:AssumeRole
                Effect: Allow
                Principal:
                  AWS:
                    Ref: TrustedAccounts
              - Ref: AWS::NoValue
      Policies:
        - PolicyDocument:
            Statement:
              - Sid: CloudFormationPermissions
                Effect: Allow
                Action:
                  - cloudformation:CreateChangeSet
                  - cloudformation:DeleteChangeSet
                  - cloudformation:DescribeChangeSet
                  - cloudformation:DescribeStacks
                  - cloudformation:ExecuteChangeSet
                  - cloudformation:CreateStack
                  - cloudformation:UpdateStack
                Resource: "*"
              - Sid: PipelineCrossAccountArtifactsBucket
                Effect: Allow
                Action:
                  - s3:GetObject*
                  - s3:GetBucket*
                  - s3:List*
                  - s3:Abort*
                  - s3:DeleteObject*
                  - s3:PutObject*
                Resource: "*"
                Condition:
                  StringNotEquals:
                    s3:ResourceAccount:
                      Ref: AWS::AccountId
              - Sid: PipelineCrossAccountArtifactsKey
                Effect: Allow
                Action:
                  - kms:Decrypt
                  - kms:DescribeKey
                  - kms:Encrypt
                  - kms:ReEncrypt*
                  - kms:GenerateDataKey*
                Resource: "*"
                Condition:
                  StringEquals:
                    kms:ViaService:
                      Fn::Sub: s3.${AWS::Region}.amazonaws.com
              - Action: iam:PassRole
                Resource:
                  Fn::Sub: ${CloudFormationExecutionRole.Arn}
                Effect: Allow
              - Sid: CliPermissions
                Action:
                  - cloudformation:DescribeStackEvents
                  - cloudformation:GetTemplate
                  - cloudformation:DeleteStack
                  - cloudformation:UpdateTerminationProtection
                  - sts:GetCallerIdentity
                Resource: "*"
                Effect: Allow
              - Sid: CliStagingBucket
                Effect: Allow
                Action:
                  - s3:GetObject*
                  - s3:GetBucket*
                  - s3:List*
                Resource:
                  - Fn::Sub: ${StagingBucket.Arn}
                  - Fn::Sub: ${StagingBucket.Arn}/*
              - Sid: ReadVersion
                Effect: Allow
                Action:
                  - ssm:GetParameter
                Resource:
                  - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CdkBootstrapVersion}
            Version: "2012-10-17"
          PolicyName: default
      RoleName:
        Fn::Sub: cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}
      Tags:
        - Key: aws-cdk:bootstrap-role
          Value: deploy
  CloudFormationExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: cloudformation.amazonaws.com
        Version: "2012-10-17"
      ManagedPolicyArns:
        Fn::If:
          - HasCloudFormationExecutionPolicies
          - Ref: CloudFormationExecutionPolicies
          - Fn::If:
              - HasTrustedAccounts
              - Ref: AWS::NoValue
              - - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess
      RoleName:
        Fn::Sub: cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}
  CdkBootstrapVersion:
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Name:
        Fn::Sub: /cdk-bootstrap/${Qualifier}/version
      Value: "10"
Outputs:
  BucketName:
    Description: The name of the S3 bucket owned by the CDK toolkit stack
    Value:
      Fn::Sub: ${StagingBucket}
  BucketDomainName:
    Description: The domain name of the S3 bucket owned by the CDK toolkit stack
    Value:
      Fn::Sub: ${StagingBucket.RegionalDomainName}
  FileAssetKeyArn:
    Description: The ARN of the KMS key used to encrypt the asset bucket (deprecated)
    Value:
      Fn::If:
        - CreateNewKey
        - Fn::Sub: ${FileAssetsBucketEncryptionKey.Arn}
        - Fn::Sub: ${FileAssetsBucketKmsKeyId}
    Export:
      Name:
        Fn::Sub: CdkBootstrap-${Qualifier}-FileAssetKeyArn
  ImageRepositoryName:
    Description: The name of the ECR repository which hosts docker image assets
    Value:
      Fn::Sub: ${ContainerAssetsRepository}
  BootstrapVersion:
    Description: The version of the bootstrap resources that are currently mastered in this stack
    Value:
      Fn::GetAtt:
        - CdkBootstrapVersion
        - Value

diff

diffも取ってみました。

--- bootstrap-template-legacy.yaml	2022-01-28 15:54:29.000000000 +0900
+++ bootstrap-template-modern.yaml	2022-01-28 18:41:30.000000000 +0900
@@ -1,20 +1,168 @@
-Description: The CDK Toolkit Stack. It was created by `cdk bootstrap` and manages resources necessary for managing your Cloud Applications with AWS CDK.
+Description: This stack includes resources needed to deploy AWS CDK apps into this environment
+Parameters:
+  TrustedAccounts:
+    Description: List of AWS accounts that are trusted to publish assets and deploy stacks to this environment
+    Default: ""
+    Type: CommaDelimitedList
+  TrustedAccountsForLookup:
+    Description: List of AWS accounts that are trusted to look up values in this environment
+    Default: ""
+    Type: CommaDelimitedList
+  CloudFormationExecutionPolicies:
+    Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation deployment role
+    Default: ""
+    Type: CommaDelimitedList
+  FileAssetsBucketName:
+    Description: The name of the S3 bucket used for file assets
+    Default: ""
+    Type: String
+  FileAssetsBucketKmsKeyId:
+    Description: Empty to create a new key (default), 'AWS_MANAGED_KEY' to use a managed S3 key, or the ID/ARN of an existing key.
+    Default: ""
+    Type: String
+  ContainerAssetsRepositoryName:
+    Description: A user-provided custom name to use for the container assets ECR repository
+    Default: ""
+    Type: String
+  Qualifier:
+    Description: An identifier to distinguish multiple bootstrap stacks in the same environment
+    Default: hnb659fds
+    Type: String
+    AllowedPattern: "[A-Za-z0-9_-]{1,10}"
+    ConstraintDescription: Qualifier must be an alphanumeric identifier of at most 10 characters
+  PublicAccessBlockConfiguration:
+    Description: Whether or not to enable S3 Staging Bucket Public Access Block Configuration
+    Default: "true"
+    Type: String
+    AllowedValues:
+      - "true"
+      - "false"
 Conditions:
+  HasTrustedAccounts:
+    Fn::Not:
+      - Fn::Equals:
+          - ""
+          - Fn::Join:
+              - ""
+              - Ref: TrustedAccounts
+  HasTrustedAccountsForLookup:
+    Fn::Not:
+      - Fn::Equals:
+          - ""
+          - Fn::Join:
+              - ""
+              - Ref: TrustedAccountsForLookup
+  HasCloudFormationExecutionPolicies:
+    Fn::Not:
+      - Fn::Equals:
+          - ""
+          - Fn::Join:
+              - ""
+              - Ref: CloudFormationExecutionPolicies
+  HasCustomFileAssetsBucketName:
+    Fn::Not:
+      - Fn::Equals:
+          - ""
+          - Ref: FileAssetsBucketName
+  CreateNewKey:
+    Fn::Equals:
+      - ""
+      - Ref: FileAssetsBucketKmsKeyId
+  UseAwsManagedKey:
+    Fn::Equals:
+      - AWS_MANAGED_KEY
+      - Ref: FileAssetsBucketKmsKeyId
+  HasCustomContainerAssetsRepositoryName:
+    Fn::Not:
+      - Fn::Equals:
+          - ""
+          - Ref: ContainerAssetsRepositoryName
   UsePublicAccessBlockConfiguration:
     Fn::Equals:
       - "true"
-      - "true"
+      - Ref: PublicAccessBlockConfiguration
 Resources:
+  FileAssetsBucketEncryptionKey:
+    Type: AWS::KMS::Key
+    Properties:
+      KeyPolicy:
+        Statement:
+          - Action:
+              - kms:Create*
+              - kms:Describe*
+              - kms:Enable*
+              - kms:List*
+              - kms:Put*
+              - kms:Update*
+              - kms:Revoke*
+              - kms:Disable*
+              - kms:Get*
+              - kms:Delete*
+              - kms:ScheduleKeyDeletion
+              - kms:CancelKeyDeletion
+              - kms:GenerateDataKey
+            Effect: Allow
+            Principal:
+              AWS:
+                Ref: AWS::AccountId
+            Resource: "*"
+          - Action:
+              - kms:Decrypt
+              - kms:DescribeKey
+              - kms:Encrypt
+              - kms:ReEncrypt*
+              - kms:GenerateDataKey*
+            Effect: Allow
+            Principal:
+              AWS: "*"
+            Resource: "*"
+            Condition:
+              StringEquals:
+                kms:CallerAccount:
+                  Ref: AWS::AccountId
+                kms:ViaService:
+                  - Fn::Sub: s3.${AWS::Region}.amazonaws.com
+          - Action:
+              - kms:Decrypt
+              - kms:DescribeKey
+              - kms:Encrypt
+              - kms:ReEncrypt*
+              - kms:GenerateDataKey*
+            Effect: Allow
+            Principal:
+              AWS:
+                Fn::Sub: ${FilePublishingRole.Arn}
+            Resource: "*"
+    Condition: CreateNewKey
+  FileAssetsBucketEncryptionKeyAlias:
+    Condition: CreateNewKey
+    Type: AWS::KMS::Alias
+    Properties:
+      AliasName:
+        Fn::Sub: alias/cdk-${Qualifier}-assets-key
+      TargetKeyId:
+        Ref: FileAssetsBucketEncryptionKey
   StagingBucket:
     Type: AWS::S3::Bucket
     Properties:
-      BucketName: null
+      BucketName:
+        Fn::If:
+          - HasCustomFileAssetsBucketName
+          - Fn::Sub: ${FileAssetsBucketName}
+          - Fn::Sub: cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region}
       AccessControl: Private
       BucketEncryption:
         ServerSideEncryptionConfiguration:
           - ServerSideEncryptionByDefault:
               SSEAlgorithm: aws:kms
-              KMSMasterKeyID: null
+              KMSMasterKeyID:
+                Fn::If:
+                  - CreateNewKey
+                  - Fn::Sub: ${FileAssetsBucketEncryptionKey.Arn}
+                  - Fn::If:
+                      - UseAwsManagedKey
+                      - Ref: AWS::NoValue
+                      - Fn::Sub: ${FileAssetsBucketKmsKeyId}
       PublicAccessBlockConfiguration:
         Fn::If:
           - UsePublicAccessBlockConfiguration
@@ -23,6 +171,10 @@
             IgnorePublicAcls: true
             RestrictPublicBuckets: true
           - Ref: AWS::NoValue
+      VersioningConfiguration:
+        Status: Enabled
+    UpdateReplacePolicy: Retain
+    DeletionPolicy: Retain
   StagingBucketPolicy:
     Type: AWS::S3::BucketPolicy
     Properties:
@@ -42,15 +194,311 @@
               Bool:
                 aws:SecureTransport: "false"
             Principal: "*"
+  ContainerAssetsRepository:
+    Type: AWS::ECR::Repository
+    Properties:
+      ImageScanningConfiguration:
+        ScanOnPush: true
+      RepositoryName:
+        Fn::If:
+          - HasCustomContainerAssetsRepositoryName
+          - Fn::Sub: ${ContainerAssetsRepositoryName}
+          - Fn::Sub: cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region}
+  FilePublishingRole:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Statement:
+          - Action: sts:AssumeRole
+            Effect: Allow
+            Principal:
+              AWS:
+                Ref: AWS::AccountId
+          - Fn::If:
+              - HasTrustedAccounts
+              - Action: sts:AssumeRole
+                Effect: Allow
+                Principal:
+                  AWS:
+                    Ref: TrustedAccounts
+              - Ref: AWS::NoValue
+      RoleName:
+        Fn::Sub: cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}
+      Tags:
+        - Key: aws-cdk:bootstrap-role
+          Value: file-publishing
+  ImagePublishingRole:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Statement:
+          - Action: sts:AssumeRole
+            Effect: Allow
+            Principal:
+              AWS:
+                Ref: AWS::AccountId
+          - Fn::If:
+              - HasTrustedAccounts
+              - Action: sts:AssumeRole
+                Effect: Allow
+                Principal:
+                  AWS:
+                    Ref: TrustedAccounts
+              - Ref: AWS::NoValue
+      RoleName:
+        Fn::Sub: cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region}
+      Tags:
+        - Key: aws-cdk:bootstrap-role
+          Value: image-publishing
+  LookupRole:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Statement:
+          - Action: sts:AssumeRole
+            Effect: Allow
+            Principal:
+              AWS:
+                Ref: AWS::AccountId
+          - Fn::If:
+              - HasTrustedAccountsForLookup
+              - Action: sts:AssumeRole
+                Effect: Allow
+                Principal:
+                  AWS:
+                    Ref: TrustedAccountsForLookup
+              - Ref: AWS::NoValue
+          - Fn::If:
+              - HasTrustedAccounts
+              - Action: sts:AssumeRole
+                Effect: Allow
+                Principal:
+                  AWS:
+                    Ref: TrustedAccounts
+              - Ref: AWS::NoValue
+      RoleName:
+        Fn::Sub: cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}
+      ManagedPolicyArns:
+        - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess
+      Policies:
+        - PolicyDocument:
+            Statement:
+              - Sid: DontReadSecrets
+                Effect: Deny
+                Action:
+                  - kms:Decrypt
+                Resource: "*"
+            Version: "2012-10-17"
+          PolicyName: LookupRolePolicy
+      Tags:
+        - Key: aws-cdk:bootstrap-role
+          Value: lookup
+  FilePublishingRoleDefaultPolicy:
+    Type: AWS::IAM::Policy
+    Properties:
+      PolicyDocument:
+        Statement:
+          - Action:
+              - s3:GetObject*
+              - s3:GetBucket*
+              - FilePublishingRoleDefaultPolicy
+              - s3:List*
+              - s3:DeleteObject*
+              - s3:PutObject*
+              - s3:Abort*
+            Resource:
+              - Fn::Sub: ${StagingBucket.Arn}
+              - Fn::Sub: ${StagingBucket.Arn}/*
+            Effect: Allow
+          - Action:
+              - kms:Decrypt
+              - kms:DescribeKey
+              - kms:Encrypt
+              - kms:ReEncrypt*
+              - kms:GenerateDataKey*
+            Effect: Allow
+            Resource:
+              Fn::If:
+                - CreateNewKey
+                - Fn::Sub: ${FileAssetsBucketEncryptionKey.Arn}
+                - Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${FileAssetsBucketKmsKeyId}
+        Version: "2012-10-17"
+      Roles:
+        - Ref: FilePublishingRole
+      PolicyName:
+        Fn::Sub: cdk-${Qualifier}-file-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region}
+  ImagePublishingRoleDefaultPolicy:
+    Type: AWS::IAM::Policy
+    Properties:
+      PolicyDocument:
+        Statement:
+          - Action:
+              - ecr:PutImage
+              - ecr:InitiateLayerUpload
+              - ecr:UploadLayerPart
+              - ecr:CompleteLayerUpload
+              - ecr:BatchCheckLayerAvailability
+              - ecr:DescribeRepositories
+              - ecr:DescribeImages
+              - ecr:BatchGetImage
+              - ecr:GetDownloadUrlForLayer
+            Resource:
+              Fn::Sub: ${ContainerAssetsRepository.Arn}
+            Effect: Allow
+          - Action:
+              - ecr:GetAuthorizationToken
+            Resource: "*"
+            Effect: Allow
+        Version: "2012-10-17"
+      Roles:
+        - Ref: ImagePublishingRole
+      PolicyName:
+        Fn::Sub: cdk-${Qualifier}-image-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region}
+  DeploymentActionRole:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Statement:
+          - Action: sts:AssumeRole
+            Effect: Allow
+            Principal:
+              AWS:
+                Ref: AWS::AccountId
+          - Fn::If:
+              - HasTrustedAccounts
+              - Action: sts:AssumeRole
+                Effect: Allow
+                Principal:
+                  AWS:
+                    Ref: TrustedAccounts
+              - Ref: AWS::NoValue
+      Policies:
+        - PolicyDocument:
+            Statement:
+              - Sid: CloudFormationPermissions
+                Effect: Allow
+                Action:
+                  - cloudformation:CreateChangeSet
+                  - cloudformation:DeleteChangeSet
+                  - cloudformation:DescribeChangeSet
+                  - cloudformation:DescribeStacks
+                  - cloudformation:ExecuteChangeSet
+                  - cloudformation:CreateStack
+                  - cloudformation:UpdateStack
+                Resource: "*"
+              - Sid: PipelineCrossAccountArtifactsBucket
+                Effect: Allow
+                Action:
+                  - s3:GetObject*
+                  - s3:GetBucket*
+                  - s3:List*
+                  - s3:Abort*
+                  - s3:DeleteObject*
+                  - s3:PutObject*
+                Resource: "*"
+                Condition:
+                  StringNotEquals:
+                    s3:ResourceAccount:
+                      Ref: AWS::AccountId
+              - Sid: PipelineCrossAccountArtifactsKey
+                Effect: Allow
+                Action:
+                  - kms:Decrypt
+                  - kms:DescribeKey
+                  - kms:Encrypt
+                  - kms:ReEncrypt*
+                  - kms:GenerateDataKey*
+                Resource: "*"
+                Condition:
+                  StringEquals:
+                    kms:ViaService:
+                      Fn::Sub: s3.${AWS::Region}.amazonaws.com
+              - Action: iam:PassRole
+                Resource:
+                  Fn::Sub: ${CloudFormationExecutionRole.Arn}
+                Effect: Allow
+              - Sid: CliPermissions
+                Action:
+                  - cloudformation:DescribeStackEvents
+                  - cloudformation:GetTemplate
+                  - cloudformation:DeleteStack
+                  - cloudformation:UpdateTerminationProtection
+                  - sts:GetCallerIdentity
+                Resource: "*"
+                Effect: Allow
+              - Sid: CliStagingBucket
+                Effect: Allow
+                Action:
+                  - s3:GetObject*
+                  - s3:GetBucket*
+                  - s3:List*
+                Resource:
+                  - Fn::Sub: ${StagingBucket.Arn}
+                  - Fn::Sub: ${StagingBucket.Arn}/*
+              - Sid: ReadVersion
+                Effect: Allow
+                Action:
+                  - ssm:GetParameter
+                Resource:
+                  - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CdkBootstrapVersion}
+            Version: "2012-10-17"
+          PolicyName: default
+      RoleName:
+        Fn::Sub: cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}
+      Tags:
+        - Key: aws-cdk:bootstrap-role
+          Value: deploy
+  CloudFormationExecutionRole:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Statement:
+          - Action: sts:AssumeRole
+            Effect: Allow
+            Principal:
+              Service: cloudformation.amazonaws.com
+        Version: "2012-10-17"
+      ManagedPolicyArns:
+        Fn::If:
+          - HasCloudFormationExecutionPolicies
+          - Ref: CloudFormationExecutionPolicies
+          - Fn::If:
+              - HasTrustedAccounts
+              - Ref: AWS::NoValue
+              - - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess
+      RoleName:
+        Fn::Sub: cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}
+  CdkBootstrapVersion:
+    Type: AWS::SSM::Parameter
+    Properties:
+      Type: String
+      Name:
+        Fn::Sub: /cdk-bootstrap/${Qualifier}/version
+      Value: "10"
 Outputs:
   BucketName:
     Description: The name of the S3 bucket owned by the CDK toolkit stack
     Value:
-      Ref: StagingBucket
+      Fn::Sub: ${StagingBucket}
   BucketDomainName:
     Description: The domain name of the S3 bucket owned by the CDK toolkit stack
     Value:
+      Fn::Sub: ${StagingBucket.RegionalDomainName}
+  FileAssetKeyArn:
+    Description: The ARN of the KMS key used to encrypt the asset bucket (deprecated)
+    Value:
+      Fn::If:
+        - CreateNewKey
+        - Fn::Sub: ${FileAssetsBucketEncryptionKey.Arn}
+        - Fn::Sub: ${FileAssetsBucketKmsKeyId}
+    Export:
+      Name:
+        Fn::Sub: CdkBootstrap-${Qualifier}-FileAssetKeyArn
+  ImageRepositoryName:
+    Description: The name of the ECR repository which hosts docker image assets
+    Value:
+      Fn::Sub: ${ContainerAssetsRepository}
+  BootstrapVersion:
+    Description: The version of the bootstrap resources that are currently mastered in this stack
+    Value:
       Fn::GetAtt:
-        - StagingBucket
-        - RegionalDomainName
+        - CdkBootstrapVersion
+        - Value

ざっくりわかることは…Modern版の方がかなりテンプレートが大きく、作るリソースの種類が多いですね。

Resources

legacyで作成されるリソースは以下2つだけです。

  • AWS::S3::Bucket
  • AWS::S3::BucketPolicy

対してmodernには上記に加えて以下リソースが作成されます。

  • AWS::SSM::Parameter
  • AWS::IAM::Role 5つ
  • AWS::ECR::Repository
  • AWS::IAM::Policy 2つ

また条件次第で以下も作成されます。

  • AWS::KMS::Key
  • AWS::KMS::Alias

Outputs

新たに作られたKMSキーやECRリポジトリの情報が追加されています。またBootstrapVersionなる項目もあります。

Parameters

legacyにはParametersはゼロですが、modernには8個あります。

結局 modern templateになって何が良くなるの?

ここまで書いておいてなんですが、正直テンプレートの差分自体はどうでも良いですよね。重要なのはこういった差分によって得られるメリットです。

デプロイ実行時の権限が分離される

legacyの場合、cdk appをcdk deployコマンドにて(CFnを介して)デプロイする際に使われる権限は、コマンドを実行したエンティティの権限です。aws sts get-caller-identityコマンドで確認できるやつですね。

対してmodernではbootstrapスタックがプロビジョニングされたときに指定されたアクセス許可を使用してデプロイします。modern templateの中にIAM Roleが5つあると先程ご紹介しました。これが使われます。各ロールの詳細を説明します。

DeploymentActionRole

cdk deployなどのcdkコマンドを打つと基本的にこのロールにassume roleしてからAWS上の操作を行なうようです。「基本的に」というのは、他のロールの責務外の箇所全部、という意味です。

CloudFormationExecutionRole

CDKはCFnテンプレートを生成してそのテンプレートをCFnに食わせて各リソースを作成するツールですが、CFnがリソースを作成する際に使われるロールです。以下で紹介されているIAM Roleを介してCFnを実行するやつです。

なお、これを実現するために、先のDeploymentActionRoleにCloudFormationExecutionRoleに対してのiam:PassRoleが許可されています。

また、このCloudFormationExecutionRoleは各リソースを作成する際に使われるロールですので、デフォルトではAdministratorAccessポリシーがアタッチされます。が、cdk bootstrapコマンドの--cloudformation-execution-policiesで別のポリシーのARNを指定すれば上書きすることが可能です。

LookupRole

以下エントリで使われているVPC ConstructのfromLookupstatic methodのようなfromLookupメソッドを使う場合に使われるロールです。

各種fromLookupメソッドは既存リソースを参照する際に使われるメソッドなので、このロールにはReadOnlyAccessマネージドポリシーがアタッチされます。

ImagePublishingRole

modern templateで作成されるECRリポジトリにアクセスするのに使われます。このECRリポジトリが使われるのは、cdk appの中でコンテナイメージをbuild、pushするような処理が必要な場合です。例えば以下のコードのような、特定のディレクトリ以下にあるDockerfileとソースコードを基にコンテナイメージを作り、それをLambda関数にするような場合です。

const getMovieFunction = new lambda.DockerImageFunction(this, 'getMovieFunction',{
    functionName: 'getMovieFunction',
    code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../src/movie-service'), {
    cmd: [ "get.get" ],
    entrypoint: ["/lambda-entrypoint.sh"],
    }),
    environment: {
            DYNAMODB_TABLE: this.table.tableName
    },
});

以下は、上記とほぼ同じコードを使って cdk deploy -vした際の出力の一部です。最初の行でImagePublishingRole(にて作成されたロール)にassume roleしてから、docker loginでECRレジストリにログインし、コンテナイメージをビルドし、タグ付けしプッシュ、ということが行われていることがわかるかと思います。

Assuming role 'arn:aws:iam::123456789012:role/cdk-hnb659fds-image-publishing-role-123456789012-us-east-1'.
[75%] check: Check 123456789012.dkr.ecr.us-east-1.amazonaws.com/cdk-hnb659fds-container-assets-123456789012-us-east-1:82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072
Call failed: describeImages({"repositoryName":"cdk-hnb659fds-container-assets-123456789012-us-east-1","imageIds":[{"imageTag":"82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072"}]}) => The image with imageId {imageDigest:'null', imageTag:'82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072'} does not exist within the repository with name 'cdk-hnb659fds-container-assets-123456789012-us-east-1' in the registry with id '123456789012' (code=ImageNotFoundException)
[75%] debug: docker login --username AWS --password-stdin https://123456789012.dkr.ecr.us-east-1.amazonaws.com
[75%] debug: docker inspect cdkasset-82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072
[75%] build: Building Docker image at /Users/kazue.masaki/hoge/cdk.out/asset.82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072
[75%] debug: docker build --tag cdkasset-82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072 .
#1 [internal] load build definition from Dockerfile
#1 sha256:f5f55ee88cf1c382b262f8e025d176d52d1b777c3d1930e1c8d3d5e203b90630
#1 transferring dockerfile: 331B done
#1 DONE 0.0s

#2 [internal] load .dockerignore
#2 sha256:5c5f981b13a8392382ffe9c1ddd1e354f5922dc3974bab6702b3192231cade5d
#2 transferring context: 2B done
#2 DONE 0.0s

#3 [internal] load metadata for public.ecr.aws/lambda/nodejs:12
#3 sha256:dffa778413db7fcfd0635689e67ed302cfc55e499bb3ec3f18f4be6f2c6d3375
#3 DONE 4.0s

#5 [internal] load build context
#5 sha256:f9b9c5c3c155c2d102874b2358df3cc2c54577508df465af93efbe31b9290404
#5 transferring context: 3.10kB 0.0s done
#5 DONE 0.0s

#4 [1/3] FROM public.ecr.aws/lambda/nodejs:12@sha256:0eb5179d02a6d6e095c048f752dd33a75263cf4136ced8787a83737228dc2354
#4 sha256:2d07cc1648b3d1bb43e4ec6303cc94d300c65d66aa31f384dcca620041a2bfd8
#4 resolve public.ecr.aws/lambda/nodejs:12@sha256:0eb5179d02a6d6e095c048f752dd33a75263cf4136ced8787a83737228dc2354 done
#4 sha256:0eb5179d02a6d6e095c048f752dd33a75263cf4136ced8787a83737228dc2354 772B / 772B done
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 0B / 102.54MB 0.1s
#4 sha256:0de26daf261e48dbc00f77c5fca094fefb2688e51d9dfc254e62da61caabf73a 0B / 81.51kB 0.1s
#4 sha256:c8880dcd4d4b595529cb48f73e39f9bda467a3fde8314e175bbc20db3e70d5e8 1.58kB / 1.58kB done
#4 sha256:42e1ffbbf5228b97e5bcbae3ac164b665f015c0ad1a6beb618abb37a0d827172 3.12kB / 3.12kB done
#4 sha256:0bd3404121966a38ff947eb711f2c1bd8ce9f3313ec50467acce079b545c8fd8 0B / 417B 0.1s
#4 sha256:0de26daf261e48dbc00f77c5fca094fefb2688e51d9dfc254e62da61caabf73a 81.51kB / 81.51kB 0.9s done
#4 sha256:ba3917f372fcb5f0400ef2e67fa308141dea840c07dd7cc3e3e4ef691d1b2a0b 0B / 2.11MB 1.0s
#4 sha256:0bd3404121966a38ff947eb711f2c1bd8ce9f3313ec50467acce079b545c8fd8 417B / 417B 1.1s done
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 0B / 36.29MB 1.3s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 5.24MB / 102.54MB 2.3s
#4 sha256:ba3917f372fcb5f0400ef2e67fa308141dea840c07dd7cc3e3e4ef691d1b2a0b 1.05MB / 2.11MB 2.4s
#4 sha256:ba3917f372fcb5f0400ef2e67fa308141dea840c07dd7cc3e3e4ef691d1b2a0b 2.11MB / 2.11MB 2.4s done
#4 sha256:446fafb864797ce9732c47ebba7d5a15244e49a309ef70767e91b29c80d46214 0B / 9.26MB 2.5s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 11.53MB / 102.54MB 2.6s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 3.15MB / 36.29MB 2.9s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 17.83MB / 102.54MB 3.1s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 9.44MB / 36.29MB 3.1s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 13.63MB / 36.29MB 3.3s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 23.07MB / 102.54MB 3.5s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 15.73MB / 36.29MB 3.6s
#4 sha256:446fafb864797ce9732c47ebba7d5a15244e49a309ef70767e91b29c80d46214 1.05MB / 9.26MB 3.6s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 28.31MB / 102.54MB 3.7s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 17.83MB / 36.29MB 3.7s
#4 sha256:446fafb864797ce9732c47ebba7d5a15244e49a309ef70767e91b29c80d46214 2.10MB / 9.26MB 3.7s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 22.02MB / 36.29MB 3.9s
#4 sha256:446fafb864797ce9732c47ebba7d5a15244e49a309ef70767e91b29c80d46214 5.24MB / 9.26MB 3.9s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 27.26MB / 36.29MB 4.2s
#4 sha256:446fafb864797ce9732c47ebba7d5a15244e49a309ef70767e91b29c80d46214 9.26MB / 9.26MB 4.2s done
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 33.55MB / 102.54MB 4.3s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 34.60MB / 36.29MB 4.5s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 41.94MB / 102.54MB 4.7s
#4 sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 36.29MB / 36.29MB 4.7s done
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 47.19MB / 102.54MB 5.2s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 53.48MB / 102.54MB 5.6s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 61.87MB / 102.54MB 5.9s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 69.21MB / 102.54MB 6.2s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 74.45MB / 102.54MB 6.5s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 80.74MB / 102.54MB 6.8s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 85.98MB / 102.54MB 7.1s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 92.27MB / 102.54MB 7.4s
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 100.66MB / 102.54MB 7.9s
#4 extracting sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9
#4 sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 102.54MB / 102.54MB 8.0s done
#4 extracting sha256:d4bbef92ccd41b6a71f82c32c66da512dff174187c784d4322aabe5d47244bb9 2.9s done
#4 extracting sha256:0de26daf261e48dbc00f77c5fca094fefb2688e51d9dfc254e62da61caabf73a 0.0s done
#4 extracting sha256:0bd3404121966a38ff947eb711f2c1bd8ce9f3313ec50467acce079b545c8fd8 done
#4 extracting sha256:ba3917f372fcb5f0400ef2e67fa308141dea840c07dd7cc3e3e4ef691d1b2a0b
#4 extracting sha256:ba3917f372fcb5f0400ef2e67fa308141dea840c07dd7cc3e3e4ef691d1b2a0b 0.1s done
#4 extracting sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 0.1s
#4 extracting sha256:14389da6c37f56d27f8498a2d48b3a27748bd1ffac7eb35cb7402a2da8f4a0b9 1.0s done
#4 extracting sha256:446fafb864797ce9732c47ebba7d5a15244e49a309ef70767e91b29c80d46214
#4 extracting sha256:446fafb864797ce9732c47ebba7d5a15244e49a309ef70767e91b29c80d46214 0.4s done
#4 DONE 13.1s

#6 [2/3] COPY list.js get.js package.json package-lock.json /var/task/
#6 sha256:0b050ecc3fb54d27aa842df1719d37d753529c6ab8fcf1c4d5d56eebea0e37c7
#6 DONE 0.1s

#7 [3/3] RUN npm install
#7 sha256:da30c0a7d837c2c108cd1e37c1d48f998e575ce9a60c364a331ec9e66fb3536e
#7 1.253 npm WARN movie-service@1.0.0 No repository field.
#7 1.253 
#7 1.254 added 2 packages from 9 contributors and audited 2 packages in 0.906s
#7 1.255 found 0 vulnerabilities
#7 1.255 
#7 DONE 1.3s

#8 exporting to image
#8 sha256:e8c613e07b0b7ff33893b694f7759a10d42e180f2b4dc349fb57dc6b71dcab00
#8 exporting layers 0.0s done
#8 writing image sha256:4bae9f552ad2d3320f5dc03cf009f229dea2565311c447b0bd4e3e100edc64a4 done
#8 naming to docker.io/library/cdkasset-82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072 done
#8 DONE 0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[75%] upload: Push 123456789012.dkr.ecr.us-east-1.amazonaws.com/cdk-hnb659fds-container-assets-123456789012-us-east-1:82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072
[75%] debug: docker tag cdkasset-82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072 123456789012.dkr.ecr.us-east-1.amazonaws.com/cdk-hnb659fds-container-assets-123456789012-us-east-1:82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072
[75%] debug: docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/cdk-hnb659fds-container-assets-123456789012-us-east-1:82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072
The push refers to repository [123456789012.dkr.ecr.us-east-1.amazonaws.com/cdk-hnb659fds-container-assets-123456789012-us-east-1]
7a14e98d7d59: Preparing
4c85dd7eb7a5: Preparing
72a490fbf8cd: Preparing
083dc41d458c: Preparing
1a913cc9bc55: Preparing
050f1cc9348a: Preparing
20455b74816f: Preparing
6339f48a17fc: Preparing
050f1cc9348a: Waiting
20455b74816f: Waiting
6339f48a17fc: Waiting
4c85dd7eb7a5: Pushed
7a14e98d7d59: Pushed
1a913cc9bc55: Pushed
72a490fbf8cd: Pushed
050f1cc9348a: Pushed
20455b74816f: Pushed
083dc41d458c: Pushed
6339f48a17fc: Pushed
82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072: digest: sha256:9c0ffd5e70d8d67e865f645322d44d24e24daed35d681b6e5478c5e544f22910 size: 1997
[100%] success: Published 82f1f2c7b5ab440f870dedc0ba5775be95a3d13321b872e92656245d32791072:current_account-us-east-1

FilePublishingRole

前述のImagePublishingRoleと似ていますが、こちらはS3バケットにアセットをアップロードするときに使われるロールです。コンテナイメージからではなくソースコードからLambda関数を作成する際などに使われます。(対象のS3バケットもcdk bootstrapによって作成されます。)

割愛しますが、cdk deploy -vコマンドを打つとImagePublishingRoleと同様このロールにassume roleしてからオブジェクトをS3にアップロードしているのがわかります。

5つのロールまとめ

5roles

クロスアカウントデプロイメントができる

cdk bootstrapコマンドに--trustオプションを追加して値にAWSアカウントIDを指定します。するとそのアカウントからのデプロイが可能になります。具体的には、前項で説明した各種ロールの信頼関係ポリシーに指定したアカウントIDが追加されるので、そのアカウントからassume roleできるようになってデプロイできるようになります。CDK PipelineでクロスアカウントのCI/CDパイプラインを作る際に活用できます。

また--trust-for-lookupというオプションもあります。こちらは前項のLookupRoleの信頼関係ポリシーのみ変更するオプションです。つまりこのオプションに指定されたアカウントからは、当アカウントの既存リソースについての参照はできるけれども、デプロイはできないという状態を作れます。

改良が続けられている

modern templateは改良が続けられていて、作成されるリソースが変わったりそのプロパティが変わったりしています。更新がかかる度にそれは新しいversionとなります。modern templateの中にAWS::SSM::Parameterつまりパラメーターストアがありましたが、このパラメーターストアには紐づくbootstrap templateのバージョン番号が格納されています。cdk appのdeploy時にこのパラメーターストアの値をチェックして、bootstrapのバージョンがそのappの要件を満たしているかチェックします。

リソース命名方法をコントロールできる

bootstrap templateを介して作成される各AWSリソースには名前にランダムな文字列が付きます。legacyではこの文字列をコントロールすることはできません。が、modernではできます。--qualifierオプションをつけてcdk bootstrapコマンドを実行します。先程ご紹介した modern templateにQualifierというパラメーターがあり、ここに--qualifierオプション値が渡されてスタックが作成されます。そしてこのQualifierパラメーターは各bootstrapリソースの名前を決定する際に使われています。例えば以下はS3バケットを作成する部分です。

  StagingBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName:
        Fn::If:
          - HasCustomFileAssetsBucketName
          - Fn::Sub: ${FileAssetsBucketName}
          - Fn::Sub: cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region}

なおQualifierデフォルト値はhnb659fdsなので、これが名前に含まれるリソースが作成されます。 hnb659fds

この機能によって、同一環境(アカウントxリージョン)に複数個のbootstrapリソースを置きたい場合に、リソース名重複エラーを避けることができます。まあそもそも同一環境に複数個のbootstrapリソースを置きたい場合はあまりなさそうな気もしますが… コンプライアンス要件的にcdk app毎に使うbootstrapリソースも完全に分けたいとか、bootstrapリソースも色々カスタマイズできるので個々のapp要件に併せてカスタマイズしたものを使いたいとかですかね。

なおcdk app側で--qualifierオプションで名前を変えたbootstrapリソース群を使うには、Stack Constructのsynthesizer propqualifierを指定します。

new MyStack(this, 'MyStack', {
  synthesizer: new DefaultStackSynthesizer({
    qualifier: 'MYQUALIFIER',
  }),
});

もしくはcdk.json内で@aws-cdk/core:bootstrapQualifierを使います。

{
  "app": "...",
  "context": {
    "@aws-cdk/core:bootstrapQualifier": "MYQUALIFIER"
  }
}

バケット暗号化のキーが選べる

bootstrapで作成されるS3バケットですが、legacyだとAWS管理のKMSキーによってオブジェクトがサーバーサイド暗号化されます。modernだと以下3つの方法を選ぶことができます。

  • AWS管理のKMSキー(legacyと同じ)
  • 作成済みKMSキーを使う
  • bootstrap内でKMSキーを作ってそれを使う

なおデフォルトはAWS管理のKMSキーです。

ガッツリカスタマイズもできる

cdk bootstrap --template my-bootstrap-template.yaml

のように--templateオプションで任意のCFnテンプレートを指定すれば、ガッツリbootstrapリソース達のカスタマイズを行なうこともできます。ただしこちらに書かれていることは守る必要があります。

参考情報