AWS CDKでらくらくネットワーク構築

この記事はセゾン情報システムズ [Advent Calendar 2021](https://qiita.com/advent-calendar/2021/sisco) 20日目の記事です。

#はじめに
AWS CDKはCloudFromation(cfn)のテンプレートをプログラミング言語で記述でき、
AWS CDKのコマンドでcfnのスタックの作成・更新・削除が行えるツールです。
https://aws.amazon.com/jp/cdk/

今回はWindows環境でAWS CDKとpythonを使用しVPCを構築してみようと思います。
そして、contextオプションを使用し、環境ごとにAWSリソースの値が異なる場合でも1つのソースコードで管理する方法を記載したいと思います。


cdkの導入方法は以下を参照してください。
https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/getting_started.html#getting_started_prerequisites

AWS CDKはバージョン2を使用します

```
>cdk version
2.2.0 (build 4f5c27c)
```

バージョン2はいろいろ便利になってるぽいです(詳細は割愛します)
https://aws.amazon.com/jp/about-aws/whats-new/2021/12/aws-cloud-development-kit-cdk-generally-available/




#VPC構築

それではさっそくVPC, Internet Gateway, NAT Gateway, Route Table, Subnetを作成していきたいと思います。


まず、適当にディレクトリを作成します。

```
>mkdir cdk-python& cd cdk-python
```

以下に従いコマンドを実行します(ログは長いので割愛)
https://cdkworkshop.com/30-python/20-create-project/100-cdk-init.html

```
>cdk init sample-app --language python
>.venv\Scripts\activate.bat
>pip install -r requirements.txt
```

cdk init実行後、`.\cdk_python\cdk_python_stack.py`が作成されていると思うので以下の様に編集します。
※ちょうどよい[テンプレ](https://github.com/aws-samples/aws-cdk-examples/blob/master/python/new-vpc-alb-asg-mysql/cdk_vpc_ec2/cdk_vpc_stack.py#L12-L34)がgithubに公開されていたのでコピってきました。

```{lst:cdk_python_stack.py}
from constructs import Construct
from aws_cdk import (
    Duration,
    Stack,
    #--- 追加 ---#
    CfnOutput,
    aws_ec2 as ec2
    #------------#
)

class CdkPythonStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # 追加
        self.vpc = ec2.Vpc(self, "VPC",
                           max_azs=2,
                           cidr="10.10.0.0/16",
                           # configuration will create 3 groups in 2 AZs = 6 subnets.
                           subnet_configuration=[ec2.SubnetConfiguration(
                               subnet_type=ec2.SubnetType.PUBLIC,
                               name="Public",
                               cidr_mask=24
                           ), ec2.SubnetConfiguration(
                               subnet_type=ec2.SubnetType.PRIVATE_WITH_NAT,
                               name="Private",
                               cidr_mask=24
                           ), ec2.SubnetConfiguration(
                               subnet_type=ec2.SubnetType.PRIVATE_ISOLATED,
                               name="DB",
                               cidr_mask=24
                           )
                           ],
                           # nat_gateway_provider=ec2.NatProvider.gateway(),
                           nat_gateways=2,
                           )
        CfnOutput(self, "Output",
                       value=self.vpc.vpc_id)
        #

```

以下のコマンドを実行し、CDKが利用するAWSリソースのデプロイを行います。

```
>cdk bootstrap
 ⏳  Bootstrapping environment aws://xxxxxxxxxxxx/ap-northeast-1...
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
 :
 :
 ✅  Environment aws://xxxxxxxxxxxx/ap-northeast-1 bootstrapped.

```

以下のcfnスタックが作成されると思います。
![cdk-tool-kit.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/6f7adc0f-902a-17b5-eefc-fd474668d839.png)




そして以下のコマンドを実行すると以下のcfnスタックが作成され、VPC等が構築されます

```
>cdk deploy
cdk-python: deploying...
[0%] start: Publishing ba4c2213578cfb0be75daf9d51461445e472292a2bbd872eb77a62059bf4412a:current_account-current_region
[100%] success: Published ba4c2213578cfb0be75daf9d51461445e472292a2bbd872eb77a62059bf4412a:current_account-current_region
cdk-python: creating CloudFormation changeset...
:
:
 ✅  cdk-python

Outputs:
cdk-python.Output = vpc-0ab9bd1270543add2

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/cdk-python/262f5400-600d-11ec-967e-0a27860de79b
```


AWSコンソールにログインしcfnを確認するとスタックが作成されており、
VPC, Internet Gateway, NAT Gateway, Route Table, Subnetが作成されます、めちゃ便利！

- cfnスタック
![cdk-python.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/5522333a-3683-8fdd-b110-bdf145e6761e.png)


- VPC
![cdk-vpc.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/895082bb-1bee-b093-f871-5448301f85f5.png)


- Subnet
![cdk-subnet.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/c65c6024-d2f2-6e85-754c-a38f28d57fb6.png)


- Route Table  
Internet Gateway, NAT Gatewayも作成されてますね。
![cdk-route1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/f0105ed5-0f0f-b5b4-d819-be732d02a29d.png)
![cdk-route2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/35ce3cc7-c503-f50d-795a-3a209728d81a.png)



削除する際は`cdk destroy`で作成したリソースをすべて削除できます。
ちなみに、`cdk synth`コマンドでcfnのテンプレの生成もできます



#context活用
開発をしていると、開発用環境、ステージング環境、本番環境といった複数環境構築することがあると思います。
環境ごとにリソース名を変えたい、VPCのCIDR変えたい、、、etc.
でも環境ごとにファイルを管理したくない、という場合活躍します。

contextは`cdk.json`またはcdkコマンド実行時の`--context`オプションでキーと値を指定することで使用できます。
今回は`cdk.json`と`--context`オプション両方を活用していきます。

`cdk.json`は`cdk init`実行時に自動で作成されているのでそれに以下の値を追加します

```{cdk.json}
      :
     (略)
      :
  "context": {
    # 追加
    "dev": {
      "envName": "develop",
      "cidr": "10.10.0.0/16"
    },
    "stg": {
      "envName": "staging",
      "cidr": "10.20.0.0/16"
    }
    #
  }
}
```

以下のコマンドを実行することでcontextで設定した値を参照できます.


```
>cdk context
Context found in cdk.json:

┌───┬─────┬──────────────────────────────────────────────────┐
│ # │ Key │ Value                                            │
├───┼─────┼──────────────────────────────────────────────────┤
│ 1 │ dev │ { "envName": "develop", "cidr": "10.10.0.0/16" } │
├───┼─────┼──────────────────────────────────────────────────┤
│ 2 │ stg │ { "envName": "staging", "cidr": "10.20.0.0/16" } │
└───┴─────┴──────────────────────────────────────────────────┘
```

また、--contextオプションを指定し、その時だけcontextを設定することもできます。

```
>cdk context --context env=dev
Context found in cdk.json:
┌───┬─────┬──────────────────────────────────────────────────┐
│ # │ Key │ Value                                            │
├───┼─────┼──────────────────────────────────────────────────┤
│ 1 │ dev │ { "envName": "develop", "cidr": "10.10.0.0/16" } │
├───┼─────┼──────────────────────────────────────────────────┤
│ 2 │ env │ "dev"                                            │
├───┼─────┼──────────────────────────────────────────────────┤
│ 3 │ stg │ { "envName": "staging", "cidr": "10.20.0.0/16" } │
└───┴─────┴──────────────────────────────────────────────────┘
```

```
>cdk context --context env=stg
Context found in cdk.json:
┌───┬─────┬──────────────────────────────────────────────────┐
│ # │ Key │ Value                                            │
├───┼─────┼──────────────────────────────────────────────────┤
│ 1 │ dev │ { "envName": "develop", "cidr": "10.10.0.0/16" } │
├───┼─────┼──────────────────────────────────────────────────┤
│ 2 │ env │ "stg"                                            │
├───┼─────┼──────────────────────────────────────────────────┤
│ 3 │ stg │ { "envName": "staging", "cidr": "10.20.0.0/16" } │
└───┴─────┴──────────────────────────────────────────────────┘
```

contextの値を取り出すときは、pythonの場合`self.node.try_get_context(key)`を使用します。

上記を踏まえ、`cdk_python_stack.py`を以下の様に修正します

```{lst:cdk_python_stack.py}
                           :
                          (略)
                           :
class CdkPythonStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # 追加・修正箇所
        envKey = self.node.try_get_context("env")
        envValue = self.node.try_get_context(envKey)

        self.vpc = ec2.Vpc(self, "VPC-"+envValue["envName"],
                           max_azs=2,
                           cidr=envValue["cidr"],
        #
                           :
                          (略)
                           :

```

以下のコマンドを実行し、AWSコンソールから作成されたリソースを確認します。

```
>cdk deploy --context env=stg
```


- VPCを例に確認   
stagingという値がリソース名に追加されており、CIDRも10.20.0.0/16になっておりcontextで設定した値が使用されております。  
これで環境ごとにファイルを用意する必要がなくなりますね。  
![cdk-vpc2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/37622ab8-951d-c6d1-879a-fcc742d9275f.png)


以下にAPIの情報が載っており、もちろんVPC以外にもAWSリソースの作成は行えます。
サンプルもあり、Python以外の言語でも使用できるので今後活用していければなと思います。
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html