AWS CDKを使えばTypeScriptでAWSインフラを定義できるらしい

AWS CDKなるものを使うとコードでインフラを実装できるらしいです。
やってみます。

## What Is the AWS CDK?

使い慣れたプログラミング言語でAWSリソースを定義できるもの。

サポート言語は
- TypeScript
- JavaScript
- Python
- Java
- C#/.NET
とのこと。

## CDKの利点

個人的な利点は
- プログラムなのでCFnと比べてレビューの敷居が下がる
- 動的なインフラ定義の幅が広がる（予想）

## やってみる
というわけで、やってみます。
### 環境
- VSCode
    - 1.43.2
- node.js
    - v12.11.0
        - aws-cdk
            - 1.31.0
        - TypeScrippt
            - 3.8.3
- aws-cli
    - 1.18.31

環境構築参考：
[AWS CDK Intro Workshop > 前提条件](http://educationhub-e901cdf0-10eb-11ea-b86c-4993bc17c7c2.s3-website-us-west-2.amazonaws.com/15-prerequisites.html)

### 作りたい構成

良さげなサンプルソースがあったので参考にして以下のような構成になればいいなと思ってます。

![Untitled Diagram.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/14b1fcb0-56ee-0d3d-326d-d50d431019f4.jpeg)
[aws-samples/aws-cdk-examples/ecs-service-with-advanced-alb-config](https://github.com/aws-samples/aws-cdk-examples/blob/master/typescript/ecs/ecs-service-with-advanced-alb-config/index.ts)

### 準備

プロジェクトを作成します。

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

libディレクトリに構築に使用するファイルが作成されます。

主にこのファイルをいじいじしていくらしいです。

```typescript:.ts

import * as sns from '@aws-cdk/aws-sns';
import * as subs from '@aws-cdk/aws-sns-subscriptions';
import * as sqs from '@aws-cdk/aws-sqs';
import * as cdk from '@aws-cdk/core';

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

    const queue = new sqs.Queue(this, 'ElbSampleQueue', {
      visibilityTimeout: cdk.Duration.seconds(300)
    });

    const topic = new sns.Topic(this, 'ElbSampleTopic');

    topic.addSubscription(new subs.SqsSubscription(queue));
  }
}
```

TypeScriptなのでファイル監視をします。

```bash

npm run watch
```
とりあえずきれいにします。

```typescript:.ts

import * as cdk from '@aws-cdk/core';
    
export class ElbSampleStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
  }
}
```

必要なcdkモジュールをインストールします。

```bash

npm i @aws-cdk/aws-elasticloadbalancingv2
npm i @aws-cdk/aws-ecs
```

### 実装

```typescript:.ts
import * as cdk from '@aws-cdk/core';
    
// 使うモジュール
import * as elb from '@aws-cdk/aws-elasticloadbalancingv2';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as ecs from '@aws-cdk/aws-ecs';

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

    // Create a cluster
    const vpc = new ec2.Vpc(this, 'cdk-sample-vpc', { maxAzs: 2 });

    const cluster = new ecs.Cluster(this, 'cdk-sample-cluster', { vpc });
    cluster.addCapacity('DefaultAutoScalingGroup', {
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T2,
        ec2.InstanceSize.MICRO
      )
    });

    // Create Task Definition
    const taskDefinition = new ecs.Ec2TaskDefinition(
      this,
      'cdk-sample-taskDef'
    );
    const container = taskDefinition.addContainer('web', {
      image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
      memoryLimitMiB: 256
    });

    container.addPortMappings({
      containerPort: 80,
      hostPort: 8080,
      protocol: ecs.Protocol.TCP
    });

    // Create Service
    const service = new ecs.Ec2Service(this, 'cdk-sample-service', {
      cluster,
      taskDefinition
    });

    // Create ALB
    const lb = new elb.ApplicationLoadBalancer(this, 'cdk-sample-elb', {
      vpc,
      internetFacing: true
    });
    const listener = lb.addListener('PublicListener', { port: 80, open: true });

    // Attach ALB to ECS Service
    listener.addTargets('ECS', {
      port: 80,
      targets: [
        service.loadBalancerTarget({
          containerName: 'web',
          containerPort: 80
        })
      ],
      // include health check (default is none)
      healthCheck: {
        interval: cdk.Duration.seconds(60),
        path: '/health',
        timeout: cdk.Duration.seconds(5)
      }
    });

    new cdk.CfnOutput(this, 'LoadBalancerDNS', {
      value: lb.loadBalancerDnsName
    });
  }
}
```

### デプロイ

CDKを元にCFnを作成します。

```bash

cdk synth
```
デプロイに使用するS3バケットを作成します。

```bash

cdk bootstrap
```

CloudFormationを使用してS3バケットが作成されるようです。

<img width="1161" alt="スクリーンショット 2020-04-05 4.21.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/f2c710fd-1dc0-084e-2ac2-40de2882828d.png">

デプロイします。

```bash
cdk deploy
```
デプロイに関する変更の確認を求められます。
![スクリーンショット 2020-04-05 4.26.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/512e07d0-b2ff-3ce3-ca5f-dde0d682392e.png)

`y`でデプロイが実施されるのでログを眺めながら待ちます。

作成されました。
![スクリーンショット 2020-04-05 4.34.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/87d57101-26fc-d316-3675-6f7b4ddf711d.png)
AWSコンソールでも確認できます。
<img width="1167" alt="スクリーンショット 2020-04-05 4.42.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/6e85cdbf-0f83-32bf-dac6-283de24db265.png">

出力されたDNSにアクセスしてみます。
<img width="1172" alt="スクリーンショット 2020-04-05 4.35.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/3dc165a4-e98e-19c6-6f46-0f2a28c9fbe9.png">

成功しました。

### 後処理

デプロイしたリソースもコマンドで消せるようなので消しておきましょう。

```bash
cdk destroy
```
![スクリーンショット 2020-04-05 4.44.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/5abb98bd-d902-99d5-0624-946f946e6997.png)
`y`で削除が実施されますのでログを見て待ちます。。

削除されたようです。

![スクリーンショット 2020-04-05 4.49.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246580/ca29a331-929c-62b0-4ee6-449c092cea95.png)

めでたし。

## 感想

AWS CDKが話題になっていたのでなんとなく試してみましたが、CFnに比べて学習コストが２段階くらい下がったような気がします。

ただ、ソース量を見れば分かる通り定義していないものもうまい具合に作成してくれるようなので、意図しないリソースが作成されないように注意しなければいけないと感じました。

## 参考

[【コードでインフラ定義】CDKという異次元体験をさくっとやるのに便利なAWS公式Workshopの紹介 | Developers.IO](https://dev.classmethod.jp/articles/cdk-workshop-typescript/)

[AWS Cloud Development Kit (AWS CDK) ワークショップ](http://educationhub-e901cdf0-10eb-11ea-b86c-4993bc17c7c2.s3-website-us-west-2.amazonaws.com/)

[aws-samples/aws-cdk-examples/ecs-service-with-advanced-alb-config](https://github.com/aws-samples/aws-cdk-examples/blob/master/typescript/ecs/ecs-service-with-advanced-alb-config/index.ts)