[AWS] CDKでIaCを実現する

## 目次

[はじめに](https://www.notion.so/AWS-CDK-IaC-Zenn-b0d831c1c9284a689880c7cff48deac0?pvs=21)
[IaCとは何か](https://www.notion.so/AWS-CDK-IaC-Zenn-b0d831c1c9284a689880c7cff48deac0?pvs=21)
[IaCを導入するメリットとデメリット](https://www.notion.so/AWS-CDK-IaC-Zenn-b0d831c1c9284a689880c7cff48deac0?pvs=21)
[AWS CDKと他の構成管理ツールの比較](https://www.notion.so/AWS-CDK-IaC-Zenn-b0d831c1c9284a689880c7cff48deac0?pvs=21)
[[環境構築〜デプロイ] AWSにIaCを導入する](https://www.notion.so/AWS-CDK-IaC-Zenn-b0d831c1c9284a689880c7cff48deac0?pvs=21)
[インストール方法、設定方法](https://www.notion.so/AWS-CDK-IaC-Zenn-b0d831c1c9284a689880c7cff48deac0?pvs=21)
[おまけ](https://www.notion.so/AWS-CDK-IaC-Zenn-b0d831c1c9284a689880c7cff48deac0?pvs=21)
[まとめ](https://www.notion.so/AWS-CDK-IaC-Zenn-b0d831c1c9284a689880c7cff48deac0?pvs=21)

# はじめに

※以下の方を対象にしています。

- AWSについて勉強している、または業務で触ったことがある。
- IaC(Infrastructure as Code)について興味がある。
- **AWSアカウントを作成済み**

# IaCとは何か

IaC(Infrastructure as Code)とはその名の通り、インフラの構築をコードを用いて行うことです。

キーワードは「**再現性**」です。

例えば、AWSでEC2インスタンス（サーバー）を立ち上げたい場合や、RDS・DynamoDBといったデータベースを管理したい場合は、AWSのマネジメントコンソールからインスタンスやクラスタの作成を行うとができます。マネジメントコンソールも最近はかなりわかりやすくなっていて、EC2であればインスタンスタイプ、AMI、セキュリティグループの作成、ストレージまでGUIで選択していくだけで簡単に作成ができます。

しかし、複雑なアプリケーションのインフラを構築する際にまず、VPCを作成して、次にAZ（アベイラビリティゾーン）、サブネット、セキュリティグループ、その次にRoute53、CloudFront…など設定する項目が多く大変になります。さらにその環境と同じものがステージング環境、本番環境といった複数環境で構築が必要な場合、毎回マネジメントコンソールから同じ設定を行わないといけなり、運用コストも高くなります。

そこで、IaCを導入することで環境の再構築を楽にしたり、人為的なミスを削減することができます。上述した複数環境の構築も楽になり、再現性が高まります。

今回はAWSにおけるIaCに焦点を当てていますが、GCPやAzureで利用できるIaCツールも増えてきていています。

[AWS CDK とは - AWS Cloud Development Kit (AWS CDK) v2](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/home.html)

### IaCを導入するメリットとデメリット

メリット

- 再利用が可能で高速に構築が行える
    - インフラの設定をコードで管理しているので、他の環境のコードを再利用したり、環境を複製したりすることができます。具体的には開発環境用に構築したコードをテスト環境用に丸々置き換えて構築するといったこともできます。
- 人為的なミスや変更が行える
    - マネジメントコンソールから設定を行うとGUIでの操作になるので人為的なミスが発生しやすくなります。IaCで管理することによって変更履歴や差分の確認が容易になりgitでバージョン管理することで設定を失敗してもすぐに元のバージョンに戻すこともできます。
- 構築した成果物をレビューできる
    - コードで管理するということは、MR（マージリクエスト）やPR（プルリクエスト）を作成してレビューするという文化を取り入れることができます。アプリケーションコード（フロントエンドやバックエンドのコード）と同じようにインフラも担当者にレビューを依頼してさらに再利用性や可読性の高いコードとして管理することができます。

デメリット

- 学習コストが高い
    - これはどのツールを使用するかにもよりますが、AWSでIaCを実現するためにはTerrafoomやCloudFormation、AWS CDKといった構成管理ツールが必要になります。ツールによって書き方が異なるので、慣れるまでには時間がかかる場合があります。
- 導入に時間がかかる
    - インストールや設定方法(今回はAWS CDK)は後述しますが、導入する際に若干時間ががかかります。（体感ではCDKはそこまで大変ではないと思いますが）

### AWS CDKと他の構成管理ツールの比較

AWS CDKは(Cloud Development Kit)の略称であり、AWSでIaCを実現するためのツールの1つです。AWSの構成管理ツールとしてCDKの他にCloudFornation、Terraformが使用されることが多いと思うので、それらと比較します。

- AWS CloudFormation
    - AWSが提供するマネージドなサービスなので機能が豊富で簡単に作成できます。
    - JSONまたはYAML形式のテンプレートを使用してリソースを定義します。
        - テンプレート記法に慣れていない場合は、学習コストが高くなります。
    - ファイルは大きくなり可読性は低くなります。
- Terraform
    - AWSだけでなくAzureやGCPでも対応しているので情報が豊富で、マルチクラウド構成で役立ちます。
    - HCL言語（JSONやYAMLのようにkeyとvalueで記述する）仕様なのでプログラミングに精通していなくてもコーディングできます。
    - コードが冗長になり、リファクタリングが難しくなる場合があ利用可能。

- **AWS CDK**
    - AWS公式のツールなので、AWSクラウドでインフラを構築する場合にドキュメントが豊富だったりAWSのサポートを受けることができます。
    - 好きなプログラミング言語で構築できます。
        - JavaScript、TypeScript、Python、Java、C#、Goが使えます。
    - CloudFormationやTerraformよりコードの量が少なく済みます。
    - プログラミングに精通している+学習コストがやや高くなります。

# [環境構築〜デプロイ] AWSにIaCを導入する(TypeScript)

### インストール方法、設定方法、構成確認

では、いよいよCDKの環境を作っていきます。今回はTypeScriptで作成することにします。事前準備として必要なものは、

- AWSアカウント
    - 作成方法は[こちら](https://aws.amazon.com/jp/free/?gclid=CjwKCAiAlJKuBhAdEiwAnZb7ldYUKLYlIheb79rOb1XnDjX2m4jfGanY-K_vZEgbiA1iVJjzs6mOiRoCpY0QAvD_BwE&trk=2f1fab87-e0c1-4d0a-984a-39b537e9a901&sc_channel=ps&ef_id=CjwKCAiAlJKuBhAdEiwAnZb7ldYUKLYlIheb79rOb1XnDjX2m4jfGanY-K_vZEgbiA1iVJjzs6mOiRoCpY0QAvD_BwE:G:s&s_kwcid=AL!4422!3!487544183224!e!!g!!aws%20%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%20%E4%BD%9C%E6%88%90&all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=*all&awsf.Free%20Tier%20Categories=*all)
- Node.js
    - バージョン指定はありません。（node18〜ぐらいであれば問題ないと思います）
- TypeScript
    - `npm install -g typescript`でインストールできます。
- AWS CLI
    - インストール方法は[こちら](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)
    - `aws —version`で確認できます。
<br>

**１.  AWS CDKをインストールする**
```shell
$ npm install -g aws-cdk

//確認
$ cdk --version
```
<br>
**２. プロジェクトディレクトリの作成<br>**
空のディレクトリを作成し、そこに移動します。
```shell
$ mkdir cdksample && cd cdksample
```
<br>
**３. プロジェクト作成**
cdk initでプロジェクトを初期化（作成）します。`—-language`オプションで言語を指定します。

```jsx
//cdk init {名前}
$ cdk init sample-app --language typescript
```
<br>
**４. ディレクトリ構成の確認**
エディタで開いてディレクトリを確認します。TypeScriptで開発を行ったことのある方は`tsconfig.json`や`package.json`についてはご存知だと思うので割愛します。

ディレクトリを確認すると、`bin`フォルダ、`lib`フォルダや`cdk.json`があると思うのでそちらについて説明します。

流れとしては、`lib/〇〇-stack.ts`にAWSリソースを記述していき、そのファイルを`bin/〇〇.ts`でimportしてインスタンス化するとデプロイ時にロードされるみたいなイメージです。

- `bin/〇〇.ts`
    - CDKアプリケーションのエントリポイントです。ここに定義されたスタックをデプロイします。デフォルトでは`lib/〇〇-stack.ts`がimportされ、定義されているので今回は触りません。（スタックとはCloudFormationスタックのことになります。スタックについては[こちら](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/stacks.html)）
    
    ```js
    #!/usr/bin/env node
    import * as cdk from 'aws-cdk-lib';
    import { CdksampleStack } from '../lib/cdksample-stack';
    
    const app = new cdk.App();
    
    //スタックを定義
    new CdksampleStack(app, 'CdksampleStack');
    ```
    
    - 具体的には複数スタックを作成してこのファイル内でインスタンス化することで定義されたスタックをロードします。
    
    ```js
    #!/usr/bin/env node
    import * as cdk from 'aws-cdk-lib';
    import { CdksampleStack } from '../lib/cdksample-stack';
    import { CdksampleStack2 } from '../lib/cdksample-stack2';
    
    const app = new cdk.App();
    
    //複数スタックを定義
    new CdksampleStack(app, 'CdksampleStack');
    new CdksampleStack2(app, 'CdksampleStack2');
    ```
    

- `lib/〇〇-stack.ts`
    - CDKアプリケーションにメインスタックが定義されます。このファイルを編集することがほとんどです。
    - 実際のAWSリソースを定義します。（プロジェクト作成時はデフォルトでSQS、SNSが定義されています）
    
    ```js
    import { Duration, Stack, StackProps } from 'aws-cdk-lib';
    import * as sns from 'aws-cdk-lib/aws-sns';
    import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
    import * as sqs from 'aws-cdk-lib/aws-sqs';
    import { Construct } from 'constructs';
    
    export class CdksampleStack extends Stack {
      constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);
    
        const queue = new sqs.Queue(this, 'CdksampleQueue', {
          visibilityTimeout: Duration.seconds(300)
        });
    
        const topic = new sns.Topic(this, 'CdksampleTopic');
    
        topic.addSubscription(new subs.SqsSubscription(queue));
      }
    }
    ```
    

- `cdk.json`
    - アプリの実行方法をツールキットに指示させるためのファイルです。
    - 設定やコンテキスト変数が含まれています。

<br>
**５. CloudFormationテンプレートを作成**
cdk（今回はTypeScript）でAWSリソースを定義していますが、内部的には、デプロイ時にはCloudFormationテンプレートが作成されていることになります。記述したコードからCloudFormationテンプレートを作成（合成）するためには`cdk synth`コマンドを打ちます。

```shell
cdk synth
```

コマンドに成功するとカレントディレクトリに`cdk.out`フォルダが作成されます。`cdk.out/〇〇.template.json`を見るとCloudFormationテンプレートが確認できます。

![スクリーンショット 2024-08-18 23.07.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655825/7c0f2964-d02f-9a1c-96b4-156365189f38.png)


<br>
**６. ブートストラップする**
cdk appを新しい環境(AWSアカウント x リージョン)でデプロイする際に、最初の一回だけ実行が必要なコマンドです。

CloudFormationスタックが作成され、cdk appのデプロイに必要な周辺リソースが作成されます。（魔法のコマンドぐらいに思っていただければ大丈夫です）

```shell
$ cdk bootstrap
```

マネジメントコンソールのCloudFormationを見るとCDKToolkitのスタックが作成されていると思います。またS3バケットも作成されます。

![スクリーンショット 2024-08-18 23.07.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655825/60fd1456-26c3-e577-ad04-a125c4cfd0c7.png)


<br>
**７. デプロイする**

下記コマンドを打つだけです。

```shell
$ cdk deploy

//全てのスタック
$ cdk deploy '*'

//指定したスタックのみ
$ cdk deploy sampleStack
```

再度マネジメントコンソールでCloudFormationを確認すると、エディタで見たスタックが作成されているのが確認できるかと思います。

![スクリーンショット 2024-08-18 23.07.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655825/894951b3-9ba1-2522-544d-a39e0dde7a95.png)



実際にデフォルトで記載されていたSQS等も確認できるかと思います。

以上が基本的な流れになります！

注意点は、`cdk bootstrap`はデプロイ時毎回実行する必要はありません。また、`cdk diff`やオプションなどまだまだ便利なコマンドがあるので[公式](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/cli.html)を参照することをお勧めします。（QiitaやZennにまとめている方も多くいるので参考になるかと思います。）

<br>
# おまけ

一通りcdkによるインフラ構築の流れがわかったところで、簡単な構成をCDKで構築してみます。（構成とはいっても、VPC内に2AZでサブネットを配置するだけですが…）

作成する構成図は以下になります。

![スクリーンショット 2024-08-18 23.07.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655825/8453ca12-e583-5782-769e-14906b2adabc.png)



[公式のドキュメント](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Vpc.html)をもとにlib/〇〇-stack.tsに記述します。完成系のコードは以下になります。

```js
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { IpAddresses, SubnetType, Vpc } from "aws-cdk-lib/aws-ec2";

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

    new Vpc(this, 'Vpc', {
      vpcName: `sample-vpc`,
      ipAddresses: IpAddresses.cidr('10.0.0.0/16'),
      maxAzs: 2,
      natGateways: 0,
      createInternetGateway: true,
      subnetConfiguration: [
        {
          subnetType: SubnetType.PUBLIC,
          name: 'Public',
          cidrMask: 24,
        },
        {
          subnetType: SubnetType.PRIVATE_ISOLATED,
          name: 'Private',
          cidrMask: 24,
        },
      ],
    });
  }
}
```

- 公式を見るとTypeScriptではaws-cdl-lib→aws_ec2からVpcコンストラクトをインポートできると書いてあるのでインポートします。

```javascript
import { Vpc } from "aws-cdk-lib/aws-ec2";
```

- Vpcコンストラクタをインスタンス化します。

```js
//引数は(scope, id, props)
//scopeはthis
//コンストラクトのローカルIDです。同じスコープ内のコンストラクト間で一意である必要があるID
//propsは初期化プロパティのセット

new Vpc(this, 'Vpc', {});
```

- パラメータとして、
`vpcName`（名前）、
`ipAddresses`（CIDR範囲）、
`maxAzs`（AZ数）、
`natGateways`（natGatewayの有無）、
`createInternetGateway`（internetGatewayの有無）、
`subnetConfiguration`（サブネットの設定）
を定義します。

最後に`cdk deploy`を実行します。

マネジメントコンソールを確認するとVPC、AZ、サブネットが作成されていることが確認できました！シンプルに書けますね。

![スクリーンショット 2024-08-18 23.07.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655825/51bd5c6c-892a-a7e7-e5ac-659c58c7e0d7.png)



# まとめ

今回はAWS CDKでAWSクラウドインフラの構築をコードで行いました。他にも紹介したようにCloudFormationやTerraformといったツールがあるので、実際に触ってみて状況に応じて適切な選定ができるようになるといいと思います。ただ、どのツールを使用するにしても重要なのは「再現性」であり、複数環境の構築、またはどの環境を作るにしても、基盤になる部分の構成を素早く構築することで効率よく作業を進めることができます。