CDKのBootstrapをカスタマイズしてみた

2024.12.25
CDKのBootstrapをカスタマイズしてみた
この記事をシェアする

はじめに

本記事は、クラウドビルダーズ株式会社のアドベントカレンダー2024の25日目の記事となります!

こんにちは、CloudBuildersのsugawaraです。

以前の記事では、cdk bootstrapコマンドを実行することで自動デプロイされるAWSリソースを一つずつみていきました。コマンド一つで必要なリソースが作成され、CDKのデプロイが実行できる環境にできました。

今回はデフォルトの実行環境をブートストラップするのではなく、好きなようにカスタマイズしていきたいと思います!カスタマイズには、以下の2つの方法があります。

  • CDK CLIを利用した簡単なカスタマイズ
  • Bootstrapテンプレートを利用した詳細なカスタマイズ

それぞれ実際に利用してみて、簡単に紹介していきます。
なお、デフォルトで作成されるリソースについて確認したい方は、下記のリンクをご参照ください。

CDK CLI

名前の通り、CDKのCLIコマンド実行時にカスタマイズする方法です。cdk bootstrapコマンドにオプションを指定することで、簡単にカスタマイズすることができます。今回はよく使われそうなものをいくつかやってみます。

スタック名の変更

cdk bootstrapコマンドを実行すると、デフォルトではCDKToolkitという名前のスタックが作成されます。しかし、オプション–toolkit-stack-nameをつけることで、スタック名を変更することが可能です。

$ cdk bootstrap aws://XXXXXXXXXXXX/ap-northeast-1 \
  --toolkit-stack-name CustomCDKToolkit

上記コマンドを実行することで、下記のようにスタックが作成されます。

S3バケット名の変更

ブートストラップ時に自動作成されるS3バケットは、CloudFormationテンプレートやLambdaのソースコードなどを格納します。このS3バケットの名前は、デフォルトで全アカウント共通のcdk-{Qualifier}-assets-{Account-ID}-{Region}という命名規則のもと作成されます。
例)cdk-hnb659fds-assets-XXXXXXXXXXXX-ap-northeast-1

先日CDKの脆弱性が発表されたため、デフォルトの名前ではなく、プロジェクトごとに指定したほうがセキュリティ上良いかと思われます。オプション–bootstrap-bucket-nameをつけることで、S3バケット名を変更することが可能です。

$ cdk bootstrap aws://XXXXXXXXXXXX/ap-northeast-1 \
  --bootstrap-bucket-name custom-s3-bucket-20241223

上記コマンドを実行することで、下記のようにリソースが作成されます。

リソースへのタグ付け

CDKのブートストラップ時に作成されるスタックやIAMロールなどにタグ付けが可能です。今度はCustomCDKToolkitスタックとそのリソースにタグ付けしていきます。

$ cdk bootstrap aws://XXXXXXXXXXXX/ap-northeast-1 \
  --toolkit-stack-name CustomCDKToolkit \
  --bootstrap-bucket-name custom-s3-bucket-20241223 \
  --tags SampleKey=SampleValue --profile local

CustomCDKToolkitスタックには下記のようなタグが追加されました。

また、S3バケットにもタグが追加されているのがわかります。

他にもスタックの削除保護やKMSによる暗号化などもCDK CLIで設定が可能です。また、次節のブートストラップテンプレートの変更でもCDK CLIは利用します。詳しくは、下記の公式ドキュメントをご参照ください。
https://docs.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-bootstrap.html

こちらはコマンド実行時にオプションで調整するため、設定の自由度やカスタマイズ性は低いです。ユースケースは特定の箇所のみの少しのカスタマイズが必要な場合になるかと思います。

Bootstrap Template

次に、cdk bootstrapコマンドを実行したときに利用されるBootstrapテンプレートをカスタマイズする方法を紹介します。

デフォルトテンプレート

デフォルトで使用されているテンプレートを確認するには、下記のコマンドのどちらかを実行します。

# ターミナルに出力
$ cdk bootstrap --show-template

# bootstrap-template.ymlに出力
$ cdk bootstrap --show-template > bootstrap-template.yml

