AWS CDK初心者が爆速でS3とかlambdaとか「とりあえずそれっぽく」作る方法

# 概要
AWSをwebから初めて使うときって特に気になりませんが、少し慣れてくると
「これいちいち所望のアプリ見に行って設定直すのめんどくせえ、、、！！！！」、と思いませんか？~~（思いますよね？？？）~~

lambdaとS3を連携したりとかよくあると思うんですが、毎回どのlambdaだっけとか探すのめんどくさい。

これってなんとかならないの〜？？
ってことで今回はAWS CDKを使って設計をコード化してまとめてAWSにあげてみます。
コードでインフラストラクチャを定義できるようになりましょう！

初心者用なので、「とりあえずそれっぽく」を軸に話します。
イメージはこんな感じ。

![スクリーンショット 2022-08-31 22.36.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005334/33a4f94e-95c2-1804-7b89-75ec53553c90.png)



# AWS CDK
そもそもAWSCDKとは？となる人もいるので公式から説明を拝借。
> AWS Cloud Development Kit (AWS CDK) は、使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースを定義するためのオープンソースのソフトウェア開発フレームワークです。

要は自分の得意なプログラミング言語を使用して、アプリリソースを定義できちゃいますということ。
リソースの塊1つ1つはcloudformationにstackとしてデプロイされます！

[公式リファレンス](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html)のmoduleを確認してみると
|  Language |  Package  |
| ---- | ---- |
|   TypeScript  |  	aws-cdk-lib  |
|   Python  |  	aws_cdk  |
| Java | software.amazon.awscdk |
| Go | github.com/aws/aws-cdk-go/awscdk/v2 |
| .NET | Amazon.CDK |

上記言語で主に使用できるようです。

今回は、npmでCDKをインストールした後、python形式でlambda,S3をそれぞれ作成してみようと思います。

:::note warn
注意
AWSアカウントの発行、AWS-CLIの設定は既に行なっているものして話を進めます。
:::

## インストール
まずは以下のコマンドを実行します。

```sh:
$ npm install -g aws-cdk
```

インストールが正しく行われているか確認してみます。

```sh:
$ cdk --version

2.37.1 (build f15dee0)
```

version情報が正しく出ていればokです。

:::note warn
注意
AWS CDKにはv1,v2の公開があり、本記事ではv2を使用しています。
ネットの他記事情報はv1,v2の情報が混在しておりますので、自身でさらに学ぶ場合には注意が必要です。
:::

## プロジェクトの作成
空のディレクトリを作成して、移動します。
ディレクトリ名はそのままcdkのプロジェクト名になります。

```sh
$ mkdir project-name
$ cd project-name
```

対象のディレクトリに移動したら、`cdk init`でプロジェクトを作成します。
```sh
$ cdk init --language python
```

今回使用言語はpythonにしました。
`--language`以降を適宜使用したい言語に変更すれば、所望の言語でプロジェクトを作成できます。

作成されるプロジェクトの構成を以下に示しました。

```sh:作成されるプロジェクト
.
├── project-name
│   ├── project-name_stack.py
│   └── __init__.py
├── README.md
├── app.py
├── cdk.json
├── requirements-dev.txt
├── requirements.txt
├── source.bat
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_project-name_stack.py
```

pythonの仮想環境にはaws-cdk関連は入っていませんので、pipでinstallします。
既にプロジェクト作成時に`requirements.txt`が作成されていますので、こちらに沿ってインストールしましょう。


```sh
（.venv） $ pip install -r requirements.txt
```

`aws-cdk-lib` `constructs`がpip listで確認できれば成功です。

```sh
（.venv） $ pip list
Package           Version
----------------- --------
aws-cdk-lib       2.37.1
constructs        10.1.92
(以下省略)
```



## s3を書く
AWSのアプリケーションを作成していきます。
まずは手始めにs3を作成します。

AWSCDKではプロジェクト1つの塊をスタックとして管理します。
初期状態ではstackの定義は以下の通りです。

```python: project-name/project-name_stack.py
from aws_cdk import (
    # Duration,
    Stack,
    # aws_sqs as sqs,
)
from constructs import Construct

class ProjectNameStack(Stack):

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

        # The code that defines your stack goes here

        # example resource
        # queue = sqs.Queue(
        #     self, "20220830QiitaTestPjQueue",
        #     visibility_timeout=Duration.seconds(300),
        # )

```

各アプリケーションを作成するためには、`aws_cdk`ライブラリから要素をimportする必要があります。
今回は`aws_s3`をimportして作成します。（見やすいように初期のコメント削除しました。）

```python: project-name/project-name_stack.py
from aws_cdk import (
    Stack,
    aws_s3 as _s3,
    RemovalPolicy
)
from constructs import Construct

class ProjectNameStack(Stack):

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

        # S3の設定
        bucket = _s3.Bucket(
            self,"ProjectxxxBucket",
            bucket_name="project-xx-bucket",
            removal_policy=RemovalPolicy.DESTROY,  # スタック削除時、自動的にバケットを削除
        )

```

