【Terraform Cloud】Cloudブロックの内容を環境変数から参照させて、backend.tfを複数環境で1枚にしてみた

# はじめに

複数環境のリモートバックエンドにTerraform Cloudを使う場合、
下図のように環境ごとの`backend.tf`へOrganization名とWorkspace名（もしくは変数参照）を書くと以下図のように環境の数だけ`backend.tf`ができる場合があるかと思います。

```
.
├── env
│   ├── dev
│   │   └── backend.tf　←　開発環境のOrganization名とWorkspace名
│   ├── prod
│   │   └── backend.tf　←　本番環境のOrganization名とWorkspace名
│   └── stg
│       └── backend.tf　←　ステージング環境のOrganization名とWorkspace名
└── main.tf
```

今回はGitHub Actionsでのデプロイ時に環境変数からデプロイ先環境のOrganizationとWorkspaceを注入し、`backend.tf`が1枚で済むCI/CDをGitHub Actionsで構成する方法の1つをご紹介します。（他にも方法はいろいろあると思います）

# 前提
- CI/CD：**GitHub Actions**
- Terraform CloudのExecutionMode：**Remote**

# ざっくりやり方
以下2つの環境変数を使います。
- `TF_WORKSPACE`
> TF_CLOUD_ORGANIZATION: Specifies the name of the organization. Terraform reads this variable when organization is omitted from the cloud block`. If both are specified, the configuration takes precedence.
出典：https://developer.hashicorp.com/terraform/cli/cloud/settings

- `TF_CLOUD_ORGANIZATION`
> TF_WORKSPACE: Specifies the name of a single HCP Terraform workspace. Terraform reads this when workspaces is omitted from the cloud block. HCP Terraform does not create a new workspace from this variable. The workspace must already exist in the specified organization. You can set TF_WORKSPACE if the cloud block uses tags. However, you must include the value of TF_WORKSPACE in the set of tags. This variable also selects the workspace in your local environment. Refer to TF_WORKSPACE for details.
出典：https://developer.hashicorp.com/terraform/cli/cloud/settings

それぞれ、`Cloud`ブロックの`organization`と`workspaces.name`にて指定が無い場合に`TF_CLOUD_ORGANIZATION`、`TF_WORKSPACE`から値を取得する挙動をします。今回はこれを利用します。
以下のようなイメージです。

```terraform:backend.tf
terraform {
  cloud  {
    organization = "<organizationの名前>" # →　指定が無い場合、TF_CLOUD_ORGANIZATIONを使用
    workspaces {
      name = "<workspaceの名前>" # →　指定が無い場合、TF_WORKSPACEを使用
    }            
  }
}
```
※なお、環境変数と`Cloud`ブロック両方に指定がある場合は`Cloud`ブロックを優先する動きをします。

# 構成例
元々のファイル構成から以下図のように変更しました。

```diff
  .
  ├── env
  │   ├── dev
- │   │   └── backend.tf
  │   ├── prod
- │   │   └── backend.tf
  │   └── stg
- │       └── backend.tf
  ├── main.tf
+ └── backend.tf
```
新規作成した`backend.tf`は以下内容だけにします。
```terraform:backend.tf
terraform {
  cloud {                   
    hostname = "app.terraform.io"
  }
}
```

あとは、ワークフローのYAML内にて以下順で処理をさせるだけ！
1. 環境変数を設定
2. `terraform init`（環境変数で指定したOrganizationとWorkspaceを認識する）
3. `terraform apply`（2で認識したバックエンドを対象にApplyを行う）


```yaml:GitHub Actionsの記述例
name: Deploy Terraform

on:
  push:

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      TF_CLOUD_ORGANIZATION: <デプロイしたい環境のOrganization名>
      TF_WORKSPACE: <デプロイしたい環境のWorkspace名>

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Setup Terraform CLI
        uses: hashicorp/setup-terraform@v3
        with:
          cli_config_credentials_token: <Terraform CloudのAPIトークン>

      - name: Terraform Init (Terraform Cloud backend)
        run: terraform init # ここで環境変数に設定したOrganizationとWorkspaceを認識してくれる

      - name: Terraform Apply
        run: terraform apply -auto-approve -no-color

```
後は

- <デプロイしたい環境のOrganization名>
- <デプロイしたい環境のWorkspace名>
- <Terraform CloudのAPIトークン

このあたりにGitHub ActionsのSecretsやVariablesから取得した値を動的に代入するようするようワークフローを組めば、デプロイ用YAMLも1枚で済みますね！

# 参考
https://developer.hashicorp.com/terraform/language/terraform#tf_cloud_organization

↑公式ドキュメントでは本記事で扱った環境変数以外にも利用可能な環境変数がリストアップされています。