AWS CDKでVPCを作りEC2を立ち上げる

AWS CDKでVPC周りとEC2を作ってみる。
次の記事で、session managerのセットアップ、nginxのセットアップも続けて行うが、本記事ではスコープ外とする。

特にVPC周りを作ろうする際には、関連リソースのセキュリティグループ、ルートテーブル、NAT Gatewayなどを自動作成してくれるが、
気をつけておかないと高額課金の可能性がある。
この辺も、実際に作成される予定のリソースを確認しながら進めてみる。

## 関連資料
* [API Reference · AWS CDK](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html)
    * API Referenceを追いかけながら、リソースを作成していく
* [料金 - Amazon VPC | AWS](https://aws.amazon.com/jp/vpc/pricing/)
    * 関連リソースの価格帯は理解しておきたい

## 前提
* aws cdkでスタック作成→削除を行える
    * [cdkでSystems ManagerのParameter Storeにパラメータを作成する #AWS - Qiita](https://qiita.com/ashiwa/items/57538e347c7de1aeedef)

## リポジトリ内でcdkの初期設定ファイルを生成する

```bash
$ mkdir sample-cdk-ec2-nginx
$ cd sample-cdk-ec2-nginx
$ cdk init --language typescript
$ cdk ls
# SampleCdkEc2NginxStack
```

## 必要なリソース
* VPC
    * セキュリティグループ
    * ルートテーブル
    * EIP
* EC2


## VPC作成

### まずは雑にVPCを作成させてみる

* [class Vpc (construct) · AWS CDK](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Vpc.html)

```ts
import * as ec2 from 'aws-cdk-lib/aws-ec2';
```

```ts
const vpc = new ec2.Vpc(this, 'SampleCdkEc2NginxVpc', {
    maxAzs: 2,
    vpcName: 'web-service'
});
```

cdk synthで作成予定のリソースを見てみると、結構えげつない量のリソースを作成してくれる事がわかる。
デフォルトで2AZ指定しているせいもあるが、2️AZにPublic Subnet, Private Subnet,４サブネット分作成するというベストプラクティスに沿った構成のようだ。
だたこれをやってしまうと、例えばNatGatewayは１ヶ月立ち上げっぱなしで `0.062(USD/h) * 155(JPY/USD) * 24(h) * 31(day) * 2個 = 14298(JPY)` 掛かってしまう。

```bash
# 出力予定のCFnを確認
$ cdk synth > out.yml

# 作成予定のリソースを数える
$ grep 'Type:' out.yml|sort|uniq -c
   1     Type: AWS::CDK::Metadata
   2     Type: AWS::EC2::EIP
   1     Type: AWS::EC2::InternetGateway
   2     Type: AWS::EC2::NatGateway # これだけで14298円/月
   4     Type: AWS::EC2::Route
   4     Type: AWS::EC2::RouteTable
   4     Type: AWS::EC2::Subnet
   4     Type: AWS::EC2::SubnetRouteTableAssociation
   1     Type: AWS::EC2::VPC
   1     Type: AWS::EC2::VPCGatewayAttachment
   1     Type: AWS::IAM::Role
   1     Type: AWS::Lambda::Function
   1     Type: AWS::SSM::Parameter::Value<String>
   1     Type: Custom::VpcRestrictDefaultSG
```

### 必要最小限のリソースに絞る

* NATGatewayは不要
* シングルサブネット（Publicサブネットオンリー）、シングルAZで作成

```ts
const vpc = new ec2.Vpc(this, 'SampleCdkEc2NginxVpc', {
    maxAzs: 1,
    subnetConfiguration: [
        {
            cidrMask: 24,
            name: 'public',
            subnetType: ec2.SubnetType.PUBLIC
        }
    ],
    vpcName: 'web-service'
});
```

大分マシになった。
NATGatewayもないので、リソースを維持していたとして、コスト上も大きく影響するものはない。

```bash
$ cdk synth > out2.yml
$ grep 'Type:' out2.yml|sort|uniq -c
   1     Type: AWS::CDK::Metadata
   1     Type: AWS::EC2::InternetGateway
   1     Type: AWS::EC2::Route
   1     Type: AWS::EC2::RouteTable
   1     Type: AWS::EC2::Subnet
   1     Type: AWS::EC2::SubnetRouteTableAssociation
   1     Type: AWS::EC2::VPC
   1     Type: AWS::EC2::VPCGatewayAttachment
   1     Type: AWS::IAM::Role
   1     Type: AWS::Lambda::Function
   1     Type: AWS::SSM::Parameter::Value<String>
   1     Type: Custom::VpcRestrictDefaultSG
```

### デプロイしておく
* 今回のリソースの中にLambdaが存在しているが、デフォルトで作成されるセキュリティグループを操作するためのもののよう。
  * 今回は深ぼらない

```bash
$ cdk deploy
# 略
# ✨  Total time: 65.61s
```

## EC2作成
* [class Instance (construct) · AWS CDK](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Instance.html)

### 再びEC2作成に必要な最低限のコードを追記する

```ts
// EC2
const instance = new ec2.Instance(this, 'SampleCdkEc2NginxInstance', {
    vpc,
    instanceType: new ec2.InstanceType('t3a.micro'),
    machineImage: ec2.MachineImage.latestAmazonLinux2023(),
});
```


### 作成される予定のリソースを確認する

今回は既存スタックへの追加なので`cdk diff` で差分を確認してみる
作成される予定のリソースは以下。

* AWS::EC2::SecurityGroup
* AWS::IAM::Role
* AWS::IAM::InstanceProfile
    * あまり馴染みが無いが、EC2インスタンスにIAMロールを紐づけるためのもの。
* AWS::EC2::Instance

```bash
$ cdk diff
# 略
Resources
[+] AWS::EC2::SecurityGroup SampleCdkEc2NginxInstance/InstanceSecurityGroup SampleCdkEc2NginxInstanceInstanceSecurityGroup0DD5BDE2
[+] AWS::IAM::Role SampleCdkEc2NginxInstance/InstanceRole SampleCdkEc2NginxInstanceInstanceRole7F91D057
[+] AWS::IAM::InstanceProfile SampleCdkEc2NginxInstance/InstanceProfile SampleCdkEc2NginxInstanceInstanceProfile2CB6865B
[+] AWS::EC2::Instance SampleCdkEc2NginxInstance SampleCdkEc2NginxInstance262C66EB
```
### デプロイしておく

```bash
$ cdk deploy
# 略
# ✨  Total time: 183.62s
```

### EC2ができたことを確認

```bash
$ aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId,InstanceType,State.Name,PublicIpAddress]' --output text
# i-0dae143923cb3d2b2     t3a.micro       running 52.69.50.147
```

## CFnスタックを削除

```bash
$ cdk destroy
#  ✅  SampleCdkEc2NginxStack: destroyed
```

## 終わりに
ここまでやって、ネットワーク周りの構築とEC2で素のAmazonLinux2023を立ち上げることができた。
ただ、これではEC2に入って何かをすることができない。
次回はsession manager経由でシェルに入れるようにする。