🥰

cdk bootstrap(CDKToolkit)を使いこなす

2022/12/09に公開

cdk bootstrap一度はしたことあるとは思います。
かなりありがたい存在なのですが、どう活用するかで悩むことも多いのではないかなと思い書いてみます。

cdk bootstrapとsynthesizerは何者か

cdk bootstrap

公式ドキュメント荷しっかりと記載がされています。

Bootstrapping is the process of provisioning resources for the AWS CDK before you can deploy AWS CDK apps into an AWS environment. (An AWS environment is a combination of an AWS account and Region).

つまり、CDKアプリケーションをデプロイするにあたって必要なリソースを作る呪文がcdk bootstrapというコマンドです。この呪文を実行するとCDKToolkitという名前のCloudFormation Stackが作成されます。

ちなみにデフォルトで作られるリソースは(2022/12/7時点で)以下の通りです。(ポリシーやKMSは除外)

  • DeploymentActionRole
  • LookupRole
  • CloudFormationExecutionRole
  • ImagePublishingRole
  • FilePublishingRole
  • ContainerAssetsRepository
  • StagingBucket

StackSynthesizer

StackSynthesizerについても、公式ドキュメントに記載がされています。

Your AWS CDK app needs to know about the bootstrapping resources available to it in order to successfully synthesize a stack that can be deployed. The stack synthesizer is an AWS CDK class that controls how the stack's template is synthesized. This includes how it uses bootstrapping resources (for example, how it refers to assets stored in the bootstrap bucket).

つまり、CDKアプリケーション内に定義したStackを

  • どのようにCloudFormationsのテンプレートを生成するか
  • どのようにデプロイするか
  • どのように実環境との差分を確認するか
  • 生成されたAssetをどの場所に格納するか

これらを定義したもの = StackSynthesizerと捉えて問題ないでしょう。

