5分で理解するAWS CDK

# はじめに
本記事はAWS CDKについてキャッチアップすることを目的に、AWS CDKの概念や、ウォークスルーとしてAWSが公開しているWORKSHOPについて記載しています。

[AWS Cloud Development Kit (AWS CDK) ](https://aws.amazon.com/jp/cdk/)（以下、CDK）はTypeScript及びPythonなどのプログラミング言語を使用して、AWSリソースを定義し、Terraformの様に**Infrastructure as Code**（以降、IaC）を実現する**手段**として、クラウドインフラのリソースをプロビジョニングすることができます。

TerraformはGo言語で書かれた公式のAWS SDKを用いて、Terraform実行時にAWSのAPIを呼び出して、環境を構築しています。

Terraformは1つのプロバイダーに依存することなく、他のプロバイダーのクラウドインフラを扱えることから、汎用性だけで見たらTerraformに勝るものはないと思いますが、AWSに依存する環境の場合、IaCの手段としてCDKは最適だと思います。

## CDKとは
IaCはChefやAnsibleなど構成情報や設定情報を定義する**手続き型**や、Terraformの様にテンプレートに必要とするリソースを定義し、冪等性を担保するために**宣言型**に分類することができます。

AWS CDKはTerraformと同じ宣言型として、テンプレートを定義することでプロビジョニングを実現します。また、CDKでは高いレベルの**抽象化**を使用して**少ないコード量**でインフラストラクチャを定義できますが、その裏では[**CloudFormation**](https://aws.amazon.com/jp/cloudformation/)が使用されています。

従ってCDKで書いたコードはCloudFormationのリソースとしてデプロイされることにより、プロビジョニングを実現しています。そのため、デプロイ時間だけ見れば直接APIを実行しているだけのTerraformに比べると遅いですが、CloudFormationが裏で使用されているメリットとして、自動ロールバックやドリフト検出などを享受できます。

CDKを理解するために、宣言型のIaCの概念が理解できていれば、CloudFormationに対する深い知識は不要だと思います。その他、以下の知識や経験があると理解しやすいと思います。

- CDKで実装するリソースについて、コンソール画面で実際に作業した経験
- CloudFormationの概念
- オブジェクト指向（クラス、コンストラクター、オーバーライド、インターフェース等の概念）
- TypeScriptおよびPython等CDKを使用するためのプログラミング言語

CDKはただリソースを定義するだけでなく、プログラム内でテストコードも書くこともできます。

- Snapshot tests
- Fine-grained assertions
- Validation tests

## AWS CDK Constructs
CDKは以下、APP->Stack->Constructの各要素によって構成されています。

- APP
    - コンポーネントの最上位要素
    - 複数スタックの依存関係を定義
- Stack
    - CloudFormationのStack
    - デプロイ可能な最小単位
- Construct
    - 1つ以上からなるAWSリソース

### Construct Library
CDKはConstruct Libraryを使用してAWSリソースを定義します。また、Construct Libraryには以下3種類があります。

- High Level Construct（L2）
    - 直感的にリソースを定義することができ、デフォルト値や便利なメソッドが定義済み
    - 全てのサービスをサポートしている訳ではない
- Low Level Construct（L1）
    - プロパティを明示的に設定する必要があるため、パラメーターを詳細に設定したい場合はこの低レベルライブラリを使用
    - クラス名にCfnプレフィックスがついている
- Patterns
    - 一般的なアーキテクチャパターンに従う高レベルのConstructを提供

以下はHigh Level Construct（L2）でS3バケットを定義しているサンプルコード[^1]です。

```typescript
import { App, Stack, StackProps } from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';

class HelloCdkStack extends Stack {
  constructor(scope: App, id: string, props?: StackProps) {
    super(scope, id, props);

    new s3.Bucket(this, 'MyFirstBucket', {
      versioned: true
    });
  }
}

const app = new App();
new HelloCdkStack(app, "HelloCdkStack");
```

サーバレースのAmazonECS（Fargate）も以下のコード量で環境構築することができます。

```typescript
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecs_patterns from "aws-cdk-lib/aws-ecs-patterns";

constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, "MyVpc", {
      maxAzs: 3 // Default is all AZs in region
    });

    const cluster = new ecs.Cluster(this, "MyCluster", {
      vpc: vpc
    });

    // Create a load-balanced Fargate service and make it public
    new ecs_patterns.ApplicationLoadBalancedFargateService(this, "MyFargateService", {
      cluster: cluster, // Required
      cpu: 512, // Default is 256
      desiredCount: 6, // Default is 1
      taskImageOptions: { image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample") },
      memoryLimitMiB: 2048, // Default is 512
      publicLoadBalancer: true // Default is false
    });
  }
```

:::note
Constructsは初期化時に3つのを引数使用します。

- scope
    - 現在の構成のスコープ内で構成を定義します。thisで渡すのが一般的です。
- id
    - 同じスコープ内では一意であるIDが必要です。
- props
    - キーワード引数になるため特に指定しない場合、適切なデフォルト値が設定されます。また、propsは省略可能です。
:::

[^1]: 出典：[AWS Cloud Development Kit (CDK) v2](https://docs.aws.amazon.com/cdk/v2/guide/home.html)

## WORKSHOP
公開されている[WORKSHOP](https://cdkworkshop.com/)を基にCDKについて学びしょう。
はじめにCDKを使用するためには、Node.jsをインストールします。

### 準備作業
次に準備作業として任意のディレクトリを作成し、作成したディレクトリに移動します。

`$ mkdir cdk-workshop && cd cdk-workshop`

以下のコマンドを実行し、TypeScriptのプロジェクトを作成します。

`$ cdk init sample-app --language typescript`

プロジェクト作成後、CDKを用いて実装していきます。:coffee:

### デプロイ作業
以下のコマンドを実行すると、CloudFormationの定義情報が出力されるので、定義するテンプレートの内容を確認します。

`$ cdk synth`

CloudFormationの定義情報確認後、CDKが利用するリソース群をCloudFormationスタックとしてデプロイします。

`$ cdk bootstrap`

なお、AWS SSOを利用している環境の場合、そのまま実行すると、エラーになります。

```console
Unable to resolve AWS account to use. It must be either configured when you define your CDK Stack, or through the environment
```

`$ cdk bootstrap --profile XXXXXXXXXXXX`など、`--profile`のオプションを付与してもエラーになります。確認すると、本記事執筆時点ではAWS SSOについてサポートされてない様です。

- 参考：[AWS SSO Named Profiles Support #5455](https://github.com/aws/aws-cdk/issues/5455)

この様な場合、環境変数をexportすることで回避できます。

```text
export AWS_ACCESS_KEY_ID="XXXXXXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXXXXX"
export AWS_SESSION_TOKEN="XXXXXXXXXXXX"
```

デフォルトのリージョンは`us-east-1`になっているため、リージョンに関するエラーが出た場合はリージョンも設定します。

```console
❌  Environment aws://XXXXXXXXXXXX/us-east-1 failed bootstrapping: AccessDenied: XXXX〜
```

```text
export AWS_DEFAULT_REGION="ap-northeast-1"
```

`bootstrap`完了後、deployを実行することで定義したリソースがCloudFormationを媒介に構築されます。

`$ cdk deploy`

# おわりに
CDKの本質は抽象化です。抽象化によってAWSの深い知識がなくてもクラウドインフラの構築が行えます。また、Terraform同様、冪等性の性質を利用し、テンプレートとして利用できることが大きなメリットと感じています。

## 参考
- [AWS Cloud Development Kit (AWS CDK)](https://github.com/aws/aws-cdk)
- [AWS Cloud Development Kit (CDK) v2](https://docs.aws.amazon.com/cdk/v2/guide/home.html)
- [[AWS Black Belt Online Seminar] AWS Cloud Development Kit (CDK)](https://d1.awsstatic.com/webinars/jp/pdf/services/20200303_BlackBelt_CDK.pdf)