AWS CDKでAmazon VPCを作ってみる

# はじめに
今までCloud Formationを利用することが多かったのですが、検証にあたり、AWS CDKの知識が必要になったため、本記事では私が調べた内容をまとめていきたいと思います。

# 想定読者
Cloud Formationとpythonの基礎知識がある方を対象としています。

# AWS CDKとは
AWS CDKは、好みのプログラミング言語を使用してAWSリソースを定義できるオープンソースのソフトウェア開発フレームワークです。2024年7月1日時点ではTypeScript、JavaScript、Python、Java、C#、Goに対応しています。

AWS CDKを利用することによるメリットは色々ありますが、大きいのは以下2点でしょう。

* 型の抽象化：AWS CDKではリソースを抽象化して記載することが可能です。Cloud Formationの場合は、リソースのプロパティを細かく指定する必要がありますが、AWS CDKではリソースを抽象化することができます。そのため、細かいプロパティを指定せずにデプロイが可能です。
* プログラミング言語による記述：使い慣れたプログラミング言語でリソースの指定が可能です。プログラミング言語で記述できるため、条件分岐やループなどの処理が可能です。

# AWS CDKのコンポーネント
AWS CDKのコンポーネントは、大きく3つに分かれています。
[※図は公式ドキュメントより抜粋](https://docs.aws.amazon.com/cdk/v2/guide/home.html)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3075801/47f00ada-2f65-b1a6-8c5a-cef1374c2e60.png)

* App：アプリケーション全体を定義するコンポーネントです。アプリケーションは複数リージョンをまたぐことができます。
* Stack(s)：デプロイ可能な最少単位です。Cloud Formationのスタックに対応しています。
* Construct：AWS CDKのビルディングブロックです。1つ以上のAWSリソースを定義します。

# Constructs Level
AWS CDKではリソースを抽象化して記述することができます。Constructs Levelという概念があり、L1、L2、L3のレベルを提供しています。L1に近いほど細かいプロパティを指定可能で、L3に近いほどリソースを抽象化することができます。そのため、ニーズに応じたレベルを選択して、コードの記述が可能です。

## L1 Constructs
Cloud Formationのように、プロパティを細かく指定したい場合に利用します。L1 Constructsは、CloudFormationのプロパティと1対1で対応します。「CfnBucket」や「CfnFunction」など、名前が「Cfn」から始まるのが特徴です。

こちらは、S3を作成するコードのサンプルです。詳細は省きますが、s3.CfnBucketでリソースを定義しており、細かいプロパティを指定していることがわかります。
```python:L1_sample
from aws_cdk import (
    Stack,
    aws_s3 as s3
)
from constructs import Construct

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

        s3.CfnBucket(self, "MyL1Bucket",
            bucket_name="my-l1-bucket",
            versioning_configuration=s3.CfnBucket.VersioningConfigurationProperty(
                status="Enabled"
            )
        )
```

## L2 Constructs
AWSサービスの一般的な使用パターンを抽象化したConstructsです。デフォルト値やベストプラクティスが組み込まれているため、細かいプロパティを設定せずに利用することができます。

こちらは、S3を作成するコードのサンプルです。L1に比べて暗号化やバージョニングなどを、より直感的に記述できます。
```python:L2_sample
from aws_cdk import (
    Stack,
    aws_s3 as s3,
    RemovalPolicy
)
from constructs import Construct

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

        s3.Bucket(self, "MyL2Bucket",
            bucket_name="my-l2-bucket",
            versioned=True,
            encryption=s3.BucketEncryption.S3_MANAGED,
            removal_policy=RemovalPolicy.DESTROY
        )
```

## L3 Constructs
複数のリソースを含む⼀般的な構成パターンを抽象化したConstructsです。

こちらは、S3を利用して静的ウェブサイトをホスティングするサンプルコードです。1つのConstructsでウェブサイトのホスティングに必要なリソースの作成や設定を実装できます。
```python:L3_sample
from aws_cdk import (
    Stack,
    aws_s3_deployment as s3deploy
)
from constructs import Construct

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

        website = s3deploy.BucketDeployment(self, "DeployWebsite",
            sources=[s3deploy.Source.asset("./website")],
            destination_bucket=s3.Bucket(self, "WebsiteBucket",
                website_index_document="index.html",
                public_read_access=True
            )
        )
```

# やってみる
今回は、L1 Constructsを利用して、Amazon VPCの作成してみます。言語はpythonを利用します。

## ローカル環境の事前準備
pythonを使ってAWS CDKを利用するには、下記を準備する必要があります。詳細は、[公式ドキュメント](python:L1_sample)をご確認ください。
| 必要なソフトウェア | 用途 |
|:-:|:-:|
| Python 3.7以上（pip、virtualenv含む）  | プログラミング言語 |
| AWS CLI v2  | AWSの認証情報管理に利用  |
| Node.js 14.15.0以上  | AWS CDK CLIの実行環境 |
| AWS CDK CLI  | AWS CDKのコマンドライン |

インストール方法については、わかりやすい記事や公式ドキュメントが多数ありますので、各自の環境に合わせてセットアップしてください。

## 認証情報の設定
AWS CDKを利用するためには、認証情報の設定が必要です。そのため、事前にアクセスキーを取得しておきます。コマンドプロンプトを立ち上げ、AWS CLIのaws configureコマンドを実行します。コマンドを実行すると、情報入力を促されるので、アクセスキーの情報などを入力します。
```
aws configure
```
情報を入力すると、自動的にconfig、credentialという名前のファイル（AWS CDKで利用する認証情報）が作成されます。本ファイルを手動で作成・編集することも可能です。

:::note info
参考
Windowsの場合は、「%USERPROFILE%\\.aws」にファイルが作成されます。
:::
 
## BootStrap
AWS CDKでは、リソースをデプロイするアカウント（リージョン）でBootStrapという処理が必要です。コマンドプロンプトで下記コマンドを実行します。
```
cdk bootstrap aws://<accountID>/<region>
```

コマンドを実行すると対象のアカウント（リージョン）で「CDKToolkit」という名前のCloud Formationスタックが作成されます。本スタックでは、AWS CDKの実行に必要なIAMロール、S3バケット、ECRリポジトリが作成されます。

## Appの作成
Appを作成します。本記事では、「sample-project」フォルダを作成し、Appを作成するためのコマンドを実行します。
```
mkdir sample-project
cd sample-project
cdk init app -l python 
```

コマンドを実行すると、いくつかのフォルダとファイルが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3075801/eb0f4f77-54d2-cf05-9850-6c4748ec1f42.png)

