Terraformでblock自体を設定する・設定しないを振り分けるには、dynamic
を使えばよい。
しかし、nullishな変数に対して、dynamic
をどう使えばよいかでハマったので、解決法を書いておく。
dynamic
が必要になるケースは?
あるblock自体の有無を振り分けたい時にdynamic
が必要になる。
例えば、GCSのgoogle_storage_bucket_iam_member
リソースのcondition
ブロックがある。
// example
resource "google_storage_bucket_iam_member" "member" {
bucket = "member-bucket"
role = "hogehoge"
member = "hugahuga"
// conditionはそれ自体がblock
condition {
content {
title = "my-rule"
description = "it is perfect"
expression = "xxx"
}
}
ここで、以下のlocals
を、ループして複数のgoogle_storage_bucket_iam_member
を宣言したいとする。
locals {
test-storage = [
// パターン1: conditionに値あり
{
bucket = "test1"
role = "test1"
member = "user:[email protected]"
condition = { # 値があるパターン
title = "test"
description = "test"
expression = "test"
}
},
// パターン2: conditionはnull
{
bucket = "test2"
role = "test2"
member = "user:[email protected]"
condition = null # nullパターン
},
// パターン3: conditionキー自体なし
{
bucket = "test3"
role = "test3"
member = "user:[email protected]"
} # keyがそもそもないパターン
]
}
パターンとしては3つ。
- パターン1: conditionに値あり
- パターン2: conditionはnull
- パターン3: conditionキー自体なし
パターン1のみcondition
を設定し、パターン2,3は設定しないようにしたい。
ここで注意が必要なのだが、condition
はそれ自体ブロックになっている。そのため、下記のように変数を単純にアサインする書き方ができない。
// 不可: 変数を代入する
...
condition = local.someMap
...
また、condition
を設定しない場合は、null
を入れる、といったこともできない。
// 不可: 設定しない場合はnull入れる
...
condition = null
...
そこでdynamic
の出番となる。
dynamic
を使った書き方
以下のように書くことで、パターン1のみcondition
を設定し、パターン2,3は設定しないようにできる。
resource "google_storage_bucket_iam_member" "member" {
for_each = { for i in local.test-storage : i.bucket => i }
bucket = each.value.bucket
role = each.value.role
member = each.value.member
dynamic "condition" {
for_each = try(each.value.condition, null) != null ? [1] : []
content {
title = each.value.condition.title
description = each.value.condition.description
expression = each.value.condition.expression
}
}
}
ここがミソ:
for_each = try(each.value.condition, null) != null ? [1] : []
try
は、パターン2,3の場合は空リスト[]
をfor_each
に返す。for_each
では処理がなされないので、condition
の設定をスキップできる。
dynamic内のfor_eachの注意点
dynamic
内でのeach
の挙動には注意が必要。
一見、each.value
にはdynamic
内のfor_each
で指定した値が入ってくるのではと思われる。
...
dynamic "condition" {
for_each = try(each.value.condition, null) != null ? [1] : []
content {
title = each.value.condition.title // each.valueは`[1]`が入ってこないの?
description = each.value.condition.description
expression = each.value.condition.expression
}
...
と思って混乱したのだが、ちゃんと公式に書いてあった。 dynamicのfor_eachは通常と少し違う動きをする。
The iterator argument (optional) sets the name of a temporary variable that represents the current element of the complex value. If omitted, the name of the variable defaults to the label of the dynamic block (“setting” in the example above).
つまり、dynamic
内のfor_each
で指定した変数にアクセスするには、each
は使わない。
今回のケースだと、condition.key
/condition.value
を使えば、dynamicのfor_eachの変数にアクセスできる。
よくよく見ると、公式の例でも、dynami
cのfor_each
はeach.key
/each.value
は使ってない。