Terraform 0.12ではlookupは要らない・・・かも

Terraform初心者です。~~ARM Templateが辛くてな・・・~~

タイトルは多分、既出な気がするけども、どうやって引っ掛ければ良いか分からなかった・・・


tl;dr
---------

Terraform 0.12からは`"${lookup(var.hoge, "terraform.workspace").fuga}"`は`var.hoge[terraform.workspace].fuga`といった形で使えます。

しかし、Terraform 0.12の構文拡張によるものなので、0.11.14以前では使えません。

（2019/06/25時点）VSCodeのTerraform Extensionは0.12に未対応なのでエラーになります。これがちょっと辛い。



参考
-----------------

0.12.0 のリリースノート

- https://github.com/hashicorp/terraform/releases/tag/v0.12.0

考えさせられたきっかけ

- https://kenzo0107.hatenablog.com/entry/2019/04/17/103558
- https://speakerdeck.com/shogomuranushi/infrastructure-as-code-is-very-tired?slide=26


背景
-----------------

tfファイルを書いて、やっぱり2環境以上に展開したい、でもs3バケットとか重複が許されない名前とかあるのでいい感じに定義したい、という事があります。

そうなると環境毎に変数を定義したくなりますが、世のやり方を学んだほうが良かろうということで調べた。

ざっくり見つけたのはこの3つ辺り。

1. workspace不使用 + モジュール化（環境ごとにtfファイルを分けてモジュールを必要に応じて使う形）
2. workspace + variables(Mapのキーで分ける)
3. variables(var-fileで分ける)

1のモジュール化は良く見るが、やりすぎ感がある。あまり試していない。
（Ansibleの体験で、9割同じだけど1割の細かい違いを両立できなくて書き直す、みたいなことがあったので。センスがないだけかもしれない。）

2は変数を使って`lookup(var.hoge, "${terraform.workspace}.fuga")`で引く、というコードを見た。「やりたい事に近いけど不格好だなぁ」と思ったが、そもそも、なんでこのような形を取ってるのかがわからなかった。`"dev.fuga"`というように1個のキーにせずにネストしたMapで書けば見た目もきれいなのに、と。

愚者は経験から学ぶので、0.11で色々試した。
理由は、ネストしたMapは参照がちょっとめんどくさかったからだと考えている。

以下のように、`var.hoge["dev"]["fuga"]`とかもエラー。lookupがMapを返すような変数だとエラー。以下コード。

``` test.tf
variable "env" {
  default = {
    dev = {
      service_name = "dev-foo"
      s3_name      = "dev-foo-s3"
      cidrs        = "10.0.0.0/24,10.0.1.0/24"
    }
    prod = {
      service_name = "prod-foo-service"
      s3_name      = "prod-foo-s3"
      cidrs        = "10.111.0.0/24,10.111.1.0/24"
    }
  }
}

# これはダメだが、0.12からは有効。
# output "output_service_name1" {
#   value = "${var.env[terraform.workspace].service_name}"
# }
# これもダメ。
# output "output_service_name2" {
#   value = "${var.env[terraform.workspace]["service_name"]}"
# }
# これもダメ。lookupでmapが返ってくるから。
#output "output_service_name3" {
#  value = "${lookup(lookup(var.env, terraform.workspace), "service_name")}"
#}

# これは出来るが2重まで。3重になるとダメ。
output "output_service_name4" {
  value = "${lookup(var.env[terraform.workspace], "service_name")}"
}


# 少しはマシだが、localsを毎回定義したくない
locals {
  env = "${var.env[terraform.workspace]}"
  service_name = "${local.env["service_name"]}"
}
output "output_service_name5" {
  value = "${local.service_name}"
}
```

だから`"dev.fuga"`というキーにしておいて、lookup 1発で取れる形に最適化したのかな？と考えている。つまり、よくわかりませんでした。

話は戻して、ネストしたMapでやる場合、0.12ではもう少しどうにかならんのですか？と思い試してみたら、普通に`var.hoge[terraform.workspace].fuga`という形が使えた。この形、ググりにくくてどれだけ一般的かわかってないんですが、まだ0.12が出て日が浅いからですかね？

確認したコード
---------------

今回はworkspaceを使っていますが必須ではないです。`terraform.workspace`は適当に1つのvariableかlocalで置き換えても良いと思います。


``` test.tf
variable "env" {
  default = {
    dev = {
      service_name = "dev-foo"
      s3_name      = "dev-foo-s3"
      cidrs        = "10.0.0.0/24,10.0.1.0/24"
    }
    prod = {
      service_name = "prod-foo-service"
      s3_name      = "prod-foo-s3"
      cidrs        = "10.111.0.0/24,10.111.1.0/24"
    }
  }
}


output "output_service_name" {
  value = "${var.env[terraform.workspace].service_name}"
}

output "output_s3_name" {
  value = "${var.env[terraform.workspace].s3_name}"
}
output "output_cidrs" {
  value = "${split(",", var.env[terraform.workspace].cidrs)}"
}
output "output_cidrs_lookup" {
  value = "${split(",", lookup(var.env, "${terraform.workspace}").cidrs)}"
}

```

ちなみに、0.12から `"${var.env[terraform.workspace].service_name}"` で済む構文は `var.env[terraform.workspace].service_name` としても同じように機能しますが、エディタが対応していないのでやっていません。


以下、実行結果

workspace devを作って切り替え、apply

``` 
$ terraform workspace new dev
$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

output_cidrs = [
  "10.0.0.0/24",
  "10.0.1.0/24",
]
output_cidrs_lookup = [
  "10.0.0.0/24",
  "10.0.1.0/24",
]
output_s3_name = dev-foo-s3
output_service_name = dev-foo
```

workspace prodを作って切り替え、apply

```
$ terraform workspace new prod
$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

output_cidrs = [
  "10.111.0.0/24",
  "10.111.1.0/24",
]
output_cidrs_lookup = [
  "10.111.0.0/24",
  "10.111.1.0/24",
]
output_s3_name = prod-foo-s3
output_service_name = prod-foo-service
```


ところで、var-file
-------------------------

これぐらいの用途ならvar-fileで気にならないかもしれない。0.11.14でも動く。

考慮点はtfstateぐらいか。でもこれも実行時の引数で変えれるのでシェルスクリプト化しておけば問題ない気も。


``` test.tf
variable "service_name" { }

variable "s3_name" { }

variable "cidrs" {}


output "output_service_name" {
  value = "${var.service_name}"
}

output "output_s3_name" {
  value = "${var.s3_name}"
}

output "output_cidrs" {
  value = "${split(",", var.cidrs)}"
}
```

``` dev.tfvars
service_name = "dev-foo"
s3_name      = "dev-foo-s3"
cidrs        = "10.0.0.0/24,10.0.1.0/24"
```

``` prod.tfvars
service_name = "prod-foo-service"
s3_name      = "prod-foo-s3"
cidrs        = "10.111.0.0/24,10.111.1.0/24"
```

実行例

```
$ terraform apply -var-file dev.tfvars

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

output_cidrs = [
  "10.0.0.0/24",
  "10.0.1.0/24",
]
output_s3_name = dev-foo-s3
output_service_name = dev-foo

$ terraform apply -var-file prod.tfvars

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

output_cidrs = [
  "10.111.0.0/24",
  "10.111.1.0/24",
]
output_s3_name = prod-foo-s3
output_service_name = prod-foo-service
```

結局どれがいいかわからない。もっと規模が大きくなってきたらまた違うのかもしれない。

それより他に時間をかけるべき作業があるんじゃあないですかね？