AWS CDK Python の v1 to v2 へのアップグレード手順

# はじめに

AWS CDK を実行すると、 AWS CDK v1 の開発がメンテナンスモードになるというアナウンスが出るようになった。

```
NOTICES

19836	AWS CDK v1 entering maintenance mode soon

	Overview: AWS CDK v1 is entering maintenance mode on June 1, 2022.
	          Migrate to AWS CDK v2 to continue to get the latest features
	          and fixes!

	Affected versions: framework: 1.*, cli: 1.*

	More information at: https://github.com/aws/aws-cdk/issues/19836


If you don’t want to see a notice anymore, use "cdk acknowledge <id>". For example, "cdk acknowledge 19836".
```

では v2 対応にアップグレードしようと公開されている資料を確認したが、いくつか分かりづらい点・つまづいた点があったため、自身が実施した v2 への対応方法を書き記す。

https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/migrating-v2.html

```bash:実行環境
# 注意: awscdk v2 は Python 3.7 以上
# https://pypi.org/project/aws-cdk-lib/
$ pipenv run python --version
Python 3.9.1
$ pipenv run cdk --version
2.25.0 (build ae1cb4b)
$ node --version
v16.14.2
```

# 対応方法

## 実行環境の更新
 
今回は `cdk` コマンドをグローバルインストールしているため、最初に `sudo npm install -g aws-cdk` で v2 バージョンの awscdk コマンドを取得する。

また v1 用の Python ライブラリをアンインストールして、v2 用の Python ライブラリをインストールする必要がある。 v1/2 の見分け方は以下の通り。

- v1用: `aws-cdk.core` などのようにサービスごと分割されているため、これらを全て uninstall する
- v2用: 全てが `aws-cdk-lib` のみを install すれば大丈夫 (例: `pipenv install --dev asw-cdk-lib` )

なお、記事執筆時点 (22/05/27) では `exceptiongroup` の依存関係でインストールに失敗するため、明示的なバージョン指定をしてこの問題を一時的に回避する必要がある。

Github Issueに記載がある通り、`exceptiongroup==1.0.0rc4` を指定すれば暫定対処できる。

https://github.com/aws/aws-cdk/issues/19844

## コードの修正

実行環境を v2 に更新すると v1 で書かれた AWSCDK の Python コードは動作しない。 v2で動作させるため、以下の点を修正する。

- v1 では `from aws_cdk import core` のように core モジュールを利用していたが、これがなくなったのでここを修正
    - `from aws_cdk import core` を `import aws_cdk as core` のように変更
- `core.Construct` がなくなり、`constructs.Construct` が利用されるようになったため、これを置き換える
    - 利用しているコードの最初に `from constructs import Construct` を記載し、`core.Construct` を `Construct` に置き換え

## メタデータの更新

実行バージョン環境が `cdk bootstrap` 実行時に生成されているので、改めて `pipenv run cdk bootstrap` を実行してメタデータを v1 のものから v2 のものに更新する。
この際、v1 で生成した cdk.json 内に v2 では無効化されたパラメータが存在する場合にエラーとなるため、もしこれがあれば取り除く。

自分の環境では `"@aws-cdk/core:enableStackNameDuplicates": "true"` が v2 では使えないという以下のようなエラーが出力されたので、このフラグを削除した。

> jsii.errors.JSIIError: Unsupported feature flag '@aws-cdk/core:enableStackNameDuplicates'. This flag existed on CDKv1 but has been removed in CDKv2. CDK will now behave as the same as when the flag is enabled.


## デプロイを実行

これで問題がなければ更新がなくともデプロイを実施する。 すると、デプロイで利用する CloudFormation 用のパラメータなどが v2 のものに置き換えられた内容のものがデプロイされる。

おそらく問題はないとは思うが、`cdk diff` などで動作に問題がないかをちゃんと確認してから実行の事。


## CloudFront Lambda@Edge が更新できない問題の対処

今回、そこそこに大きな環境の CDK を v2 にアップグレードしたのだが、そこで利用している Lambda@Edge を更新できないという問題があった。 この Lambda@Edge は [`aws_cdk.cloudfront.experimental`](https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_cloudfront.experimental.html) を使って v1 でデプロイしたものであった。

これは同様の問題が報告されているが、解決には至っていない。 原因としては、CloudFormation の制約にもあるようだが……

https://github.com/aws/aws-cdk/issues/18757

暫定的対処方法を検証した結果、「古いバージョンから新しいバージョンに更新しようとするから失敗する」わけで、「**v2用に新しく Lambda@Edge 用の関数を新しいリソース名で再デプロイする**」 という方法で問題を回避できる。
具体的な方法としては、`EdgeFunction` を生成する部分の コードの第二引数 `id` に新しい文字を追加することで、元々のIDの要素を制御から外して delete させ、新しいID の要素を追加するようにできる。　
今回の場合、ID名部分の末尾に `V2` を追加することで古い v1 向けの Lambda@Edge リソースを削除し、v2 向けのリソースを再デプロイした。

この変更を行った後に deploy を行うと、**元々の Lambda@Edge の削除には失敗して、約300秒待つことになる[^1]。** これはデプロイの手順が Lambda@Edge -> CloudFront の順だからであり、古いLambda@Edge はいまだに CloudFront で用いられているため削除に失敗するためである。

元々あった Lambda@Edge リソースは後々手動で削除する必要がある点だけには注意。

[^1]: cdk のコマンドとしては削除に失敗したというエラーがでるが、300秒ぐらい経過すると削除待ちを諦めて次の構築へとすすむ

v2 用の Lambda@Edge リソースをデプロイした後は、同じ内容を再度デプロイすることになっても問題なく対処できるようになる。

:::note warn
なお、この手順はリソースの削除という処理を挟むため、本番への影響がないかどうかはちゃんと自身の開発環境で検証の後実施することを強く推奨する。 何か問題が起こっても自己責任で。
:::

# おわりに

AWS CDK は確かに便利ではあるのだが、そのバックエンドにあるのが CloudFormation という仕組みなので、問題が発生した場合の原因が分かりにくい、リソースの更新完了までにかなりの時間がかかる、ということも経験した。 
インフラ構成ツールのメジャーチェンジはやりたくないという印象だが、それでも比較的簡単に移行はできたように思う。