今日から始めるCDK！　～既存リソースをCDK移行する～

# 2025/01/31追記
本記事ではAWSの既存リソースをCDKに変換する手順について記載していますが、いろいろと触ってみた結果、個人的には「既存リソースをCDKに置き換えるより、1から作り直した方が楽」というように考えるようになりました。
というのも、取り込むための手順が複雑なのと、そもそも「よくわからないけどCDKに変換したい」という層に対して本技術は不向きだと感じたからです。
また、自動生成されるCDKコードはかなり煩雑で、理解の妨げになり、さらにドキュメント化という面でもあまり効力を発揮しません（将来的には改善されるかもしれませんが）
なのでこれからCDKを使いたい、移行したいと考える方は、システムを1から作って置き換える、といった方針の方が良いかと思います。

データベースのような1から作るのが難しいリソースにおいてのみ、`cdk import`機能を使って、取り込むのはよさそうです。ただしその場合は、Retain属性を付与してCDKを削除してもデータが保持されるようにしましょう。


# はじめに
クラウドインフラはCDKで管理する時代だ。という波に乗って、自分が主管のAWSアカウントをCDKに移行して展開しよう！　というモチベーションから始めたのですが、「そもそも何言っているのかわからない」とか「既存リソースのCDK移行に関してドキュメントが少ない」という点でとても苦労してきました。

というわけで、本記事はそんな私と同じ道を歩む方への道しるべとして、２つの内容で構成しております。
1. CDKの概念をざっくりと知る
2. 既存のリソースをCDKに移行する

### 注意事項
以下については解説しません
* CDKのコーディング
* ゼロベースでCDKを始める方法



# CDKとは何か

CDKを理解するにあたっては、まず登場人物を整理するところから始めましょう。
※前提：CDKは、ローカルのVSCodeで動作するものとします。

## 単語

1. CDKコード
    * CDKの本体。Typescript, Pythonなど、様々な言語で記述できる
1. テンプレート（ローカル）
    * CDKコードを記述し、`cdk synth`コマンドを実行すると、コードが変換されて生成される
    * yamlファイル
    * `cdk synth`はあくまでローカル上で動作する
1. テンプレート（クラウド上）
    * `cdk deploy` コマンドを実行すると、ローカルのテンプレートがPushされる（同時にスタックに反映される）
    * yamlファイル
    * CDKを触る上では、あまり意識しなくていい
1. スタック
    * Cloudformationが管理するリソースのセット
    * テンプレートを元に作成される、デプロイ可能な最小単位
    * 実態というよりは概念に近い
    * AWS実態リソースを直接変更した場合、このスタックには反映されない
1. AWS実態リソース
    * Lambda, EC2, VPC, IAM, Stepfunctions, etc...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/8ccc6bef-4f0e-1795-f06b-bc9f88477d01.png)

## 主要コマンド

1. `cdk synth`
    * CDKコードを、Cloudformationのテンプレートに変換する
    * コードのエラーなどはここで検知される
    * あくまでローカル上のみで動作する
1. `cdk deploy`
    * CloudformationのテンプレートをAWS上に反映させ、スタックを介して実態リソースを更新する
    * `cdk synth`コマンドが内包されており、CDKが未変換の場合は変換してからPushされる
1. `cdk diff`
    * ローカルのテンプレートと、Cloudformation上のスタック（こうあるだろうと管理されたリソース群）の差異を検出するコマンド
    * ローカルで行ったコーディングが、どのような変化をもたらすかを検出できる
    * ただし、AWSの実態リソースとスタックの実態が異なっていた場合、それは検出できないので注意
1. driftsコマンド
    * 正確には`aws cloudformation describe-stack-resource-drifts --stack-name your-stack-name` のようなコマンドで、CDK関連のコマンドではない
    * AWSの実リソースとスタック間での差異を検出する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/33b96f50-b15e-adca-a26b-81c3496d17ce.png)

基本的なコマンドの順序は、
1. cdk synth : コードをテンプレートに変換
1. cdk diff : 変換されたテンプレートとスタックの差異を表示
1. cdk deploy : テンプレートをスタックに反映

という風になります。
マネコンで編集した実態コードを上書きしたくない場合は、最初にDriftsコマンドを使うといいかと思います。


## つまりCDKとは何か？
ここまでの図を見ていただければわかるかと思いますが、実態はCloudformationのテンプレートであり、CDKはテンプレートを生成するためのものです。
もともと存在していたテンプレートのYamlファイルを様々な言語でラップして、管理性や再利用性を向上させたというわけです。


