インフラ設計者がAWS CDKに入門してみる②～構築編～

# 前提など

構築の前提や準備などは前回の[記事](https://qiita.com/nnydtmg157/items/688348b64fa8325b8f9d)を参照してください。


# 作成したコード

ひとまず完成したコードがこちらです。
L1コンストラクトで書いてます。コンストラクトについては後日書きたいと思います。
細かく設定したいのでL1を利用しましたが、L2、L3と抽象度が上がるにつれて記載するコードが少なくなっていきます。
（ファイル名は構成とずれてます、、、）


```typescript:lib/aws-1vpc-3private-subnet-stack.ts
import * as cdk from '@aws-cdk/core';
import {
   CfnVPC,
   CfnSubnet,
   CfnInternetGateway,
   CfnVPCGatewayAttachment,
   CfnRouteTable,
   CfnRoute,
   CfnSubnetRouteTableAssociation, 
   VpcEndpoint
  } from '@aws-cdk/aws-ec2';

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

    const systemName = this.node.tryGetContext('systemName');
    const envType = this.node.tryGetContext('envType');

    //VPC
    const vpc = new CfnVPC(this, 'Vpc', {
      cidrBlock: '10.0.0.0/16',
      tags: [{ key: 'Name', value: `${systemName}-${envType}-vpc` }]
    });

    //InternetGateway
    const igw = new CfnInternetGateway(this, 'igw', {
    })
    new CfnVPCGatewayAttachment(this, 'igwAttachment', {
      internetGatewayId: igw.ref,
      vpcId: vpc.ref
    })

    //PublicSubnet
    const subnetPublic1a = new CfnSubnet(this, 'SubnetPublic1a', {
      cidrBlock: '10.0.0.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1a',
      mapPublicIpOnLaunch: true,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-public-subnet-1a-1` }]
    })
    const subnetPublic1c = new CfnSubnet(this, 'SubnetPublic1c', {
      cidrBlock: '10.0.1.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1c',
      mapPublicIpOnLaunch: true,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-public-subnet-1c-1` }]
    })
    const subnetPublic1d = new CfnSubnet(this, 'SubnetPublic1d', {
      cidrBlock: '10.0.2.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1d',
      mapPublicIpOnLaunch: true,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-public-subnet-1d-1` }]
    })

    //PrivateSubnet
    const subnetPrivate1a1 = new CfnSubnet(this, 'SubnetPrivate1a1', {
      cidrBlock: '10.0.10.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1a',
      mapPublicIpOnLaunch: false,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-private-subnet-1a-1` }]
    })
    const subnetPrivate1a2 = new CfnSubnet(this, 'SubnetPrivate1a2', {
      cidrBlock: '10.0.20.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1a',
      mapPublicIpOnLaunch: false,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-private-subnet-1a-2` }]
    })
    const subnetPrivate1c1 = new CfnSubnet(this, 'SubnetPrivate1c1', {
      cidrBlock: '10.0.11.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1c',
      mapPublicIpOnLaunch: false,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-private-subnet-1c-1` }]
    })
    const subnetPrivate1c2 = new CfnSubnet(this, 'SubnetPrivate1c2', {
      cidrBlock: '10.0.21.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1c',
      mapPublicIpOnLaunch: false,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-private-subnet-1c-2` }]
    })
    const subnetPrivate1d1 = new CfnSubnet(this, 'SubnetPrivate1d1', {
      cidrBlock: '10.0.12.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1d',
      mapPublicIpOnLaunch: false,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-private-subnet-1d-1` }]
    })
    const subnetPrivate1d2 = new CfnSubnet(this, 'SubnetPrivate1d2', {
      cidrBlock: '10.0.22.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1d',
      mapPublicIpOnLaunch: false,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-private-subnet-1d-2` }]
    })

    //RouteTable
    const publicRouteTable = new CfnRouteTable(this, 'PublicRouteTable', {
      vpcId: vpc.ref,
      tags: [{
        key: 'Name',
        value: `${systemName}-${envType}-Public-RouteTable`,
      }]
    })
    new CfnRoute(this, 'PublicRoute', {
      routeTableId: publicRouteTable.ref,
      destinationCidrBlock: '0.0.0.0/0',
      gatewayId: igw.ref,
    })
    new CfnSubnetRouteTableAssociation(this, 'PublicSubnet1aRouteTableAssociation', {
      routeTableId: publicRouteTable.ref,
      subnetId: subnetPublic1a.ref
    })
    new CfnSubnetRouteTableAssociation(this, 'PublicSubnet1cRouteTableAssociation', {
      routeTableId: publicRouteTable.ref,
      subnetId: subnetPublic1c.ref
    })
    new CfnSubnetRouteTableAssociation(this, 'PublicSubnet1dRouteTableAssociation', {
      routeTableId: publicRouteTable.ref,
      subnetId: subnetPublic1d.ref
    })

    const privateRouteTable = new CfnRouteTable(this, 'privateRouteTable', {
      vpcId: vpc.ref,
      tags: [{
        key: 'Name',
        value: `${systemName}-${envType}-Private-RouteTable`,
      }]
    })
    new CfnSubnetRouteTableAssociation(this, 'PrivateSubnet1a1RouteTableAssociation', {
      routeTableId: privateRouteTable.ref,
      subnetId: subnetPrivate1a1.ref
    })
    new CfnSubnetRouteTableAssociation(this, 'PrivateSubnet1c1RouteTableAssociation', {
      routeTableId: privateRouteTable.ref,
      subnetId: subnetPrivate1c1.ref
    })
    new CfnSubnetRouteTableAssociation(this, 'PrivateSubnet1d1dRouteTableAssociation', {
      routeTableId: privateRouteTable.ref,
      subnetId: subnetPrivate1d1.ref
    })
    new CfnSubnetRouteTableAssociation(this, 'PrivateSubnet1a2RouteTableAssociation', {
      routeTableId: privateRouteTable.ref,
      subnetId: subnetPrivate1a2.ref
    })
    new CfnSubnetRouteTableAssociation(this, 'PrivateSubnet1c2RouteTableAssociation', {
      routeTableId: privateRouteTable.ref,
      subnetId: subnetPrivate1c2.ref
    })
    new CfnSubnetRouteTableAssociation(this, 'PrivateSubnet1d2dRouteTableAssociation', {
      routeTableId: privateRouteTable.ref,
      subnetId: subnetPrivate1d2.ref
    })
  }
}
```

細かく見ていきます。

## インポート

各種パッケージをインポートしています。
ここでインポートするためには、事前にディレクトリ内でnpm installします。ここでは、`npm install @aws-cdk/aws-ec2`が必要です。

```typescript
import * as cdk from '@aws-cdk/core';
import {
   CfnVPC,
   CfnSubnet,
   CfnInternetGateway,
   CfnVPCGatewayAttachment,
   CfnRouteTable,
   CfnRoute,
   CfnSubnetRouteTableAssociation, 
   VpcEndpoint
  } from '@aws-cdk/aws-ec2';
