AWS CDK WorkshopをJavaでやってみた

# 概要

[AWS CDK Workshop](https://cdkworkshop.com/50-java.html)をJavaでやってみた。
その際に感じたことのメモ。

# 前提

OS: macOS Ventura 13.2
プロセッサ: 2.4 GHz 8コアIntel Core i9
cdkのversion: 2.63.2 (build e08e34a)

# [New Project](https://cdkworkshop.com/50-java/20-create-project.html)

## (自分用メモ)cdkコマンドをインストール

`npm install -g aws-cdk`コマンドを実行する。

## [cdk init](https://cdkworkshop.com/50-java/20-create-project.html)

上記ページの通り、ディレクトリを作って cdk init をする。 `Executing 'mvn package`が長くかかったが焦らず待つ。

## [Project structure](https://cdkworkshop.com/50-java/20-create-project.html)

上記ページの通りの構成になっていることを眺める。
なんとなくSQS,SNSのことを操作していることを感じ取る。 

## [cdk synth](https://cdkworkshop.com/50-java/20-create-project.html)

上記ページに従って`cdk synth`を実行すると、標準出力にCloudFormationのtemplateが出力される。


## [cdk deploy](https://cdkworkshop.com/50-java/20-create-project/500-deploy.html)

ここから実際にAWS上にリソースを作ることになるので若干慎重に。

まず`aws sts get-caller-identity`して、aws cliの向き先があっていることを確認する。

ここからは[このページ](https://cdkworkshop.com/50-java/20-create-project/500-deploy.html)に従って、まず`cdk bootstrap`する。  
これでCloudFormationスタックとしてAWS CDKが利用するリソースを準備できる模様。

そこから`mvn package`して、`cdk deploy`実行する。
下記のような感じでどんな影響が出るか確認できる。
```
IAM Statement Changes
┌───┬─────────────────────────┬────────┬─────────────────┬───────────────────────────┬─────────────────────────────────────────────────────────┐
│   │ Resource                │ Effect │ Action          │ Principal                 │ Condition                                               │
├───┼─────────────────────────┼────────┼─────────────────┼───────────────────────────┼─────────────────────────────────────────────────────────┤
│ + │ ${CdkWorkshopQueue.Arn} │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com │ "ArnEquals": {                                          │
│   │                         │        │                 │                           │   "aws:SourceArn": "${CdkWorkshopTopic}"                │
│   │                         │        │                 │                           │ }                                                       │
└───┴─────────────────────────┴────────┴─────────────────┴───────────────────────────┴─────────────────────────────────────────────────────────┘
```

終わるとSQS,SNSのリソースが作成されている。

# [Hello, CDK](https://cdkworkshop.com/50-java/30-hello-cdk.html)

API GatewayとLambdaを使ったサンプルを作っていきますよ、と。

## [Cleanup sample](https://cdkworkshop.com/50-java/30-hello-cdk/100-cleanup.html)

コードからSQS, SNSの記述を消し去り(テストも消す)、再度デプロイする。

`cdk diff`して、差分を確認できる。`terraform plan`みたいなものかな。

```
┌───┬─────────────────────────────────┬────────┬─────────────────┬───────────────────────────┬─────────────────────────────────────────────────────────────────┐
│   │ Resource                        │ Effect │ Action          │ Principal                 │ Condition                                                       │
├───┼─────────────────────────────────┼────────┼─────────────────┼───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ - │ ${CdkWorkshopQueue50D9D426.Arn} │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com │ "ArnEquals": {                                                  │
│   │                                 │        │                 │                           │   "aws:SourceArn": "${CdkWorkshopTopicD368A42F}"                │
│   │                                 │        │                 │                           │ }                                                               │
└───┴─────────────────────────────────┴────────┴─────────────────┴───────────────────────────┴─────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[-] AWS::SQS::Queue CdkWorkshopQueue50D9D426 destroy
[-] AWS::SQS::QueuePolicy CdkWorkshopQueuePolicyAF2494A5 destroy
[-] AWS::SNS::Subscription CdkWorkshopQueueCdkWorkshopStackCdkWorkshopTopicD7BE96438B5AD106 destroy
[-] AWS::SNS::Topic CdkWorkshopTopicD368A42F destroy
```

差分を確認して、`cdk deploy`すると、コードから消したリソースがAWSからも消える。

## [Hello Lambda](https://cdkworkshop.com/50-java/30-hello-cdk/200-lambda.html)

lambdaにjsのコードをあげてみる。  
書きながら、どんなリソースを作ろうとしているのか理解していく。  
lambdaの作成は手順に従っていけばすんなりできる。  

このページで、簡単にcdkのsdkで提供しているクラスの概念的なものを説明している。  
cdkのsdkで提供されているクラスは`construct`(何かの要素を組み合わせてできたもの、というイメージ?)であると説明している。  
`construct`を組み合わせたものが`construct`を構成するという、入れ子なイメージ。  
また、各クラスのコンストラクタが`(scope, id, props)`というシグニチャを持っており、それらが何を意図しているか説明している。

- scopeはその`construct`が作成されたscope(scopeとは？という気持ちになるが我慢して進める。コンテキスト、みたいなイメージか？)を指し示す。基本的にthisを渡す。
- idは`construct`内で一意になる値を渡す。
- propsは各`construct`の性質によって取りうる値が異なる。`lambda.Function`なら`runtime`,`code`,`handler`など

## [CDK Watch](https://cdkworkshop.com/50-java/30-hello-cdk/300-cdk-watch.html) 

lambdaのコードだけ変えるのに、`cdk deploy`するのだと時間かかってしょうがないよね。。？みたいな問題提起からこの章は始まる。  
`cdk deploy --hotswap`を使えば、cloud formationを経由せずにデプロイできるため、デプロイ時間短縮につながる。

実際、この章でやってるコマンドを私の環境でやってみたところ、普通の`cdk deploy`だと40s近かったが、`cdk deploy --hotswap`だと10秒くらいで完了した。  

そして、CDKでは`cdk watch`コマンドなるものを用意している。  
手元で`cdk watch`コマンドを流しておけば、コードの変更を勝手に検知して、AWSリソースに変更を加えてくれる。
コードに変更を加えた際にhotswapで変更可能(cloud formationを介さくても良い変更)であればhotswapでデプロイし、そうでなければcloud formationでデプロイする。

手元で`cdk watch`を試してみたところ、lambdaにあげているjsファイルを変更した際にはhotswapでのデプロイが走り、lambdaのhandler名を変えた場合にはcloud formationを介したデプロイが走った。

## [API Gateway](https://cdkworkshop.com/50-java/30-hello-cdk/400-apigw.html)

API Gatewayを上で作成したLambdaの前段に置いてあげましょう、という章。  
コードを記述して`cdk deploy`すると、標準出力に

```
Outputs:
CdkWorkshopStack.Endpoint~~~~~~~~ = https://~~~~~~~~~~~~.execute-api.ap-northeast-1.amazonaws.com/prod/
```

みたいな感じでエンドポイントが表示されるのでcurlなりブラウザなりでアクセスできる。

# [Writing constructs](https://cdkworkshop.com/50-java/40-hit-counter.html)

これまでの構成要素にDynamoDBを加えて、URLをアクセスした回数をカウントするようにする。

## [Define the HitCounter API](https://cdkworkshop.com/50-java/40-hit-counter/100-api.html)

`HitCounter.java`なるクラスと、`HitCounterProps.java`なるインターフェースを作成する。

## [Hit counter Lambda handler](https://cdkworkshop.com/50-java/40-hit-counter/200-handler.html)

`hitcounter.js`なるLambdaにあげるファイルを作成する。  
`hitcounter.js`を記述するにあたって必要なLambdaの識別子、DynamoDBの接続先はdeployの際に確定するので、コード上は環境変数としている。
これらはconstructのコード(Javaのコード)に記述していく。

## [Define resources](https://cdkworkshop.com/50-java/40-hit-counter/300-resources.html)

`HitCounter.java`にDynamoDBのリソース定義とLambdaのリソース定義を記述している。  
１つ前の章で言及した環境変数は、ここで注入するためのコードを書いている。

## [Use the hit counter](https://cdkworkshop.com/50-java/40-hit-counter/400-use.html)

`CdkWorkshopStack.java`に`HitCounter.java`というconstructを利用する旨を記述する。
API Gatewayからは`hitcounter.js`を携えたlambdaを呼ぶようにする。

この状態で`cdk deploy`したものにアクセスすると502 Bad Gatewayになる。

## [CloudWatch Logs](https://cdkworkshop.com/50-java/40-hit-counter/500-logs.html)

CloudWatchにエラーをみにいく方法が書いてある。DynamoDBに書き込みにいく権限が無さげなエラーが出ていることがわかる。
(Lambdaあまり使ってない勢からするとこういうエラー解析の説明はかなり助かる)

## [Granting permissions](https://cdkworkshop.com/50-java/40-hit-counter/600-permissions.html)

hitcounterのLambdaから、DynamoDBとhelloのLambdaを叩く権限を与える。
すると、200が返るようになる。

## [Test the hit counter](https://cdkworkshop.com/50-java/40-hit-counter/700-test.html)

色々なパスにアクセスして、DynamoDBのコンソールから正しくカウントされていることを確認する。

また、HitCounterは再利用可能で、maven centralにあげて、誰でも容易に同じようなことができるよ、と言及している。

# [Using construct libraries](https://cdkworkshop.com/50-java/50-table-viewer.html)

[cdk-dynamo-table-viewer](https://github.com/cdklabs/cdk-dynamo-table-viewer)というconstructのライブラリを利用して、Webページからhitcounterのテーブル情報をみれるようにする。

## [Learning about the Table Viewer construct](https://cdkworkshop.com/50-java/50-table-viewer/100-discovery.html)

[cdk-dynamo-table-viewer](https://github.com/cdklabs/cdk-dynamo-table-viewer)の紹介。
ざっとみた感じ、DynamoDBから情報を取得し、それを元にhtmlを組み立て返すLambdaを作成しているようだった。
(TypeScriptで記述されているっぽいが、これをどうやってJavaのライブラリとして固めているんだ、、？jsiiがよしなにやってくれるのだろうか)

あと、これは権限制御とかせずにDynamoDBの中身が見れちゃうので本番では使うなよ、と。

## [Installing the library](https://cdkworkshop.com/50-java/50-table-viewer/200-install.html)

`pom.xml`にライブラリの設定を加える。

## [Add a table viewer to your stack](https://cdkworkshop.com/50-java/50-table-viewer/300-add.html)

上記ライブラリ由来のconstructを加えるコードを`CdkWorkshopStack.java`に付け足す。

## [Deploying our app](https://cdkworkshop.com/50-java/50-table-viewer/400-deploy.html)

`cdk deploy`してブラウザから、どのパスに何回アクセスしたか見れることを確認する。

また、Constuctライブラリは便利な反面、IAMの権限などをいじったりするので、信頼できるもの以外は使わないようにとの注意喚起をしている。

## [Extra credit](https://cdkworkshop.com/50-java/50-table-viewer/500-extra-credit.html)

ブラウザ表示された表はhitsの降順であるとのこと。
(extra creditは、おまけ、みたいな意味なのだろうか)

# [Clean up](https://cdkworkshop.com/50-java/60-cleanups.html)

`cdk destroy`で作ったリソースを消していく。
この際、DynamoDBのテーブルはデフォルトの設定だと残り続けるので、明示的に`RemovalPolicy.DESTROY`の設定を入れてやることで消せるようになる。

また、CloudWatch Logsのログは手で消すしかないとのこと。

# 所感

私の環境だと全く詰まることなく実行できた。

IaCはこんなに進んでいるのか、という感想を得た(今更感)。

特に`cdk watch`のあたりはリアルタイムにローカルのコードがAWSに反映されており、開発スピード向上に役立ちそうである。

実際に導入するとなると、
- test、staging、productionの資材をどう書き分けるか
- ディレクトリ構成をどうするのか
- 汎用化したクラスを作るべきなのか(terraform のmoduleみたいな感じで)

とかを考える必要がありそう。この辺はどんなIaCツールを使おうとも考慮が必要だろう。

cdkの対抗馬として考えるならterraform。
例えばJavaを専門に扱っているチームなのであれば、JavaでやってしまえばHCLを新たに覚える必要はなくなるので良いのかなぁ。(AWSを離れるという選択がありうる場合はterraformの方が安心感はあるが)