主要なフォルダとファイルについて、解説します。
* .venvフォルダ：pythonのvirtualenv(仮想環境)と同じ環境が作成されます。Scriptsフォルダ配下のactivateコマンドにより、仮想環境を立ち上げることができます。
* sample_project_stack.py：メインのスタック定義ファイルです。ここに、作成したいリソースを定義します。名前はフォルダ名に依存します。
* requirements.txt：Pythonの依存パッケージが記載されたファイルです。

## 仮想環境の準備
まず、仮想環境を立ち上げます。私は、PowerShellを利用しているので、「Activate.ps1」を実行します。
```
.\.venv\Scripts\Activate.ps1
```

仮想環境を起動した後、pipのアップグレードと、必要なパッケージをインストールします。
```
python -m pip install --upgrade pip
pip install -r requirements.txt
```

## リソースの記述
AWSの提供している[リファレンス](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html)を参考に、リソースを定義していきます。今回はVPCを作成したいので、最初にaws_ec2のライブラリを確認します。[Overview](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html)から必要なモジュールを確認することができます。pythonでは、「aws_cdk.aws_ec2」が必要なので、「sample_project_stack.py」のimport部分に追記します。
※デフォルトで記述されているコメントは削除しています
```python:sample_project_stack.py
from aws_cdk import (
    Stack,
    aws_ec2 as ec2 # 追記
)
from constructs import Construct

class SampleProjectStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
```
クラスの書き方については、[CfnVPC](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.CfnVPC.html)から確認します。pythonが選択されていることを確認し、VPC、インターネットゲートウェイ、インターネットゲートウェイアタッチメントを記述します。
```python:sample_project_stack.py
from aws_cdk import (
    Stack,
    aws_ec2 as ec2
)
from constructs import Construct

class SampleProjectStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
 
        # 追記
        my_vpc = ec2.CfnVPC(
            self, "MyVPC",
            cidr_block="10.0.0.0/16",
            enable_dns_hostnames=True,
            enable_dns_support=True
        )
        internet_gateway = ec2.CfnInternetGateway(
            self, "InternetGateway"
        )
        ec2.CfnVPCGatewayAttachment(
            self, "VPCGatewayAttachment",
            vpc_id=my_vpc.attr_vpc_id,
            internet_gateway_id=internet_gateway.attr_internet_gateway_id
        )
```

## デプロイ
仮想環境が有効になっていることを確認して、デプロイコマンドを実行します。
```
cdk deploy
```

デプロイコマンドを実行すると、アプリケーションフォルダ配下に「cdk.out」フォルダが作成されます。「cdk.out」フォルダ配下には、Cloud Formationテンプレートが作成されます。また、AWS上でCloud Formationがデプロイされ、コードで定義したリソース（今回はVPC）が作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3075801/26154f06-2d7d-48cd-883b-fe09247603f5.png)


## リソース削除
デプロイしたリソースを削除するには、下記コマンドを実行します。
```
cdk destroy
```
:::note info
参考
本コマンドの動作は、Cloud Formationを手動削除するのと似ていますが、空でないS3バケットも削除できるなど、微妙な違いがあります。
:::

# おわりに
最後まで読んでいただき、ありがとうございます。気に入っていただけたら「いいね！」してもらえると嬉しいです！

# 参考URL
https://docs.aws.amazon.com/cdk/v2/guide/home.html
https://docs.aws.amazon.com/cdk/api/v2/
https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AWS-CDK-Basic-1-Overview_0731_v1.pdf