aws-cdk(typescript)入門した際のメモ

# 記事の概要
* AWSへリソースをデプロイ/管理するIaCツールとして`terraform`の利用経験はあったが aws-cdkは経験がなかった。
* また`node.js`や`typescript`の開発経験もありません。
* そんな私が`aws-cdk`へ入門した際のメモ。

# やった事
* 頭を空にしてAWSの公開している 入門ガイド に沿ってサンプルコードで`VPCとサブネットを作成する`ところまでをとりあえず。
    * AWS CDK の使用を開始する 入門ガイド: https://aws.amazon.com/jp/getting-started/guides/setup-cdk/

# 事前用意したもの
* 作業PC
    * 今回は macを使用して作業した
* AWS CLIの実行環境
    * `brew instal awscli` などする。
* node.js実行環境
    * `brew install node`でもよいが、nodeのバージョン管理ツールを使った方がよさそう。
* AWS CLI の認証情報
    * AWSアカウントID(12桁の数字)
    * リージョン: 今回は `ap-northeast-1` を使用した
    * `AdministratorAccess`相当の権限を持ったIAMユーザ
    * 手元の作業環境に 同IAMユーザのAPIキーを設定した profileの設定
    * 今回は `cdk-test` という profileをあらかじめ用意した

# 入門ガイドに沿ってやっていく
* AWS CDK の使用を開始する 入門ガイド: https://aws.amazon.com/jp/getting-started/guides/setup-cdk/

とりあえず 入門ガイドに沿ってやっていきます

## 1. aws-cdk cli インストール

> ここから https://aws.amazon.com/jp/getting-started/guides/setup-cdk/module-two/

>AWS CDK CLI をインストールするためには、まず、Node Package Manager をインストールする必要があります。 次のコマンドを実行して、AWS CDK CLI をグローバルにインストールします

なるほど、はい。
作業PCに　グローバルで aws-cdk cli をインストールしていきます。

```bash
% npm install -g aws-cdk
```

バージョン確認

```bash
% cdk --version
2.45.0 (build af1fb7c)
```

無事にインストールできたようです。
cdk の実行は `cdk` で良いようです。

helpを見たい時は `cdk --help` で確認できるようです。

```bash
% cdk --help                 
Usage: cdk -a <cdk-app> COMMAND

Commands:
~
```

## 2. bootstrapの実行

>デプロイする多くの AWS CDK スタックには、アセット、AWS Lambda 機能や Docker イメージなど、スタックとともにデプロイされる外部ファイルが含まれています。CDK は、これらをAmazon S3 バケットや他のコンテナにアップロードして、デプロイ中に AWS CloudFormation で使用できるようにします。デプロイには、これらのコンテナが AWS アカウントとデプロイ先のリージョンに既に存在していることが必要です。これらのコンテナの作成は、ブーストラップと呼ばれています。AWS アカウント (および特定のリージョン) をブートストラップするには、次を実行します。

...はい。
要は この`cdk bootstrap`を実行することでAWSアカウント上に、aws-cdkを使うためのリソース一式を用意してくれるようです。

ドキュメントに沿ってコマンドを実行していきます

```bash
% aws sts get-caller-identity

Unable to locate credentials. You can configure credentials by running "aws configure".
```

...認証情報が無いぞ的なエラー
上記のコマンドはアクセスしようとしているAWSのアカウント情報を表示してくれるコマンドのようです。
* 参考: https://dev.classmethod.jp/articles/get-aws-account-id-with-get-caller-identity/

今回は  検証用に profileを `cdk-test` で用意していたので --profile で指定。

```bash
% aws sts get-caller-identity --profile cdk-test
{
    "UserId": "xxxxxxxxxxxxx",
    "Account": "123456789012",
    "Arn": "xxxxxxxxxxxx"
}

※ 実際の値とは異なります
```

profile cdk-test の情報を確認できました。
`アカウントID`と決めておいたデプロイ先のリージョン`ap-northeast-1` を指定して `cdk bootstrap`を実行。

```bash
cdk bootstrap aws://123456789012/ap-northeast-1

※ 実際の値とは異なります
```

指定した "AWSアカウント" と "リージョン”　の組み合わせで "初めて" bootstrapが実行された場合、aws-cdk用のリソース一式が作成されるようです。
既にAWS側に関連リソースが存在する場合は再作成はされない。が、必要に応じてバージョンアップされるようです。

## 3. 新しい CDK プロジェクトを作成する
ここから https://aws.amazon.com/jp/getting-started/guides/setup-cdk/module-three/

> 新しい CDK プロジェクトを作成するために、CDK CLI を使用します。開始するには、システムに空のディレクトリを作成し、そのディレクトリに変更します。次に、cdk init を使用して、新しい TypeScript CDK プロジェクトを作成します。

`cdk-demo` ディレクトリを作成し、そこへ移動、`cdk init --language typescript`コマンドを実行するようです

```bash
mkdir cdk-demo
cd cdk-demo
cdk init --language typescript
```

