AWS CDKを使ってた人がTerraformを使ってみて感じた話

## はじめに
はじめまして。
11月から、クリエーションライン株式会社に入社したKodakです。
以後、よろしくお願いします。

今回は、前職でAWS CDKを使っていて、入社後にTerraformを使うことになったため、Terraformを使ってみて、思った事や感じた事などを書いていこうかと思います。

## AWS CDKとTerraformの違い

![スクリーンショット 2024-11-30 7.26.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241956/cc2b5016-2e95-d96e-6a05-a82b8b5d0336.png)

AWS CDKは、書いたコードがCloudFormationのテンプレートに変換され、テンプレートをAWS CloudFormation上で動かして、インフラを構築していきます。
それに対して、Terraformは、AWSのAPIを使用して、直接インフラを構築していきます。

こういった仕組みになっているため、Terraformを複数人で操作する際は注意が必要です。

![スクリーンショット 2024-11-30 22.54.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241956/e8a08edc-8f48-4ff6-7bbf-d70d39b0219c.png)

AWS CDK側は、複数人で操作してもAWS CloudFormationが実行中の場合はエラーを出してくれますが、Terraformは複数人で操作するとコンフリクトが発生します。

また、Terraformは構築したインフラの状態を保持する**Stateファイル**をローカルで持ってるため、複数人でTerraformを操作する場合は共通で持っておかないと、「ある人はS3にバケットを作った」「ある人はS3にバケットを作っていない」など、結果的にAWS側に何を構築したのかわからなくなってしまいます。

そのため、Terraformは複数人で操作する場合は、**Stateファイル**を共通化し、誰かがTerraformを操作している場合はロックをかけるような仕組み作りが必要となります。

![スクリーンショット 2024-11-30 23.04.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241956/9b1ff13d-cdde-1c46-6cc0-36b5c07007ab.png)

Terraformは、これをS3とDynamoDBを組み合わせて仕組み化できるようになっており、誰かがTerraformを操作している場合はDynamoDBに書き込みが発生してロックできるようになっています。

以下のように`backend`を設定すると実装できます。

```terraform
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  required_version = ">= 1.9.0"

  backend "s3" {
    bucket         = "stage.terraform.state"
    key            = "terraform.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "stage-terraform-state-lock"
  }
}
```

## デプロイ速度の違い

Terraformは、APIを使用して直接インフラを構築しているため、AWS CDKと比べて圧倒的にデプロイ速度が早いと感じました。
AWS CDKを使っていた人からすると、「ぇ、もうデプロイおわったの？」というくらい速度の違いを体感できます。

## 書くコード量の違い

AWS CDKは、コードを書く時に基本L2コンストラクト（よくL2やL2レイヤーとか呼ばれています）で書いていきます。
このL2コンストラクトとは、最低限の設定をコードに書くだけで、残りの部分の設定を**いい感じにしてくれる**ものです。
対して、Terraformにはこのような書き方はできなくて、殆どすべての設定を手動で書いていくことになります。

そのため、書くコード量は相対的にTerraformのほうが多くなります。

たとえば、VPCを作成して、パブリックサブネット（InternetGatewayあり）を作成したい場合、AWS CDKだと以下を書くだけで済みます。

```typescript
        const vpc = new ec2.Vpc(this, `${props.envName}Vpc`, {
        cidr: '10.2.0.0/16',
        subnetConfiguration: [
                {
                    cidrMask: 24,
                    name: 'public',
                    subnetType: ec2.SubnetType.PUBLIC,
                },
            ],
            restrictDefaultSecurityGroup: true,
            maxAzs: 1
        });
```

対して、Terraformだと以下のように、細かいところまで指定しないとリソースを作ってくれません。

```terraform
# VPCを作成
resource "aws_vpc" "sample_vpc" {
  cidr_block = "10.1.0.0/16"
}

# InternetGatewayを作成
resource "aws_internet_gateway" "sample_internet_gateway" {
  vpc_id = aws_vpc.sample_vpc.id
}

# ルートテーブルを作成
resource "aws_route_table" "sample_route_table" {
  vpc_id = aws_vpc.sample_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.sample_internet_gateway.id
  }
}

# サブネットを作成
resource "aws_subnet" "sample_subnet" {
  vpc_id                  = aws_vpc.sample_vpc.id
  cidr_block              = "10.1.1.0/24"
  map_public_ip_on_launch = true
}

# ルートテーブルの関連付け
resource "aws_main_route_table_association" "main_route_table_association" {
  vpc_id         = aws_vpc.sample_vpc.id
  route_table_id = aws_route_table.sample_route_table.id
}
```

一見、これは大変だなぁ と思いつつも、最近は[Cursor](https://www.cursor.com/)や[GitHub Copilot](https://github.com/features/copilot)などで、AIが勝手に補完してくれる機能もあるため、そこまで大変だと思わなくなりました。

## 環境の分け方の違い

AWS CDKでは、パラメーターによってDev環境やProd環境など作り分けるパターンが多いと思います。

そんな私も以下のようにパラメーターを使って構築する環境を分けていました。

```typescript
import * as cdk from 'aws-cdk-lib';
import { devParameter, type AppParameter } from './parameter';
import { VpcEc2Stack } from '../lib/stack/vpcEc2Stack';

const app = new cdk.App();

const envKeyContext = 'env';
const taskKeyContext = 'task';

const envKey = app.node.tryGetContext(envKeyContext) as string;
const taskKey = app.node.tryGetContext(taskKeyContext) as string;

if (envKey === undefined)
    throw new Error(
        `Please context option. ex) cdk deploy -c ${envKey}={ dev | prod }`,
    );

const parameters = [devParameter];
const appParameter: AppParameter = parameters.filter(
    (obj: AppParameter) => obj.envName === envKey,
)[0];

new VpcEc2Stack(app, `${appParameter.envName}-VpcEc2-Stack`, {
    ...appParameter,
});
```

対して、TerraformはDev環境やProd環境をディレクトリで分けます。

```sh
├── environments
│   ├── prod
│   │    └── main.tf
│   └── dev
│        └── main.tf
├── global
│   ├── dynamodb
│   └── s3
├── modules
│   ├── ec2
│   └── vpc
└── src
    └── ec2
```

これは、Terraformには上記で書いた**Stateファイル**の存在があるからです。
たとえば、すべての環境を1つのStateファイルに保存してしまっていた場合、どこか1ヵ所での間違いが全体に広がってしまう可能性があり、非常に危険です。

そのため、各環境ごとに、ディレクトリを分けてStateファイルも分けておく必要があります。

AWS CDKの場合は、パラメーターによって環境を分けることで、作られるCloudFormationのテンプレートも異なるので、そのあたりを意識しなくて良いのが改めてわかります。

## さいごに

というわけで、AWS CDKとTerraformの4つの違いを書いたわけですが、個人的にはTerrafomもそこまで悪くないと感じました。
特にデプロイ速度が圧倒的に早いというのが良いですね。コードの書き方も慣れだと思います。

ただ、AWS CDKは、やはりAWSに特化しているだけあって、色々なことを考えなくて済むので、AWSしか触らないならAWS CDKが便利だと思いました。

仕事の都合上、AWSだけでは無くなりそうなので、これからもTerraformについて学びを深めていきたいと思います。