Terraformで開発環境 / 本番環境を分けてリソースを管理したい

## 要約
最終的なソースコード全体は以下のリンクからご確認いただけます。

https://github.com/mochi-yu/qiita-material/tree/main/terraform-separate-env

## 関連記事
**Terraformの開発環境を整理**したのを備忘録も兼ねて複数の記事にしています。この記事はそのうちの1本目として、「Terraformで開発環境と本番環境を分けて実行する」ことについてまとめます。

<!-- TODO -->
- **Terraformで開発環境と本番環境を分けて管理する**（この記事）
- [Terraformの`tfstate`をS3で他者と共有して管理する](https://qiita.com/mochi_2225/items/0fb66faec0cd9a1d133c)

## 背景
普段アプリなどの開発をしていると、**開発環境 / ステージング環境 / 本番環境などのように複数の環境を用意して開発を進める**ということが多いかと思います。Terraformを用いてクラウドを利用した開発を行うときも、例えばインフラ構成を変えたいような時に、一度開発環境で`terraform apply`を実施してみて問題ないことを確認してから、本番環境でも適用したいなんてときがあります。

そこで、このような**環境を分けてTerraformを実行する方法**についてこの記事にまとめたいと思います。また、環境ごとにリソースの名前を変えたりといったこともしてみたいと思います。

## 実装
今回Terraformで管理する内容は、シンプルに**AWSのVPCを1つ作る**というものにしたいと思います。それを開発環境(`dev`)と本番環境(`prod`)のそれぞれで作っていきます。

環境を分ける方法としては[公式のドキュメント](https://developer.hashicorp.com/terraform/language/style#multiple-environments)にも以下のようなディレクトリ構成が紹介されています。**`./environments`の下に環境ごと固有のファイルを配置**し、**共通するファイルは`./modules`の下に配置する**といった構成です。

https://developer.hashicorp.com/terraform/language/style#multiple-environments

```:ディレクトリ構成
.
├── .gitignore
├── environments
│   ├── dev
│   │   ├── .terraform.lock.hcl
│   │   ├── main.tf
│   │   └── terraform.tfstate
│   └── prod
│       ├── .terraform.lock.hcl
│       ├── main.tf
│       └── terraform.tfstate
└── modules
    ├── main.tf
    ├── variables.tf
    └── vpc.tf
```

### 共通するリソースの定義
まずは共通する部分を見ておきます。

`./modules/vpc.tf`でVPCの設定を記述しています。**VPCの`Name`タグは`{環境}-test_vpc`というフォーマット**にしており、**環境ごとに名前の異なるVPCが作成**されることを期待しています。たとえば開発環境なら`develop-test_vpc`という名前がついて欲しいです。

この環境の情報は`/modules/variables.tf`の中で宣言している変数`env`に入っています。変数の値は環境ごとに変わるものなので、後述の通り環境ごとに渡します。

```terraform:/modules/main.tf
terraform {
  required_version = ">=1.7"

  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}
```

```terraform:/modules/vpc.tf
resource "aws_vpc" "test_vpc" {
  cidr_block = "10.1.0.0/16"

  tags = {
    Name = "${var.env}-test_vpc"
  }
}
```

```terraform:/modules/variables.tf
variable "env" {
  type = string
}
```

### 各環境固有のファイルを作成する
以上のような共通部分ができたら、今度は環境ごとのファイルを以下のように作成します。先述の通り、**環境ごとのファイルは`/environments`の下の環境名のフォルダにそれぞれ用意**します。例えば開発環境なら`/environments/dev`の下に、本番環境なら`/environments/prod`の下にファイルを置きます。

開発環境のエントリーポイントとなるファイルは次のようにしました。`modules "app"`のブロックで先ほどの共通部分のパスを設定して、変数`env`に`"develop"`という値を設定しています。

```terraform:./environments/dev/main.tf
module "app" {
  source = "../../modules"
  env = "develop"
}
```

本番環境についても同様に`./environments/prod`以下に`main.tf`を作ります。今回、開発環境と本番環境で違うのは`env`変数の内容だけですので、`/environments/dev/main.tf`との差分もそこだけになります。

```diff_terraform:./environments/prod/main.tf
module "app" {
  source = "../../modules"
- env = "develop"
+ env = "prod"
}
```

### `terraform apply`の実行
`terraform apply`のコマンドを実行するときは、**各環境のフォルダに移動してからこれを実行**します。例えば開発環境なら`cd ./environments/dev && terraform apply`のような形です。また、**`terraform init`も各フォルダでそれぞれ実行する必要があります**。

そうすれば、それぞれの環境でのコンフリクトなどがなく、以下のようにVPCを2つ作成できたかと思います。

![terraform_env_separate-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/abe67fe6-90e7-9ad5-8988-c9acfe266d91.png)

## 参考

https://zoo200.net/terraform-tutorial-module-and-directory/

https://developer.hashicorp.com/terraform/language/style#multiple-environments