下記のようなディレクトリ/ファイル郡が作成された事を確認した。

```bash
% tree -L 1
.
├── README.md
├── bin
├── cdk.json
├── jest.config.js
├── lib
├── node_modules
├── package-lock.json
├── package.json
├── test
└── tsconfig.json
```


## 4. インフラストラクチャを作成する

>最初の VPC を作成する前に、cdk init コマンドが作成したファイルを理解する必要があります。

...なるほど。
入門ガイドに書いてある事を読んで `cdk init` で作られたものをとりあえず確認していきます...
https://aws.amazon.com/jp/getting-started/guides/setup-cdk/module-three/

### CDK アプリケーションを構築してデプロイする

入門ガイドを読み進める。

```bash
├── bin
│   └── cdk-demo.ts
├── cdk.json
├── jest.config.js
├── lib
│   └── cdk-demo-stack.ts
├── package.json
├── package-lock.json
├── README.md
├── test
│   └── cdk-demo.test.ts
└── tsconfig.json

```

> 重要なファイルのいくつかとそれらの用途は次のとおりです。
>
>* bin/cdk-project.ts
    * これは CDK アプリケーションへのエントリポイントです。これにより、lib/* で定義したすべてのスタックがロード/作成されます。
>* lib/cdk-project-stack.ts
    * ここでメインの CDK アプリケーションスタックが定義されます。リソースとそのプロパティはこちらに含めることができます。
>* package.json
    * ここで、プロジェクトの依存関係、およびいくつかの追加情報とビルドスクリプト (npm build、npm test、npm watch) を定義します。
>* cdk.json
    * このファイルは、アプリケーションの実行方法、および CDK とプロジェクトに関連する追加設定とパラメータをツールキットに指示します。
>
>lib/cdk-demo.ts ファイルと bin/cdk-demo.ts ファイルに焦点を当てます。コードを追加しましょう。

... `cdk init`で吐かれた構成では `bin/ckd-project.ts` `lib/cdk-project-stack.ts` というファイルは居ないが `bin/cdk-demo.ts` と `lib/cdk-demo-stack.ts` のことでよさそう。

*  `bin/cdk-demo.ts`
*  `lib/cdk-demo-stack.ts`
*  package.json
*  cdk.json

あたりを抑えておけば良さそう?

### アプリケーションでいくつかのことを定義する

> bin/cdk-demo.ts スタックを次のように変更します。AWS アカウント ID を正しい番号に置き換え、正しいリージョンを選択してください。このガイドでは、us-east-1 または eu-west-1 のいずれかを選択することをお勧めします。

...はい。
とりあえず今回は `ap-northeast-1`を使うと決めてるのでそのように記述していきます。

```typescript:bin/cdk-demo.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { CdkDemoStack } from '../lib/cdk-demo-stack';

const app = new cdk.App();
new CdkDemoStack(app, 'CdkDemoStack', {
  env: { account: '123456789012', region: 'ap-northeast-1' },

});

※ 実際の値とは異なります
```
`env:` の行にAWSアカウントと リージョンを設定して保存。

`cdk init` で作成されたテンプレートを編集しようとして少し注意が必要だった点として
入門ガイドに掲載されていたコードは、`cdk init` で出力されたコードとimportの書き方が若干異なりました。

```typescript
// cdk init で吐かれたテンプレートの import
import * as cdk from 'aws-cdk-lib';
↓
// 入門ガイドの import
import * as cdk from '@aws-cdk/core';
```

aws-cdk v1とv2の違いとかだろうか？（どうも入門ガイドの方が v1の書き方で古いっぽい気配？)

### VPC のコード

> モジュールをインストールするには、npm を使用します。プロジェクトディレクトリで次のコマンドを実行します。

...はい。

```bash
npm install @aws-cdk/aws-ec2
```

`package.json` というファイルが作成され、必要なモジュール類がインストールされたようです。

>これで、VPC を作成する準備が整いました。lib/cdk-demo.ts でスタック定義を開きます。最初にファイルを開くと、次のようなものが表示されます。

> これがこのプロジェクトの骨組みです。ここで CDK を実行する場合、まだ定義していないため、リソースは作成されません。VPC の使用を開始するには、インストールされているモジュールをインポートし、その中のコードモジュールを参照する必要があります。Vpc クラスと SubnetType クラスをインポートします。

> VPC を作成するには、以下に示すように、2 つの AZ と詳細を指定してパブリックサブネットを作成します。

なるほど。`lib/cdk-demo.ts`　では
* 必要なモジュールを追加して
* リソースを定義する。

...ってコト？

```typescript:lib/cdk-demo-stack.ts
import * as cdk from '@aws-cdk/core';

// CHANGE: This is where you import the classes from the module:
import { Vpc, SubnetType } from '@aws-cdk/aws-ec2';

export class CdkDemoStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // The code that defines your stack goes here
    // CHANGE: We have created the vpc object from the Vpc class.
    const vpc = new Vpc(this, 'MainVpc',{

      // CHANGE: this is where we define how many AZs to use
      maxAzs: 2,

     // CHANGE: We define a single subnet configuration per AZ.
        subnetConfiguration:  [
          {
            // CHANGE: this is it's CIDR mask so 255.255.255.0
            cidrMask: 24,

            // CHANGE: a name for each of these subnets
            name: 'public-subnet',

            // CHANGE: and the subnet type to be used - here we will have
            // a public subnet. There are other options available here.
            subnetType: SubnetType.PUBLIC
          },
        ]
      });
  }
}

```

※ このファイルも、`cdk init`で出力されたテンプレートの内容と 入門ガイド で掲載されているコードの import 内容が少し異なったので、init のテンプレートを元に変更しようとする場合は注意が必要。

> これで、このインフラストラクチャの変更をアカウントにデプロイする準備が整いました。

やったね。

### デプロイ時間

>デプロイをテストする時間です。コードが有効かどうかを確認するには、TypeScript を JavaScript にコンパイルする npm ビルドを実行します。それが成功した場合は、次のデプロイコマンドを実行できます。

はい。なるほど？

```
% cdk deploy --profile cdk-test
```

...と。早まってこれをすぐ実行してしまうと、確認なしで AWS側へデプロイされてしまうようです。割と怖い？。
入門記事の文面は、おそらく要は test を通してから、という事を言いたかった？が、具体的な test については特に触れずにスルーされている？

記事には無かったが `cdk deploy` する前に `cdk diff`コマンドを使うと `terraform plan`と同じように変更差分を確認できるようです。

```
% cdk diff --profile cdk-test
```

また、`cdk synth` コマンドでデプロイされるCFnそのものが 同じ階層の ckd.out/ ディレクトリに出力されるのでCFnを読める場合は内容を確認できるようです。

```
% cdk synth
```

記事で飛ばされている test　については、node.js または typescript での適切なテストを構築する必要があるようです。
私にはまだその辺の知見があまり無いので一旦スルー。

問題なさそうなら `cdk deploy`します。

```
% cdk deploy --profile cdk-test
```

deploy が成功すると、指定したAWSアカウントとリージョンで
* `CloudFormation` に今回実行したstack が新たに作成されている事
* コードで記述したVPCとサブネット、および付随するリソースが新規に作成されている事

が確認できました。また、
* `cdk diff`で出ていた IGW や Routetable はコードには明示的に書いて無いが自動で作成されていた。CFnのスタック内を確認すると暗黙的に作成されたリソースも確認できる。
* リソースの`Name`タグは`Terraform`では設定しなければ付与されなかったが、aws-cdkでは自動でわかりやすい物が付与されていた。良いですね。

### リソースをクリーンアップする (オプション)

今回作成したリソースをまとめて削除する事ができます

```
% cdk destroy --profile cdk-test
```

stack名が表示され、消していいか聞かれるので'y' と回答すると、先ほど`cdk deploy`で作成されたCFn一式が削除されました。

というところまでで 入門ガイド の内容は終了。

>フォルダ構造、設定、スタック、アプリケーションなどの概念について詳しく知りたい場合は、ドキュメントを読むことをお勧めします。 
https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/home.html

はい、今後も功夫を積んで行きたいと思います。

# その他

## CFn の stack 名を変えてみる

今回サンプルにしたコードの場合は `bin/cdk-demo.ts`ファイルの下記の部分で変更できました。

```typescript:bin/cdk-demo.ts
new CdkDemoStack(app, 'CdkDemoStack', {
↓
new CdkDemoStack(app, 'myDemoStack', {
```
NameタグやCFnのスタック名に付与される Prefixになっているようです。

が、デプロイ済みのスタック名を後から変更した場合、`変更`としての追従はされず `別のスタック`として扱われるようでした。
設計,運用時には注意が必要になりそう。

## AZの数を変えてみる

`lib/cdk-demo-stack.ts`　の設定箇所を変えれば簡単に変更できました。

```typescript:lib/cdk-demo-stack.ts
      // CHANGE: this is where we define how many AZs to use
      maxAzs: 2,
```
リソースごとのパラメータはドキュメントを見て行けばよさそう。

変更後は `cdk diff` で差分を確認して `cdk deploy` すれば反映されました。

# 感想
* 今回はVPCとSubnetを作っただけだったが比較的少ない操作でも実現できるのは良い体験だった。
* CFnを素で書こうと思うと、個人的には大変な苦痛を伴う事が多いので 読みやすい言語で書けるのはよさそう。
* AWSが開発しているのでAWSを使う上では新機能への追従などは`terraform`より早そう?という期待。
* 今回参考にした入門記事が aws-cdk v1準拠で書かれている場合は、v2に対応させるという事もやってみるか...
* ECSを操作する`aws copilot` という同じくAWSの開発しているOSSがあるが、使用感が近いかもと思った。
    * https://aws.github.io/copilot-cli/ja/