# 既存のリソースをCDKに移行するには？
さて、基本的な登場人物を理解したところで、次は既存のリソースをCDKに取り込むにはどうすればいいかについて解説します。
まず最初に言わなければならないのは、簡単ではないということです。エラーが多々発生し、一定程度のCDKやコーディングの知識が必要なのに加え、生成されるコードが人間Likeにできない場合もあるので、最小限必要な部分（DBやS3など、データが入っていたり、運用中のリソース）以外はゼロベースで作った方が良い気がしています。

本記事では解説しませんが、ゼロからCDKを作成する方法はAWSの公式記事から始めるとよさそうです。（AWSの日本語ページより、英語ページをChromeで翻訳したほうがわかりやすいです）

https://docs.aws.amazon.com/cdk/v2/guide/hello_world.html

こちらはコーディングの例が載っている公式Githubです。

https://github.com/aws-samples/aws-cdk-examples

## CDK移行に関するコマンド
とはいえ、「ゼロベースで作るなんて面倒くさい！」もしくは「このリソースは作り直したくない！」というものがある場合は、実態リソースからCDKへの移行を試したいですよね。
どんな方法があるのかとググってみると、`cdk import`, `cdk migrate`コマンドがどうやらCDK移行に関するものだと書いてあったのですが、正直わかりにくいのでそれぞれ解説します。

1. `cdk import`
  * AWS上の実態リソースを『スタックに』取り込むコマンド。つまり、CloudFormationの管理下に加えるコマンド。逆に言えば、それだけしかしてくれない
  * CloudFormationのテンプレートやCDKコードに変換してくれる機能はない
  * 実際にこのコマンドで移行するには、CDKコードで対象リソースをしっかり記述してから、`cdk import` でスタックに取り込む必要がある。ロケットのランデブーみたいなイメージ
1. `cdk migrete`
  * 実際にCDKコードを生成できる
  * 主に3つのオプションがある
    1. `--from-scan`：AWSリソースから直接CDK化する。エラーが多すぎてやりたくない
    1. `--from-stack`：cdk import した後、ここからCDK化することもできそう
    1. `--from-path`：Cloudformationテンプレートから作成する

これらのコマンドを組み合わせれば、移行はできます。
ただ、AWSは2023年後半にIaCジェネレータという「実態リソース」⇒「CDK/テンプレート」の変換フローを作ってくれているので、そちらに乗っかるのがよさそうです。

## IaCジェネレータとは

概念的には以下のようなイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/f15d6014-e040-c30b-878b-408a3e6bf55d.png)

手順
1. アカウント内の全リソースをスキャン
1. スキャンしたリソースのうち、必要なものを選択してテンプレートに変換
1. テンプレートをダウンロード
1. ダウンロードしたテンプレートファイルを元にして、`cdk migrate --from-path`コマンドでCDKコード化する

以下は実際のコンソール画面でお見せします。


## CDKに移行する

* まず最初に、CDK管理させたいリソース（Lambda, Stepfunctions, etc...）にタグ付けします（必須ではないですが、強くおすすめします。尋常じゃなく面倒なことになります）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/6f8b9d18-065e-772d-81a7-ff2d2a092e91.png)

* マネコンから「Cloudformation -> IaCジェネレータ」を選択します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/aa50da0d-b3b6-cec3-ea0f-af24515631cf.png)

* スキャンを開始します。（私はすでにスキャンしているので該当ボタンが「再スキャン」になっています）

* スキャンが終わったら、「テンプレートを作成」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/674d442f-d879-fb5d-a452-9983aa7fdb27.png)

* テンプレート名は任意です。ただ、削除ポリシーは「保持」にすることを強くお勧めします。CDK移行後にあやまってコード削除してDeployしたりローカルのスタックを削除すると、今あるリソースが消えてしまいます

* テンプレートに取り込むリソースを選択します。このとき、最初に設定したタグで検索して、一括選択してしまいましょう。タグ付けしていないと、最悪数千件のリソースをポチポチ選択したり、エラーでやり直しになったりします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/689cc002-4392-3ad9-a5f2-b7875b23b474.png)

* テンプレートの作成が完了したら、「テンプレートの表示」⇒「該当テンプレートを選択」⇒「アクション」⇒「CDKを表示するコマンド」を選びます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/a6caa9cc-426c-2b4e-a99f-8f9314aa40cc.png)

