「このメンバーの一覧に対して、このリストの一覧の権限の付与したい」ってケース、結構あると思います。「このメンバー一覧に対して、この権限だけを付与したい」という場合はTerraformだとfor_each
で割と簡単に書くことができますが(一重ループ)、タイトルのように複数の集合の要素の組合せ(集合の直積)だとfor_each
が簡単にできません(二重ループ)。あれこれ試行錯誤したので、メモしておきます。
Terraformのsetproductについて
集合の直積についてはTerraformだとsetproduct
で簡単にできます。ありがたい。
こういう感じ。配列の配列を返してくれます。
> setproduct(["staging", "production"], ["a", 2]) [ [ "staging", "a", ], [ "staging", "2", ], [ "production", "a", ], [ "production", "2", ], ]
setproductをfor_eachで回す
Terraformは二重ループ的なことは対応していなくて、ループを回す対象が以下のどちらかである必要があります。
- map
- 要素がstringのset
setproductの戻り値は配列の配列になっているため、The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type tuple.
と怒られます。for_eachで解決できるように、型を変えていきましょう。今回はmapにします(インデックスで管理するのはダルいので)。
google_project_iam_member
を例に説明するとこんな感じ。
resource "google_project_iam_member" "iam_member" { for_each = { for p in local.member_role_pairs : "${p.member} ${p.role}" => p # ここがミソ! } member = each.value.member role = each.value.role } locals { members = [ "group:aaa@gmail.com", "group:bbb@gmail.com", ] roles = [ "roles/bigquery.dataEditor", "roles/bigquery.jobUser", ] member_role_pairs = [ for pair in setproduct(local.members, local.roles) : { # ここもミソ! member = pair[0] role = pair[1] } ] }
ざっくり説明するとこんな感じですね。
- 一旦、配列の配列をmapの配列に変換
- forの中でconcatしたものをkeyに無理やり変換して、最終的にmapに変換
...と偉そうに書いたものの、去年も同じことでちょっと悩んでてid:heleeenさんに助けてもらっていたんですが、ちゃんとメモしていなくて今年も困ってしまいました。来年は困らないように今年はちゃんとメモしたぞ...!