ちなみに、cdk synthを実行した際に、cdk.out/manifest.jsonにも出力されています。

    "pipeline-demo-kawagoe": {
      "type": "aws:cloudformation:stack",
      "environment": "aws://unknown-account/ap-northeast-1",
      "properties": {
        "templateFile": "pipeline-demo-kawagoe.template.json",
        "validateOnSynth": false,
        "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-ap-northeast-1",
        "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-ap-northeast-1",
        "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-ap-northeast-1/3e3ee14f343627d4cac341247c84566d016772d39810f9eb3d7eb1c2a4d8a892.json",
        "requiresBootstrapStackVersion": 6,
        "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
        "additionalDependencies": [
          "pipeline-demo-kawagoe.assets"
        ],
        "lookupRole": {
          "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-ap-northeast-1",
          "requiresBootstrapStackVersion": 8,
          "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
        }
      },

公式ドキュメントに戻ります

The AWS CDK's built-in stack synthesizers is called DefaultStackSynthesizer. It includes capabilities for cross-account deployments and CDK Pipelines deployments.

デフォルトで提供しているものはDefaultStackSynthesizerと呼びます。これがbootstrapの正体です。

DefaultStackSynthesizerの挙動をデバッグログから追って
bootstrapで作成されるリソースごとにざっくりとまとめてみるとこうなります。

  • DeploymentActionRole
    • Deployに関わる挙動を実行する。
      • CreateChangeset/ExecuteChangesetなど
      • CloudFormationExecutionRoleをCloudFormationStackにpassRoleする
    • CDKToolkitの更新途中有無を確認する
  • LookupRole
    • Lookupメソッド(Vpc.fromLookup()など)を実行する
    • cdk diffを実行するためにDescribeStackを実行する
    • IAM Statement Changeを作成するためにDescribeStackを実行する
  • CloudFormationExecutionRole
    • CloudFormationのStackにpassRoleされる
    • デフォルトはAdministrator権限
  • ImagePublishingRole
    • ContainerAssetsRepositoryにコンテナイメージをpushする
  • FilePublishingRole
    • StagingBucketにいろんなファイルをpushする
  • ContainerAssetsRepository
  • StagingBucket

詳しい挙動はクラスメソッド様のブログにも掲載されています。こちら
非常に参考になります。

なお、cdk deployを実行する際に、DeploymentActionRole/LookupRole/ImagePublishingRole/FilePublishingRoleにAssumeRoleを行おうとしますが、
失敗した場合、「とりあえず続行するで。」と言って処理が継続します。

黄色の字で出てくるこれです。

current credentials could not be used to assume 'arn:aws:iam::012345678901:role/cdk-hnb659fds-deploy-role-012345678901-ap-northeast-1', but are for the right account. Proceeding anyway.

つまり実際はAssume Roleできなくても該当処理が実行できれば処理は継続します。

また、AWSがデフォルトで用意してくれているStackSynthesizerがいくつか存在します

BootstraplessSynthesizer

bootstraplessなsynthesizerです。

今まで登場が確認できたのは下記です。

  • cdkPipelineでマルチリージョンへのデプロイをサポートするcross-region-support-stack
  • EdgeFunctionを定義したときにus-east-1に作成されるStack

どうしても特定のリソースを別リージョンで作成する必要がある場合に使われるsynthesizerのようです。

CliCredentialsStackSynthesizer

名前の通り、CDKToolkitのRoleを使用せず、cdkコマンドを実行した際のクレデンシャルが持つ権限のみでDeployやdiffを行うsynthesizerです

LegacyStackSynthesizer

レガシーです。
使い道は不明です。

NestedStackSynthesizer

NestedStackを作成したときに利用されているようです。

我々はどうbootstrapを活用するのか

以下のようなケースにはbootstrapをデフォルトのまま利用することができません。

  1. 開発者にAdministrator権限は渡せない
  2. チームごとに異なる権限でデプロイを行わせたい

これらの場合にどう活用するのが良いのか考えてみます。

1. CDKBootsrapをカスタマイズする

--cloudformation-execution-policiesで任意のマネージドポリシーを付与する

このオプションでマネージドポリシー名を渡すことで任意のポリシーを渡すことができます。

PowerUserのポリシーを渡す時は以下のようにすれば問題なしです。

$ cdk bootstrap --cloudformation-execution-policies arn:aws:iam::aws:policy/PowerUserAccess

Permission Boundaryを活用し、CloudFormationExecutionRoleを制限する

AWS公式ブログでも紹介されているやり方です。

このブログでは、pythonのスクリプトが提供されていますので、非常に簡単にPermission Boundaryを活用できます。

bootstrapのテンプレートを直接変更する

cdk bootstrapのテンプレートは--show-templateのオプションをつけることで出力できます。

cdk bootstrap --show-template

また、デプロイする際には--templateオプションで、yamlファイルを指定することで変更したtemplateでcdk bootstrapを実行できます。

cdk bootstrap --template xxxxxxx.yaml

--show-templateで出力されるものはCloudFormationのテンプレートなので、
yamlを好きなようにいじってテンプレートに渡しましょう。

この際、絶対消してはいけないリソースも存在するので、必ず公式ドキュメント bootstrapping contractを参照してください。

2. 識別子をチームごとに分けて、チームごとにcdk bootstrapする

例えば、

  • アプリケーションチームにはネットワーク操作はさせない
  • インフラチームではVpcの作成も行いたい
    といったチームごとに異なる2つの権限が必要になる場合があります。

その場合、cdk bootstrapには--qualifierというオプションをつけてそれぞれの名前を変更することができます。デフォルトは一度は目にしたことのある、"hnb659fds"という値ですが、この数字に特に意味はありません。チームごとに好きな値に変えてしまいましょう。

$ cdk bootstrap --qualifier devops --template {チームごとのテンプレート}.yaml --toolkit-stack-name {お好みのStack名}

こうすることでチームごとに別々の権限でCDKのデプロイまでを実行することができるようになります。

(2023/01/07追記)
--toolkit-stack-nameのオプションをつけないとデフォルト(CDKToolkit)のスタックが更新される点には注意が必要です。(検証環境で自身でやらかしました)

また、チームごとにqualifierを割り当てた場合、Stackコンストラクを生成する際に、synthesizerを指定する必要がある点には注意が必要です。

new Stack(app, 'stack', {
  synthesizer: new DefaultStackSynthesizer(
    {
      qualifier: "チームごとのqualifier"
    }
  )
})

まとめ

デフォルトのままだとAdministrator権限だし・・・チームごとに権限分けれないし・・・で何かしら組織のポリシーに引っかかりそうなbootstrapですが、1と2を組み合わせて工夫することで、組織ごとに適したポリシーでCDKのありがたい機能を活用することができるではないでしょうか。

参考

Discussion