Bucketの中に必要な設定を行うことで、所望のs3を作成することができます。
今回はバケット名をproject-xx-bucketとし、 スタック削除時、自動的にバケットを削除するように設定しました。
このほかにもライフタイムのルールや、ポリシーの設定なども可能です。


:::note warn
注意
詳細な設定は[APIリファレンス](https://docs.aws.amazon.com/cdk/api/v1/python/aws_cdk.aws_s3/Bucket.html)を読み解く必要があります。
:::

## lambdaを作成する
次にlambdaを作成してみます。
s3と同様に今回は`aws_lambda`をimportして作成します。

```python: project-name/project-name_stack.py
from aws_cdk import (
    Stack,
    aws_s3 as _s3,
    RemovalPolicy,
    aws_lambda as _lambda,
)
from constructs import Construct

class ProjectNameStack(Stack):

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


        （省略）

        # lambdaの設定
        my_function = _lambda.Function(
            self,"ProjectxxxLambda",
            function_name="project-xxx-function",
            runtime=_lambda.Runtime.PYTHON_3_9, #　 使用するランタイムの設定（今回はpython3.9）
            code=_lambda.Code.from_asset("lambda"), # lambdaで動かすコードのありか
            handler="testcode.handler_test", # handlerの設定
        )

```

`runtime`でlambdaで使用するランタイムを設定します。今回はpython3.9にしました。
`code`では実際にlambdaに入れ込むコードのフォルダの場所を記述します。
`handler`はcodeで示したコード類の中で、どのファイルのメソッドをhandlerにするか示します。
今回はtestcode.pyのhandler_testメソッドがhandlerになります。lambdaは起動後このhandlerを動作させます。

次にプロジェクトディレクトリ直下にlambdaで実行するコードを作成します。
```sh
$ mkdir lambda
$ cd lambda
$ touch testcode.py
```

```python: testcode.py
def handler_test(event, context):
    print('Hello World')
```

コード内のメソッドをhandlerとして機能させるには`event` `context`の2つを引数で受け取ります。

## deployする
AWS環境にdeployします

### 初回のみ
初回のみ以下コマンドを実行します。

```sh
$ cdk bootstrap
```

この操作により、デプロイプロセスの際に使用される CloudFormation のテンプレートやアセットを保存するための S3 バケットが作成されるようです。

### 現状コードと実際のAWSの差分の確認
```sh
$ cdk diff
```

現状と比較して、その差分を検出してコンソールに表示してくれます。
以下は初回実行時の例です

```sh
＄ cdk diff
Stack QiitaPjStack
IAM Statement Changes
┌───┬─────────────────────────────────────┬────────┬────────────────┬──────────────────────────────┬───────────┐
│   │ Resource                            │ Effect │ Action         │ Principal                    │ Condition │
├───┼─────────────────────────────────────┼────────┼────────────────┼──────────────────────────────┼───────────┤
│ + │ ${ProjectxxxLambda/ServiceRole.Arn} │ Allow  │ sts:AssumeRole │ Service:lambda.amazonaws.com │           │
└───┴─────────────────────────────────────┴────────┴────────────────┴──────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬─────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                        │ Managed Policy ARN                                                             │
├───┼─────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${ProjectxxxLambda/ServiceRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │
└───┴─────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Parameters
[+] Parameter BootstrapVersion BootstrapVersion: 
(省略)

Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: 
(省略)

Resources
(省略)

Other Changes
(省略)

```

### 環境にデプロイする
差分を確認して、問題なければdeployします。
初回は`Do you wish to deploy these changes (y/n)?`と質問されます。
```sh
$ cdk deploｙ
```
問題なければ、AWSコンソールからデプロイしたアプリケーションが確認できるはずです。

#### CloudFormation
プロジェクトと同名の新しいスタックが作成されています。
![スクリーンショット 2022-08-31 21.33.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005334/7c8d0be2-9cae-422e-fcc6-4c91df7db4bc.png)

#### S3
![スクリーンショット 2022-08-31 21.38.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005334/0a7b7995-ff25-068a-90f7-dd1997c8a401.png)
#### lambda
![スクリーンショット 2022-08-31 21.39.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005334/beae10f0-ede3-9e1c-4e32-b081ca13248f.png)

## 削除する
最後にdeployした環境を削除してみます。
```sh
$ cdk destroy
```

削除不可の要素がなければ、コマンドは実行されます。
CloudFormation等を確認して、アプリケーションがないことを確認できます。

# まとめ
コードでインフラストラクチャを定義できるようになりました！！やったね！
とは言いつつ、設定やアプリの連携はさらに知識が必要なので鍛錬を怠らないようにしていきたいですね。