Pythonで書くAWS CDK ～既存VPCを使ったVPCエンドポイント作成～

# 概要

AWS CDK はAWS上にクラウドサービスを構築するための便利なソフトウェアです。

個人で検証している際は特に、以下の点で便利だと感じています。

* 課金抑止
    * 検証したいときに作り、検証が終わったときに削除できる
    * 削除コマンドを実行すれば、消し忘れることがない
* 作業効率化
    * 同じ環境をすぐに再現できる


この記事ではPythonを使って、AWS CDKによるクラウドサービス構築を行う方法を記載していきます。

# 前提条件

この記事では以下の環境を使用しています。

OS：Windows10
言語：Python3.7

# CDKを始めるための準備

## node.jsをWindows10へインストールする

### NVM for Windows のダウンロードとインストール

CDKはnode.jsを使用するため、CDKを動作させる環境へnode.jsをインストールする。
ここでは、node.jsを直接インストールするのではなく、node.jsをバージョン管理してくれるnvmをインストールし、そこからnode.jsをインストールするようにした。

これは、node.js自体のバージョンアップが素早く、node.jsのバージョン管理も後々必要になってくると想定されるためだ。
※CDK自体もnode.jsのバージョンに依存している

Windows環境へnvmをインストールするためには、[NVM for Windows](https://github.com/coreybutler/nvm-windows#installation--upgrades)を使用する。

GitHubのページへアクセスして、以下の **Download Now!** を選択する。
![node.js ダウンロード1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302006/cd1b5a82-4190-1624-a47f-aad44a88ca8d.png)

基本的には **setup.exe** をダウンロードして、実行すれば良い。
![node.js ダウンロード2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302006/88d4e404-c3b2-7d9c-6648-eb3e1f993eb5.png)

Powershellを実行して `nvm --version` と実行する。

```Powershell
nvm --version
```

バージョンが出力されればインストールが成功している。
```Powershell:出力
Running version 1.1.9.
```

### node.jsのインストール

まず、使用可能なnode.jsのバージョンを確認する。

```Powershell
nvm list available
```

```Powershell:出力
|   CURRENT    |     LTS      |  OLD STABLE  | OLD UNSTABLE |
|--------------|--------------|--------------|--------------|
|    19.3.0    |   18.12.1    |   0.12.18    |   0.11.16    |
|    19.2.0    |   18.12.0    |   0.12.17    |   0.11.15    |
|    19.1.0    |   16.19.0    |   0.12.16    |   0.11.14    |
|    19.0.1    |   16.18.1    |   0.12.15    |   0.11.13    |
|    19.0.0    |   16.18.0    |   0.12.14    |   0.11.12    |
|   18.11.0    |   16.17.1    |   0.12.13    |   0.11.11    |
|   18.10.0    |   16.17.0    |   0.12.12    |   0.11.10    |
|    18.9.1    |   16.16.0    |   0.12.11    |    0.11.9    |
|    18.9.0    |   16.15.1    |   0.12.10    |    0.11.8    |
|    18.8.0    |   16.15.0    |    0.12.9    |    0.11.7    |
|    18.7.0    |   16.14.2    |    0.12.8    |    0.11.6    |
|    18.6.0    |   16.14.1    |    0.12.7    |    0.11.5    |
|    18.5.0    |   16.14.0    |    0.12.6    |    0.11.4    |
|    18.4.0    |   16.13.2    |    0.12.5    |    0.11.3    |
|    18.3.0    |   16.13.1    |    0.12.4    |    0.11.2    |
|    18.2.0    |   16.13.0    |    0.12.3    |    0.11.1    |
|    18.1.0    |   14.21.2    |    0.12.2    |    0.11.0    |
|    18.0.0    |   14.21.1    |    0.12.1    |    0.9.12    |
|    17.9.1    |   14.21.0    |    0.12.0    |    0.9.11    |
|    17.9.0    |   14.20.1    |   0.10.48    |    0.9.10    |

This is a partial list. For a complete list, visit https://nodejs.org/en/download/releases
```

安定している最新バージョンをインストールしたいため、LTS列の一番上のバージョンを指定してインストールしよう。

```Powershell
nvm install 18.12.1
```

インストールされたことを確認するために`nvm list`を実行する。

```Powershell
nvm list
```
```Powershell:出力
18.12.1
```

最後に使用するnode.jsのバージョンを指定するために`nvm use`コマンドを実行する。

```Powershell
nvm use 18.12.1
```

確認してみる。

```Powershell
nvm list
```

```Powershell:出力
  * 18.12.1 (Currently using 64-bit executable)
```
このように`*`がついていれば大丈夫だ。

## AWSへアクセスするCDK用のアクセスキーの作成

CDK用のIAMユーザーでアクセスキーを発行する。

1. AWSコンソールへログインする。
1. IAMのページを開く。
1. 対象のIAMユーザーをクリックする。
1. 認証情報タブを開く。
1. アクセスキーの作成ボタンを押す。

## aws configの設定

既定で使用されるプロファイルの設定を行うために、`%USERPROFILE%\.aws\config`ファイルを作成する。
※%USERPROFILE%は`C:\Users\hogehoge`

```config:config
[default]
region=ap-northeast-1
```

ここではリージョンとして東日本を設定。

## aws credentialsの設定

defaultプロファイルを使う際の認証を行うために、` %USERPROFILE%\.aws\credentials`ファイルを作成する。

```config:credentials
[default]
aws_access_key_id=【自分のキーID】
aws_secret_access_key=【自分のキー名】
```

## CDKをインストールする

Powershell上で以下を実行する。

```
npm install -g aws-cdk
```

バージョンを確認して表示されればOKだ。
```Powershell
cdk --version
```

## CDK Bootstrapの実行

CDKを実行するためには、CDKを使ってサービスを構築するAWSアカウント上に、S3とIAMロールが必要だ。
CloudFormation（CDKToolKitという名前）で作成される。

これらを作成するために`cdk bootstrap`コマンドを実行する。

```Powershell
cdk bootstrap aws://【自分のAWSアカウント番号】/【リージョン】
```

# CDK アプリケーションの作成

## 初期セットアップ

CDK用にフォルダを作成する。ここでは`test`フォルダを使う。
Powershellでフォルダ内へ移動したら、初期セットアップのために以下のコマンドを実行する。

```Powershell
cdk init app --language python
```

```Powershell:出力
Applying project template app for python

# Welcome to your CDK Python project!

This is a blank project for CDK development with Python.

The `cdk.json` file tells the CDK Toolkit how to execute your app.

This project is set up like a standard Python project.  The initialization
process also creates a virtualenv within this project, stored under the `.venv`
directory.  To create the virtualenv it assumes that there is a `python3`
(or `python` for Windows) executable in your path with access to the `venv`
package. If for any reason the automatic creation of the virtualenv fails,
you can create the virtualenv manually.

～～中略～～

✅ All done!
```

コマンド実行後には以下のようなフォルダとファイルが作成されている。

```Powershell:testフォルダ
.venv
test
tests
.gitignore
app.py
cdk.json
README.md
requirements-dev.txt
requirements.txt
source.bat
```

次にPythonの仮想環境を起動して、必要なPythonパッケージをpipでインストールする。

```Powershell:Python仮想環境のActivate（testフォルダ内で実行）
.\.venv\Scripts\activate
```

```Powershell:Pythonパッケージをインストール
python -m pip install -r requirements.txt
```

## app.pyファイル内でデプロイ先となる環境を設定

生成された`app.py`のコメント文を削除して`env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),`だけアンコメントする。

これは現在OS上に設定された環境変数を読み取ってくれる。

```Python:app.py
#!/usr/bin/env python3
import os

import aws_cdk as cdk

from test.test_stack import TestStack


app = cdk.App()
TestStack(app, "TestStack",
    env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),
    )

app.synth()
```

# チュートリアル　既存VPCと既存サブネットへECRのVPCエンドポイント作成

VPCやサブネットなど変更が少ないAWS上のリソースは、既に作成されていることが多いだろう。
その場合を想定し、既存のリソースを使いつつ、CDKでサービスをデプロイする方法を記載する。

```Python:test_stack.py
from aws_cdk import (
    Stack,
    aws_ec2,
)
from constructs import Construct


class TestStack(Stack):

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

        # CDKで管理していない既存のVPCとサブネットを定義する。
        vpc = aws_ec2.Vpc.from_lookup(self, id="test-vpc", vpc_id="VPCIDを入力")
        subnet_filter = aws_ec2.SubnetFilter.by_ids(["サブネットIDを入力"])
        security_group = aws_ec2.SecurityGroup.from_lookup_by_id(
            self, id="test-sg", security_group_id="セキュリティグループIDを入力"
        )

        interface_vpc_endpoint_api = aws_ec2.InterfaceVpcEndpoint(
            self, "VPC Endpoint api",
            private_dns_enabled=True,
            vpc=vpc,
            service=aws_ec2.InterfaceVpcEndpointService(
                "com.amazonaws.ap-northeast-1.ecr.api", 443
            ),
            subnets=aws_ec2.SubnetSelection(subnet_filters=[subnet_filter]),
            security_groups=[security_group],
        )
```

## 既に作成されているAWSリソースの定義

まず、VPCとサブネット、セキュリティグループを定義する。これらは既に作られていることを想定している。

```Python
vpc = aws_ec2.Vpc.from_lookup(self, id="test-vpc", vpc_id="VPCIDを入力")
subnet_filter = aws_ec2.SubnetFilter.by_ids(["サブネットIDを入力"])
security_group = aws_ec2.SecurityGroup.from_lookup_by_id(
    self, id="test-sg", security_group_id="セキュリティグループIDを入力"
)
```

**aws_ec2.Vpc.from_lookup**

https://docs.aws.amazon.com/cdk/api/v1/python/aws_cdk.aws_ec2/Vpc.html#aws_cdk.aws_ec2.Vpc.from_lookup

**aws_ec2.SubnetFilter.by_ids**

https://docs.aws.amazon.com/cdk/api/v1/python/aws_cdk.aws_ec2/SubnetFilter.html#aws_cdk.aws_ec2.SubnetFilter.by_ids

**aws_ec2.SecurityGroup.from_lookup_by_id**

https://docs.aws.amazon.com/cdk/api/v1/python/aws_cdk.aws_ec2/SecurityGroup.html#aws_cdk.aws_ec2.SecurityGroup.from_lookup_by_id

## VPCエンドポイントの定義

今回は例としてecrのapiエンドポイントを作成する。

```Python
interface_vpc_endpoint_api = aws_ec2.InterfaceVpcEndpoint(
    self, "VPC Endpoint api",
    private_dns_enabled=True,
    vpc=vpc,
    service=aws_ec2.InterfaceVpcEndpointService(
        "com.amazonaws.ap-northeast-1.ecr.api", 443
    ),
    subnets=aws_ec2.SubnetSelection(subnet_filters=[subnet_filter]),
    security_groups=[security_group],
)
```

**aws_ec2.InterfaceVpcEndpoint**

https://docs.aws.amazon.com/cdk/api/v1/python/aws_cdk.aws_ec2/InterfaceVpcEndpoint.html

## CDK によるデプロイ

以下のコマンドを`test`フォルダで実行して、VPCエンドポイントを作成する。

```Powershell
cdk deploy
```

## CDK によるVPCエンドポイントの削除

以下のコマンドを`test`フォルダで実行して、VPCエンドポイントを削除する。

```Powershell
cdk destroy
```

# 参考資料

https://learn.microsoft.com/ja-jp/windows/dev-environment/javascript/nodejs-on-windows

https://aws.amazon.com/jp/premiumsupport/knowledge-center/create-access-key/

https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html

https://docs.aws.amazon.com/cdk/api/v1/python/index.html