terraform workspaceで茨の道を歩いてみた

この記事は[3-shake Advent Calendar 2023](https://qiita.com/advent-calendar/2023/3-shake) シリーズ２の14日目の記事です。

ほぼほぼ興味本位でterraform workspaceを用いて使い始めてみたものの、中々に茨の道でした...

そんなお話しです。

## 背景
とあるお仕事にてAWSにてインフラ構築することになりました。いつものようにTerraformを使おうと思ったのですが、ふと思い出してworkspaceの存在を思い出しました。経験もなかったですしちょっと興味があったので以下の理由もあることから採用してみました。

* そこまで大きな規模でもなく、
* 環境間の差分もあまりなさそうだったため
* 何より使ったことないから試してみようっていう好奇心97%

今回のお話では**環境ごとの差分を吸収する目的**が前提になります。

## [Terraform workspace](https://developer.hashicorp.com/terraform/language/state/workspaces)について

“面”をworkspaceとして管理し、同じコードベースで複数面を管理できるような仕組みです。
バックエンドにこの複数の面のに対応するtfstateファイルが作られます。

```shell
terraform workspace -h
Usage: terraform [global options] workspace

  new, list, show, select and delete Terraform workspaces.

Subcommands:
    delete    Delete a workspace
    list      List Workspaces
    new       Create a new workspace
    select    Select a workspace
    show      Show the name of the current workspace
```

まずはworkspaceを作りましょう。

```bash
terraform workspace new prd
terraform workspace new stg
terraform workspace new dev

terraform workspace list
  default
  dev
  prd
  stg
```

workspaceを切り替えます。

```bash
bucket = "stg-tf"
region = "ap-northeast-1"
key  = "inf.tfstate"
encrypt = true
shared_credentials_file = "[$HOME/.aws/credentials]"
profile = "stg"
```

workspaceをセレクトして、initします。

```bash
terraform workspace select stg
terraform init -reconfigure -backend-config=backends/stg.tfbackend
```

## で、どうだった？

**辛くなっちゃった。**

## Q: 何が辛かった？
A: ユースケースにあっていなかった

当初聞いたところでは環境差分はあまりなく、1回作って複製していこうって話だった。だが、そもそも環境ごとにインフラが管理するスコープが違ったり、シンプルに「この環境にはこれが必要」ということが発生しました。

[公式では以下のような注釈](https://developer.hashicorp.com/terraform/cli/workspaces#when-not-to-use-multiple-workspaces)があります。

> In particular, organizations commonly want to create a strong separation between multiple deployments of the same infrastructure serving different development stages or different internal teams. In this case, the backend for each deployment often has different credentials and access controls. CLI workspaces within a working directory use the same backend, so they are not a suitable isolation mechanism for this scenario.


平たく言うと **dev / stg / prdのような開発段階の違う環境を管理する場合には不向きとのこと。**

逆に[一般的なユースケース](https://developer.hashicorp.com/terraform/cli/workspaces#use-cases)として以下が挙げられていました。

> A common use for multiple workspaces is to create a parallel, distinct copy of a set of infrastructure to test a set of changes before modifying production infrastructure.

同じ環境を複数個作成する、本番を変更する前のテストとしてコピーを作成するみたいなこと。

すなわち**茨の道を大地を力強く蹴り上げるように歩き続けていました。**

ここからは上記に合わないアンチパターンをどストライクで踏んだ私が、どういう点が不向きなのかを体験をもとに話します。

### コーディングが煩雑になる

workspace名を変数にしてリソースの有無を表現することになるので、このような記述があります。

```terraform
resource "aws_lb" "alb" {
  name               = var.name
  load_balancer_type = "application"
  internal           = var.enable_internal
  idle_timeout       = 60
  enable_deletion_protection = (
    terraform.workspace == "infra-dev" ? false :
    terraform.workspace == "dev" ? false : true
  )
```

コスト最適化で環境ごとにインスタンスサイズや数、SPOTインスタンス化などなどやりますよね？
そのたびに環境差分が多くなると、こういった記法が乱立します。

### バージョンアップをdev→stg→prdのように環境を追って順繰りに行えない

Terraform使っているということは、Core, Providerともにバージョンアップ作業しますよね？
基本的には上記のように環境を追って実施していくと思います。

以下のようなterraformブロックのrequired_versionを書き換えて、再init → 必要に応じて修正 → 差分解消・apply が基本的な流れだと思います。

```terraform
terraform {
  required_version = "1.3.5"
  backend "s3" {
  }

  required_providers {
    aws = "4.39.0"
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = ">=2.16.0"
    }
    tls = {
      source  = "hashicorp/tls"
      version = ">= 3.0"
    }
  }
}
```

しかし、**terraformブロック自体もlockファイルも環境ごとに用意できません。**

先ほどの一般的なユースケースを鑑みるとそりゃそうだよなと思いますので、確かに納得感はあります。

## まとめ
こんな感じで使ってみて辛さはあったものの、その分このworkspace機能についてだいぶ解像度見ることができました。
ユースケースにおける所感をまとめると以下のような感じです。

- dev / stg / prdのように環境差分を吸収するような一般的な目的で使うものではない
- 逆に複数人に全く同じ開発環境を払い出したり、本番前のテストapplyなどでは有用に感じる

少し大変ですが、後でマージして移行することもできそうな肌感なので、Terraformにおける初手のリポジトリ構造は一旦ワーキングディレクトリの切り替え前提で問題ないと思いました。

ぜひユースケースごとの選択肢の一つとして頭の片隅に置いていただけると幸いです〜

## 参考

- https://dev.classmethod.jp/articles/how-to-use-terraform-workspace/