* 指示に従って、テンプレートのダウンロードとCDK化コマンドを実行すれば完了です。私の場合はここでいくつかのエラーが出たので、言われたことをしらみつぶしにエラー対処していきました
* ※cdkをインストールしている必要があるので、まだインストールしていない人は[公式サイト](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/hello_world.html)を参考に環境整備をしてください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/9093b242-4dd7-ac3e-8f29-d05b200fd1ec.png)

* ここまでできれば、CDKコードがローカルに生成されていることかと思います
* `./bin/`配下のファイルに、自分のアカウントIDとリージョンを記述する必要があるのでお忘れなく

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/df203a5b-3a26-44ba-94a9-086d4b5f8c9c.png)

## テンプレートからスタックを作成する（2024/10/10 追記）
作業中にEIPが二重で作成されてしまったり、「既存のリソースが存在する」と言われうまくいかなかったので、試行錯誤してうまく取り込めるようにしてみました。
実リソースがスタックに取り込まれていないと、CDK Deploy コマンドがリソースを新規作成しようとしてエラーとなるようです。
というわけで、まずは以下画像のようにスタックを更新する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/eb22470f-cde6-f8a7-83cf-70d0cf54608c.png)

## CDK側にスタック情報をインポートする（2024/10/10 追記）
この状態だと、CDK側が実リソースを認識していないのかまだうまくいきません。
そこで`import`コマンドを使用してスタック情報を取り込みます。

```
cdk import --force
```
私の環境では`--force`コマンドを使用しなければ上書きできませんでした。
ただこのコマンドはリソースを上書きするようなので、この時点で削除保護が付与されていなかったり、CDKのクリティカルな部分を編集していると問題が発生するかもしれません。

## CDKを編集する

不満があるとすれば、変数の命名がとても長いことと、コードが高レベルではなく低レベルレイヤーの関数だということでしょうか？　高レベル関数なら`new stepfunctions.StateMachine`となるようなのですが、`Cfn`とついているのは低レベル関数のようです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/7f61a7d6-4d4e-7dc3-0099-ae0697b445a5.png)

* というわけで、ここまでできたらあとは自分の思うがままに編集して、AWS上のリソースを更新できます
* 私は手始めに、1ファイルにまとめられていたリソース群と、メインファイルに直接書かれていたStepfunctionsコードを分割しました（古くて今際使われていないっぽい記法もあったのですが、アプローチは[こちらのサイト](https://dev.classmethod.jp/articles/cdk-practice-7-split-file/)を参考にしました）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/6dcd4610-6984-15e7-b084-591292de1484.png)

* 編集が完了したら、`cdk synth`でエラーチェックとテンプレート化を実施します。その後、`cdk diff`コマンドを実行して、不用意な変更がされていないか確かめました

## 補足 個別のImportについて（2024/10/10 追記）
さんざんIaCジェネレータの話をしてきましたが、VPCのようにUI上で強力な生成ツールが存在したり、リソースが大量すぎて手動での追加は無理！　という場合は除いて、基本的には個別にImportする方が確実です。

こちらの記事に個別インポートの方法がわかりやすく掲載されていました。

https://kakakakakku.hatenablog.com/entry/2024/01/31/190511


以上で準備完了です！！！
これで現状のAWS実態リソースをCDK管理下に置き、さらにCDKコード化する手順は終わりです。後は、思うがままにCDKライフを楽しんでいきましょう！！


# おわりに
CDKを既存リソースの移管前提で触り始めたとき、新しい概念が多く、かつドキュメントも少なく、だいぶ苦労してしまいました。ただまあ私の結論としては、 __リソースの全移行はやらないほうがいい__ ということでしょうか。それが肌感でわかっただけでも、とても収穫が多かったです。

とはいえまだ理解を進めている最中ですので、間違っている箇所がありましたらご指摘いただければ幸いです。

# 参考文献

https://qiita.com/Brutus/items/6c8d9bfaab7af53d154a

https://dev.classmethod.jp/articles/cdk-practice-7-split-file/

https://qiita.com/Brutus/items/6c8d9bfaab7af53d154a

https://docs.aws.amazon.com/cdk/v2/guide/hello_world.html

https://github.com/aws-samples/aws-cdk-examples

https://kakakakakku.hatenablog.com/entry/2024/01/31/190511

スペシャルサンクス
　ChatGPT先生


# 編集履歴
### 2024/10/10
CDK deploy がうまくいかなかったので、実リソースをスタックへインポートする手順を追加。