CDKv2 で AssumeRole を使い安全に CD する方法

## 課題：CDK v2 で組んだ IaC を安全に CD したい

AWS のサーバーレスリソースを使ったウェブサイトやウェブアプリの構築にトライしています。
インフラは [CDK v2](https://docs.aws.amazon.com/cdk/v2/guide/home.html) を使った IaC（Infrastructure as Code）にしたので、さらに Git にプッシュしたら自動的にデプロイされるよう CD（Continuous Delivery, 継続的デリバリー）を設定したいと考えました。

CD するには CD サービスに対し私の AWS アカウントの操作権を委ねる必要がありますが、できるだけリスクを下げて安全に行いたいものです。
これには AssumeRole を使うベストプラクティスがありますが、 CDK v2 では少し気を付ける点がありましたので、ここでまとめます。

## TL;DR （先に結論）

* CDK v2 の bootstrap でデプロイ時に必要なロールが自動的に作られる。
* このロールに AssumeRole できるユーザーを作り、そのユーザーの Access Key ID と Secret Access Key を CI/CD に登録する

## CD の安全性を高める一般的なベストプラクティス

CD では、 CD サービスに対して AWS アカウントの操作を委ねることになるため、クレデンシャルの漏洩などのリスクがあり得ます。漏洩したクレデンシャルに強い権限をつけないようにして、リスクを下げて安全に行いたいものです。

この課題には定石の解決策があり、 Classmethod さんの記事を参考にしました。
なお、記事中は開発者に与える権限を制限するという文脈になっていますが、 CD でも同じ考えで問題ありません。

https://dev.classmethod.jp/articles/assume-role-deploy-iam-user-and-role/

https://www.youtube.com/watch?v=LbZj68v4k8I

私の理解でまとめると、まず、CloudFormation サービスに私のアカウントでリソースを操作することを許可するための「ロール１」と、CloudFormation の起動のみを行える「ロール２」を作ります。
そして、ロール１を利用できるがリソースを直接作れない「ユーザー」を作り、そのユーザーのクレデンシャルを CI サービスに登録します。

### CDK v2 ではそのままでは実行できない

さて、Classmethod さんの記事などを参考に、ロール１，ロール２、ユーザーを作り、 `cdk diff` などのコマンドを使って試してみますと、次のような警告が出るようになりました。

```
current credentials could not be used to assume 'arn:aws:iam::123456789012:role/cdk-xxxxxxxxx-aaaa-bbbbbbb-role-123456789012-yyyyyyyy', but are for the right account. Proceeding anyway.
```

また、 `cdk diff` では GetTemplate できないとか、 `cdk deploy` の場合はテンプレートを S3 に転送する部分あたりで Access Denied エラーになってしまうなど、いろいろな問題がでて実行できません。

権限を追加していけばよいのでしょうが、本当にそれが正解なのでしょうか・・・。
CDK はベストプラクティスの集合体でもあるので、もっとスマートな解決策を用意してくれているのでは・・・？？

## CDK v2 は自動的にロールを用意してくれる

いろいろ調べた結果、 Stack Overflow に正解がありました！

https://stackoverflow.com/a/61102280

CDK v2 ではデプロイ作業に必要なロールを bootstrap コマンド実行時に自動的に作ってくれるため、自分で作る必要はないようです！

AWS コンソールの IAM サービス画面から調べてみると、下記のロールがありました。実際には `xxxxxxxxx` はランダム文字列、 `yyyyyyyy` はリージョンが入ります。

```
cdk-xxxxxxxxx-cfn-exec-role-123456789012-yyyyyyyy
cdk-xxxxxxxxx-deploy-role-123456789012-yyyyyyyy
cdk-xxxxxxxxx-file-publishing-role-123456789012-yyyyyyyy
cdk-xxxxxxxxx-image-publishing-role-123456789012-yyyyyyyy
cdk-xxxxxxxxx-lookup-role-123456789012-yyyyyyyy
```

５つもロールがあるのは、操作によってロールを使い分けるためのようです。
cdk コマンドは、テンプレートの差分を調べたり、テンプレートを S3 に送信したり、ウェブサイトコンテンツの送信で BucketDeployment を使っている場合など、 CloudFormation 起動以外のいろいろな操作を行ってくれます。
これらの操作が使う権限を調べて自力でロールを作るのも大変ですから、自動作成されたロールをそのまま使うのが、 CDK v2 を使う上での正しい作法といえそうです。

### bootstrap のバージョンに注意

なお、 [bootstrap は何度かバージョンアップされています](https://github.com/aws/aws-cdk/commits/0ef4bb4bf493a7e3b72b518841f676e91d014ba9/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml)。作られるロールもアップデートされてきているようですので、上記のようなロールが見当たらない場合は bootstrap をアップグレードしてみるとよいかもしれません。

なお、自分の bootstrap のバージョンは、 AWS コンソールから **AWS Systems Manager / パラメータストア** を開き、 `/cdk-bootstrap/xxxxxxxxx/version` という名前のパラメータで確認できるようです。

記事執筆時点（2022年8月15日）で最新版の CDK 2.37.1 (build f15dee0) で bootstrap したものでは `14` という値が入っていました。

## 結論：CD 用のユーザーを作るだけでよい

結論としては、CDK v2 が作ったロールを利用するユーザーを作り、 CD はそのユーザーで行うように設定すれば OK でした。

具体的には、まず CDK v2 で bootstrap を行います。

```
cdk bootstrap
```

作られたロールへの AssumeRole を許可する下記のようなポリシーを持つユーザーを作ります。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "arn:aws:iam::*:role/cdk-*"
            ]
        }
    ]
}
```

その後、 AWS コンソールの IAM サービスを開き、そのユーザーのアクセスキーを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/957245/237f30a6-3fc0-fbc5-2108-d6ea492d5f79.png)

取得したアクセスキーを CD サービスに設定します。

私の場合、 Git ホスティングに [GitLab.com](https://gitlab.com/) を使っているので、標準の [GitLab CI/CD](https://docs.gitlab.com/ee/ci/) 機能を使います。GitLab の Settings > CI/CD > Variables より、 AWS_ACCESS_KEY_ID と AWS_SECRET_ACCESS_KEY という環境変数を設定しておけば CD スクリプト実行時に使われます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/957245/135d940a-0884-2a29-f4d5-ca6b98ec8317.png)

なお、CI スクリプトに記述する `cdk deploy` コマンドに対して何か特別な設定は不要です。このアクセスキーで認識されるユーザーを用いて、自動的に AssumeRole して各種操作を実行してくれるようです。
（なお、私はリージョンは CDK コードで設定しています。必要に応じて変数に追加ください）

おわり。

### 補足１：CDK のプロンプトをOFFにするには

`cdk deploy` コマンドの実行中、特に IAM 関連の操作が必要になる場合などに、実行してよいか `y` の入力を求めるプロンプトが出てくることがあります。 CD ではプロンプトが出てくると実行が止まってしまうので困ります。プロンプトを抑制するためには `--require-approval never` という自動承認するオプションを付ければ OK です。

```sh
cdk deploy --require-approval never
```

### 補足２：bootstrap自体をどうするのか

さて、 `cdk bootstrap` コマンドを実行できる前提でまとめましたが、よく考えると bootstrap はどういう権限で行えばいいでしょう？
私はルートユーザーで実行してしまいました・・・が、本当は良くないですね。
Classmethod さんの説明にある一般的な定石に従うのが良さそうです。