Terraform の Workspace について調べてみる

インフラをデプロイする前に、Terraform のスクリプトをテストしたいという要望があり、Workspaceを検討することになったので、試してみた。

# Workspaceとは

Workspaceは、１つのバックエンドをもった１つのコンフィグファイルの複数のインスタンスを管理するためのものである。具体的には、ワークスペースを指定すると、複数のstateファイルを管理できるようになる。

[ドキュメント](https://www.terraform.io/docs/state/workspaces.html)を読むと、典型的な用途は、インフラのスクリプトを本番に適用する前に、実際デプロイしてテストしたいという用途のようだ。今回の目的に適している。

# Getting Started 

## Terraform script 
簡単なTerraforma のスクリプトを用意する。今回のスクリプトは Azure 向けに作成しているが、一番簡単ですぐに作成、削除できるリソースを指定するといいだろう。

_terraform.tf_

```
provider "azurerm" {
  version = "=1.39.0"
}

resource "azurerm_resource_group" "test" {
  name     = "testResourceGroup1"
  location = "West US"

  tags = {
    environment = "${terraform.workspace} branch"
  }
}
```

## Workspace を追加する

```bash
$ terraform workspace new test
```

こんなオプションがある模様。

```bash
$ terraform workspace new --help
Usage: terraform workspace new [OPTIONS] NAME [DIR]

  Create a new Terraform workspace.


Options:

    -lock=true          Lock the state file when locking is supported.

    -lock-timeout=0s    Duration to retry a state lock.

    -state=path    Copy an existing state file into the new workspace.
```

list コマンドでワークスペースが`default` から切り替わったのがわかる。`default` は従来のと同じでカレントディレクトリにstateファイルが作られる。

```bash
$ terraform workspace list
  default
* test   
```

## Apply の実施

`terraform apply` を実施してみると、`terraform.tfstate.d` というディレクトリが出来てその中に作成した`workspace`のディレクトリが出来ているのがわかる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/c2c17dc6-7acb-3da6-6d25-88b03b19a299.png)

また、`${terraform.workspace}` が現在のワークスペース名に置き換えられていることも確認できる。

```bash
$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_resource_group.test will be created
  + resource "azurerm_resource_group" "test" {
      + id       = (known after apply)
      + location = "westus"
      + name     = "testResourceGroup1"
      + tags     = {
          + "environment" = "test branch"
        }
    }
```

# backend を指定した時の振る舞い

Terraform では state を保存するバックエンドを指定できる。バックエンドを指定した時にどのように workspaceはワークするだろうか？
下記の例は Azure のバックエンド (Storage Account) だが、他にも様々なバックエンドが指定できる。[Standard Backends](https://www.terraform.io/docs/backends/types/azurerm.html)

backend を使うように `tf` ファイルを更新する。

```
provider "azurerm" {
  version = "=1.39.0"
}
terraform {
    backend "azurerm" {
        resource_group_name = "RemoveTerraform"
        storage_account_name = "tsushistatetf"
        container_name = "tfstate"
        key = "terraform.tfstate"
    }
}
resource "azurerm_resource_group" "test" {
  name     = "testResourceGroup1-${terraform.workspace}"
  location = "West US"

  tags = {
    environment = "${terraform.workspace} branch"
  }
}

```

このコンフィグの場合、事前に`RemoveTerraform` という名前のリソースグループの中に`tsushistatetf` という名前のStorage Account を作成しておく必要がある。また、`Storage Account`には、事前に`tfstate` という名前のコンテナを作成しておく必要がある。
`key` で指定している内容は、バックエンドに格納されるときのオブジェクト名だ。今回は同じ`terraform.tfstate`にしてみた。

ちなみに、バックエンドを指定すると、`terraform init` をやり直す必要が生じる。既に、tfstateファイルがある場合は、マイグレーションしてくれるらしいが、今回は無かったのでマイグレーションは発生しなかった。

```bash
$ terraform init
```

## Workspace の作成

さて、２つのワークスペースを作成して、apply してみよう。

```bash
$ terraform workspace new test
$ terraform apply 
$ terraform workspace new pr-branch-one
$ terraform apply 
``` 

Storage Account 上の stateファイルはどのように格納されているだろうか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/09f7e016-645d-6737-3b96-76dbe5e19254.png)

単純に、Workspaceが`env:workspace_name` の形式でポストフィックスが追加されている。これは、Azure プロバイダの仕様だと思うが、他のバックエンド`consul` や `s3`、`etcd` だとおそらく振る舞いが違うのだろう。

# 次のステップ

多分自分はStateの理解が浅いので、[State](https://www.terraform.io/docs/state/index.html)の章を読んでみたい。