Skip to content
Go back

Terraform: dynamicを使ってblock設定の有無を振り分ける

Published:  at  12:00 AM

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,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の変数にアクセスできる。 よくよく見ると、公式の例でも、dynamicのfor_eacheach.key/each.valueは使ってない。



Previous Post
VivaldiにDeepLプラグインをインストールする
Next Post
CloudAssetInventoryでGCP内のリソースを確認する