AWS CDK と Typescript で EC2 を構築してみる。

## 前提条件

```sh
% node --version
v12.13.0
```

```sh
% npm --version
6.12.0
```

## CDK をインストール

[README](https://github.com/aws/aws-cdk#at-a-glance) を参考にして npm からインストールします。

```sh
% npm i -g aws-cdk
```

## プロジェクトの初期化

適当なディレクトリを作成します。

```sh
% mkdir cdk-ec2
% cd cdk-ec2
```

初期化用のテンプレートは次のコマンドから確認できます。

```sh
% cdk init --list
Available templates:
* app: Template for a CDK Application
   └─ cdk init app --language=[csharp|fsharp|java|javascript|python|typescript]
* lib: Template for a CDK Construct Library
   └─ cdk init lib --language=typescript
* sample-app: Example CDK Application with some constructs
   └─ cdk init sample-app --language=[csharp|fsharp|java|javascript|python|typescript]
```

今回はテンプレートとして sample-app を選択しました。

```sh
% cdk init sample-app --language=typescript
```

コマンドを実行すると次のファイルとディレクトリが作成されます。

```sh
% tree -I node_modules -L 2 -a .
.
├── .git
│   ├── COMMIT_EDITMSG
│   ├── HEAD
│   ├── config
│   ├── description
│   ├── hooks
│   ├── index
│   ├── info
│   ├── logs
│   ├── objects
│   └── refs
├── .gitignore
├── .npmignore
├── README.md
├── bin
│   └── cdk-ec2.ts
├── cdk.context.json
├── cdk.json
├── jest.config.js
├── lib
│   └── cdk-ec2-stack.ts
├── package-lock.json
├── package.json
├── test
│   └── cdk-ec2.test.ts
└── tsconfig.json
```

インフラ構築用のコード以外にテストが追加されています。
git リポジトリの初期化も行ってくれるようです。

## CDK の認証

`aws-cli` と同じように `--profile`  オプションを指定して ~/.aws/config のプロファイルを指定できます。

```sh
% cdk diff --profile my-profile
```

自分の場合、次のエラーが発生しました。

```sh
% cdk diff --profile my-profile
Stack CdkEc2Stack
AWS region must be configured either when you configure your CDK stack or through the environment
```

~/.aws/config の source_profile のプロファイルから region を引き継がないようです。

```text
[default]
region = ap-northeast-1

[profile my-profile]
role_arn = arn:aws:iam::123456789012:role/myrole
source_profile = default
```

~/.aws/config の my-profile へ region を追加した後、再度コマンドを実行します。

```sh
% cdk diff --profile my-profile
Stack CdkEc2Stack
Unable to resolve AWS account to use. It must be either configured when you define your CDK or through the environment
```

どうやら JavaScript の AWS SDK では MFA 認証が行えないようです。
[aws-mfa](https://github.com/broamski/aws-mfa) を使用して temporary credentials を取得する必要があります。

まずは pip から aws-mfa をインストールします。

```sh
% pip install aws-mfa
```

~/.aws/credentials の default プロファイルを default-long-term プロファイルへ変更します。

```text
[default-long-term]
aws_access_key_id = type_your_id
aws_secret_access_key = type_your_key
aws_mfa_device = type_your_mfa_arn
```

`aws-mfa` コマンドを実行します。

```sh
aws-mfa
```

デバイスに表示されているコードの入力を求められるため、入力します。
すると ~/.aws/credentials の default プロファイルが追加されます。

```text
[default-long-term]
aws_access_key_id = type_your_id
aws_secret_access_key = type_your_key
aws_mfa_device = type_your_mfa_arn

[default]
aws_access_key_id = xxxxx
aws_secret_access_key = yyyyy
aws_security_token = zzzzz
```

この状態で再度、CDK の認証を試みます。

```sh
% cdk diff --profile my-profile
Stack CdkEc2Stack
IAM Statement Changes

...長いので略
```

認証でだいぶハマってしまいました。
今回発生したエラーの詳細は次の issue にまとめられています。
<https://github.com/aws/aws-cdk/issues/1656>

## VPC の構築

aws-ec2 モジュールを import した後、 Vpc クラスを new するだけです。

```typescript
import cdk = require('@aws-cdk/core');
import ec2 = require('@aws-cdk/aws-ec2')

export class CdkEc2Stack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    new ec2.Vpc(this, 'MyVpc');
  }
}
```

コンストラクタの第三引数から作成する VPC のタイプを選択できます。

```typescript
    new ec2.Vpc(this, 'MyVpc', {
      subnetConfiguration: [
        {
          cidrMask: 28,
          subnetType: ec2.SubnetType.PUBLIC,
          name: 'public'
        },
      ]
    });
```

## インスタンスの構築

Instance クラスを new します。
コンストラクタの第三引数へインスタンスを構築する VPC とイメージ、インスタンスタイプを指定できます。

```typescript
    const image = new ec2.AmazonLinuxImage()
    new ec2.Instance(this, 'MyInstance', {
      vpc: vpc,
      instanceType: ec2.InstanceType.of(ec2.InstanceClass.T2, ec2.InstanceSize.MICRO),
      machineImage: image
    })
```

## デプロイ

次のコマンドからデプロイすることができます。

```sh
% cdk deploy --profile my-profile
```

後始末は `destroy` コマンドから行うことができます。

```sh
% cdk destroy --profile my-profile
```