[terraform] やはり俺のWorkspaceは間違っている(かもしれない)

これは「[「はじめに」の Advent Calendar 2021](https://adventar.org/calendars/7037)」2日目の記事です。

## 悩み
- tfファイルは1つで、複数環境への反映するベストプラクティスがわからない。

## 複数環境を管理する方法

3つのAWSアカウントに同じリソースを作りたい場合にどうするか考える。
3つのAWSアカウントはそれぞれ

* 開発環境: `development`
* ステージング: `staging`
* 本番環境: `production`

とする。
まず、開発環境を構築するために以下のような構成になったとする。

```
terraform
 |- terraform.tf
 |- ec2.tf
 |- vpc.tf
 |- s3.tf
 |- provider.tf
```
ここから、2アカウント目、3アカウント目と増やす場合、どのようにStateを管理するのが良いのだろうか。
なお、State管理はファイルではなく、リモートで行うこととする。

### A: 1ディレクトリ、複数ステート、defaultワークスペース

* ディレクトリ内のファイル構成:
    * そのまま
* State:
    * アカウントごとにStateを管理するS3を準備
* 環境の切替方法:
    * init時にでstateの場所を指定
* ワークスペース: 
    * `default`

環境ごとに、 

```bash
rm -fr .terraform
terraform init -backend-config=../staging.conf
```
を実行する。

割と単純だが、ワークスペースが `default` 固定なのでどのStateを触っているのか分かりづらいので、開発環境で変更をテストしてみよう、などと実行してみたら本番だった、という事故が起きそう。
事故を防いだりbackendの指定を簡単にするために、ラッパーシェルなどterraform外の力が必要。

### B: 1ディレクトリ、1ステート、複数ワークスペース

* ディレクトリ内のファイル構成:
    * そのまま
* State:
    * Stateを管理する専用のS3を1つ準備
* 環境の切替方法:
    * workspaceの切り替え
* ワークスペース: 
    * `development` `staging` `production`

ワークスペースで切り替える方法が一番キレイに思えるが、公式では推奨されていないらしい。
countで制御したり複雑になったりするからだろうか・・・

https://www.terraform.io/docs/language/state/workspaces.html


State管理するS3のアカウントをどこに置くのか、という悩みは出てくる。
productionに置くとデータ管理の都合上、許容されないことが多く、developmentに置いても本番のリソース情報が開発環境にある時点でアウトになるケースが多い。
とすると、セキュアに管理する為に別アカウントをもう一つ用意することになる。悩ましい。

### C: 1ディレクトリ、複数ステート、個別のワークスペース名

* ディレクトリ内のファイル構成:
    * そのまま
* State:
    * アカウントごとにStateを管理するS3を準備
* 環境の切替方法:
    * init時にでstateの場所を指定した後、workspaceの切り替え
* ワークスペース: 
    * `development` `staging` `production`

Aパターンの亜種で、ワークスペースを別にすることで事故が起きづらくなるようにしたパターン。
使うときはこうなる

```bash
rm -fr .terraform
terraform init -backend-config=../staging.conf
terraform workspace select staging
```

シェルプロンプトにワークスペース名を表示すれば事故は減らせる。

https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/terraform/README.md


操作は増える。
ちなみに、このパターンは運用したことがあるけど、気づいたら `staging` のS3バケットに `production` workspaceスペースが生えたりしていた。
variableで色々ガードしていたから、applyは出来ないけどなんか無駄なものが増えている感じで気持ち悪い。

### D: モジュール分割で、環境ごとにディレクトリ、ディレクトリごとに1ステート、defaultワークスペース

* ディレクトリ内のファイル構成:
    * 環境ごとにディレクトリを分ける。リソースは全てモジュール化。
* State:
    * アカウントごとにStateを管理するS3を準備
* 環境の切替方法:
    * ディレクトリ移動
* ワークスペース: 
    * `default`

公式ドキュメントでもオススメの方法。
複数環境で利用 = 再利用なんだから共通モジュール化する。
たしかに一番疎結合。
けど、労力に見合わない感じ。
数多くのチームがあって、各チームでモジュールを自由に利用してもらうような組織体制ならわからなくはないんだけど、小さいチームだと辛すぎる。
メリットより、作る時間がかかりすぎるというデメリットのほうが大きいと感じた。

## 結局どうしたか

S3で管理すると大変なので、terraform cloudを利用してみた。
すると、こうなる。

* ディレクトリ内のファイル構成:
    * そのまま
* State:
    * terraform cloudのworkspace
* 環境の切替方法:
    * initは最初の1回だけ。あとはworkspaceの切り替え。
* ワークスペース: 
    * `development` `staging` `production`

パターンBで、state管理をS3ではなくterraform cloudにするだけ。

ファイルはgithubで管理。
terraform cloud上でワークスペースを3つ作成し、全て同じリポジトリ、同じディレクトリに向けた。
そして `terraform.tf` を以下のようにする

```
terraform {
  backend "remote" {
    hostname     = "app.terraform.io"
    organization = "hajimeni"

    workspaces {
      prefix = "hajimeni-"
    }
  }
}
```
これで `terraform init` は最初の1回だけでよく、`workspace` を切り替えれば環境を切り替えることができ、State管理はterraform cloudに任せ、ローカルapplyを禁止すれば事故も防げる。

公式はモジュール化を推奨しているが、このやり方が今の所ちょうどいい塩梅に感じている。
リソースが増えてきたら違う問題や悩みがでるんだろうけど。

というわけで、 `terraform cloud` をみんな使おう！(ダイマ)

https://www.hashicorp.com/products/terraform/pricing

- 開発チームが少なければ(5人以下なら)FreePlanで全然使える。
- Team planでも1人あたり `$20/mo` なので、高くはないと思う。
    - チーム内にTerraform触れる人数によると思うけど。
    - 20人触る人がいても `$400/mo` で日本円で4万チョットだし会社組織なら余裕だと思う。

## 余談

- タイトルはとあるラノベのタイトルから拝借いたしました。
- が、私はそのラノベを読んでおりませぬ・・・
- タイトルは煽りであって、自分のワークスペースが間違っているかどうかは知りません :innocent: 
- みんなどういう管理をしているのだろうか・・・