```

## スタック定義

ここからがスタック定義になります。
`cdk.Stack`を継承した`Aws1Vpc3PrivateSubnetStack`クラスに構築したいリソースを定義していきます。
デプロイすると`Aws1Vpc3PrivateSubnetStack`というCfnスタックが作成されます。
`constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {super(scope, id, props);`でコンストラクトの定義をしていますが、基本的にはそのままでも問題ないです。
最後の2行は変数を定義しています。ここで参照される`systemName`と`envType`は`cdk.json`の`context`内で定義します。

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

    const systemName = this.node.tryGetContext('systemName');
    const envType = this.node.tryGetContext('envType');
```

## VPC

VPCリソースはCidrBlockとTagを指定しています。

```typescript
    const vpc = new CfnVPC(this, 'Vpc', {
      cidrBlock: '10.0.0.0/16',
      tags: [{ key: 'Name', value: `${systemName}-${envType}-vpc` }]
    });
```

## InternetGateway

igwインスタンスを作成して、VPCにアタッチします。

```typescript
    const igw = new CfnInternetGateway(this, 'igw', {
    })
    new CfnVPCGatewayAttachment(this, 'igwAttachment', {
      internetGatewayId: igw.ref,
      vpcId: vpc.ref
    })
```


## Subnet

基本的にパブリックもプライベートも同じ定義ですが、`mapPublicIpOnLaunch`のみ`true`がパブリック、`false`がプライベートです。指定しないとfalseが設定されます。

```typescript
    const subnetPublic1a = new CfnSubnet(this, 'SubnetPublic1a', {
      cidrBlock: '10.0.0.0/24',
      vpcId: vpc.ref,
      availabilityZone: 'ap-northeast-1a',
      mapPublicIpOnLaunch: true,
      tags: [{ key: 'Name', value: `${systemName}-${envType}-public-subnet-1a-1` }]
    })
```

## RouteTable

RouteTableの作成には、RouteTable、Route、Associationの3つの要素があります。

```typescript
    const publicRouteTable = new CfnRouteTable(this, 'PublicRouteTable', {
      vpcId: vpc.ref,
      tags: [{
        key: 'Name',
        value: `${systemName}-${envType}-Public-RouteTable`,
      }]
    })
    new CfnRoute(this, 'PublicRoute', {
      routeTableId: publicRouteTable.ref,
      destinationCidrBlock: '0.0.0.0/0',
      gatewayId: igw.ref,
    })
    new CfnSubnetRouteTableAssociation(this, 'PublicSubnet1aRouteTableAssociation', {
      routeTableId: publicRouteTable.ref,
      subnetId: subnetPublic1a.ref
    })
```

localへのRoute定義以外が必要な場合はRoute定義が必要ですが、必要ない（プライベートサブネットにアタッチする）場合は、Route定義は不要です。

```typescript
    const privateRouteTable = new CfnRouteTable(this, 'privateRouteTable', {
      vpcId: vpc.ref,
      tags: [{
        key: 'Name',
        value: `${systemName}-${envType}-Private-RouteTable`,
      }]
    })
    new CfnSubnetRouteTableAssociation(this, 'PrivateSubnet1a1RouteTableAssociation', {
      routeTableId: privateRouteTable.ref,
      subnetId: subnetPrivate1a1.ref
    })
```


# 最後に
今回作成したリソースはデプロイしても料金のかからないものばかりなので、CDKの勉強にはちょうど良いかなと思っています。
実際は疎通確認用のEC2やNATGatewayを含んだスタックにする方が、利用用途は多くなると思います。

今後はそういったリソースもチャレンジしていきたいと思います。TypeScript初心者なので、コードが汚いのはご容赦ください。

今回作成したコードは[こちら](https://github.com/nnydtmg/aws-1vpc-3private-subnet.git)です。