実行すると、下記のような出力になります。
※とても長いため詳細は必要に応じてご参照ください。

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"
  InputPermissionsBoundary:
    Description: Whether or not to use either the CDK supplied or custom permissions boundary
    Default: ""
    Type: String
  UseExamplePermissionsBoundary:
    Default: "false"
    AllowedValues:
      - "true"
      - "false"
    Type: String
  BootstrapVariant:
    Type: String
    Default: "AWS CDK: Default Resources"
    Description: Describe the provenance of the resources in this bootstrap stack. Change this when you customize the template. To prevent accidents, the CDK CLI will not overwrite bootstrap stacks with a different variant.
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
  ShouldCreatePermissionsBoundary:
    Fn::Equals:
      - "true"
      - Ref: UseExamplePermissionsBoundary
  PermissionsBoundarySet:
    Fn::Not:
      - Fn::Equals:
          - ""
          - Ref: InputPermissionsBoundary
  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
              - kms:TagResource
              - kms:UntagResource
            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
      LifecycleConfiguration:
        Rules:
          - Id: CleanupOldVersions
            Status: Enabled
            NoncurrentVersionExpiration:
              NoncurrentDays: 30
          - Id: AbortIncompleteMultipartUploads
            Status: Enabled
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 1
    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:
      ImageTagMutability: IMMUTABLE
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 1,
                "description": "Untagged images should not exist, but expire any older than one year",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "sinceImagePushed",
                  "countUnit": "days",
                  "countNumber": 365
                },
                "action": { "type": "expire" }
              }
            ]
          }
      RepositoryName:
        Fn::If:
          - HasCustomContainerAssetsRepositoryName
          - Fn::Sub: ${ContainerAssetsRepositoryName}
          - Fn::Sub: cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region}
      RepositoryPolicyText:
        Version: "2012-10-17"
        Statement:
          - Sid: LambdaECRImageRetrievalPolicy
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action:
              - ecr:BatchGetImage
              - ecr:GetDownloadUrlForLayer
            Condition:
              StringLike:
                aws:sourceArn:
                  Fn::Sub: arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*
  FilePublishingRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:TagSession
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
          - 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:TagSession
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
          - 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:TagSession
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
          - 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*
              - s3:GetEncryptionConfiguration
              - s3:List*
              - s3:DeleteObject*
              - s3:PutObject*
              - s3:Abort*
            Resource:
              - Fn::Sub: ${StagingBucket.Arn}
              - Fn::Sub: ${StagingBucket.Arn}/*
            Condition:
              StringEquals:
                aws:ResourceAccount:
                  - Fn::Sub: ${AWS::AccountId}
            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:TagSession
            Effect: Allow
            Principal:
              AWS:
                Ref: AWS::AccountId
          - 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
                  - cloudformation:RollbackStack
                  - cloudformation:ContinueUpdateRollback
                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
                  - cloudformation:GetTemplateSummary
                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
                  - ssm:GetParameters
                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}
      PermissionsBoundary:
        Fn::If:
          - PermissionsBoundarySet
          - Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/${InputPermissionsBoundary}
          - Ref: AWS::NoValue
  CdkBoostrapPermissionsBoundaryPolicy:
    Condition: ShouldCreatePermissionsBoundary
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Statement:
          - Sid: ExplicitAllowAll
            Action:
              - "*"
            Effect: Allow
            Resource: "*"
          - Sid: DenyAccessIfRequiredPermBoundaryIsNotBeingApplied
            Action:
              - iam:CreateUser
              - iam:CreateRole
              - iam:PutRolePermissionsBoundary
              - iam:PutUserPermissionsBoundary
            Condition:
              StringNotEquals:
                iam:PermissionsBoundary:
                  Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region}
            Effect: Deny
            Resource: "*"
          - Sid: DenyPermBoundaryIAMPolicyAlteration
            Action:
              - iam:CreatePolicyVersion
              - iam:DeletePolicy
              - iam:DeletePolicyVersion
              - iam:SetDefaultPolicyVersion
            Effect: Deny
            Resource:
              Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region}
          - Sid: DenyRemovalOfPermBoundaryFromAnyUserOrRole
            Action:
              - iam:DeleteUserPermissionsBoundary
              - iam:DeleteRolePermissionsBoundary
            Effect: Deny
            Resource: "*"
        Version: "2012-10-17"
      Description: Bootstrap Permission Boundary
      ManagedPolicyName:
        Fn::Sub: cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region}
      Path: /
  CdkBootstrapVersion:
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Name:
        Fn::Sub: /cdk-bootstrap/${Qualifier}/version
      Value: "25"
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

カスタムテンプレート

さきほど出力したデフォルトのブートストラップテンプレートをベースに、custom-bootstrap-template.ymlという名前のカスタムテンプレートを作成します。

変更点は、自動作成されるリソースの命名規則の変更です。具体的には、hnb659fdsという共通のQualifierが利用されていますが、これをcustomに修正します。下記は修正箇所になります。

Description: This stack includes resources needed to deploy AWS CDK apps into this environment
Parameters:
  ...
  Qualifier:
    Description: An identifier to distinguish multiple bootstrap stacks in the same environment
    Default: custom  # デフォルト値 hnb659fds を修正
    Type: String
    AllowedPattern: "[A-Za-z0-9_-]{1,10}"
    ConstraintDescription: Qualifier must be an alphanumeric identifier of at most 10 characters
  ...

下記のコマンドで、カスタマイズしたテンプレートを使用してブートストラップを実行します。

$ cdk bootstrap aws://XXXXXXXXXXXX/ap-northeast-1 \
  --toolkit-stack-name CustomCDKToolkit \
  --template custom-bootstrap-template.yml

上記コマンドを実行することで、下記のようにスタックが作成されます。作成されたIAMなどのリソース物理名がcdk-customから始まっていることが確認できます。今回はQualifierだけを修正しましたが、作成されるIAMロールやポリシーなどもカスタマイズ可能です。

ブートストラップテンプレートをカスタマイズするため、設定の自由度が高いです。IAM関連のカスタマイズが可能なため、セキュリティ要件が厳しい場合などがユースケースになると思われます。

おわりに

今回はCDKの初期設定で実行されるcdk bootstrapコマンドを、CDK CLIとカスタムテンプレートを用いてカスタマイズしてみました。これまでデフォルトのリソース名やテンプレートを利用していましたが、必要に応じて柔軟にそれらを設定していきたいと思います。

2つの方法を紹介しましたが、お手軽に特定のリソース名やタグを変更したい場合にはCDK CLIを利用し、セキュリティ要件が厳しいケースや、IAMロールやポリシーの詳細な調整が必要な場合にはカスタムテンプレートを用いる方法が適しているとか思われます。

ぜひ活用してみてください!

この記事をシェアする
著者:sugawara
元高校英語教員。2023&2024 Japan AWS All Certifications Engineers。IaCやCI/CD、Platform Engineeringに興味あり。