Terraformのfor_eachをちゃんと理解する
背景
どの言語でコードを書くにも必要になる繰り返し処理だが、Terraform ではじめてこれを書く際は syntax 以外にも細かい前提知識を知っておかないと地味に手こずった。個人的な覚え書きがてら実際のユースケースを添えてまとめる。内容は v1.3.x の仕様に沿っている。
前提知識
例えば Terraform でユーザ一覧を扱いたいケースは以下のように書く。
resource "organizatoin_member" "member" { for_each = toset(["hoo@example.org", "bar@example.org", ...]) email = each.value }
for_each に渡す要素の値は必ずユニークになっている必要があるので、Map か Set のみが使える。resource block 内では each.value で要素にアクセスできる。Set の場合は key と value はどちらも同じになる。
ここで書いた resource を plan すると以下のような結果になる。
Terraform will perform the following actions: # organizatoin_member.member["hoo@example.org"] will be created + resource "organizatoin_member" "member" { + email = "hoo@example.org" } # organizatoin_member.member["bar@example.org"] will be created + resource "organizatoin_member" "member" { + email = "bar@example.org" } ...
organizatoin_member.member["hoo@example.org"]
は state 上でこのリソースをユーニークに識別するアドレスとなる。for_each が unique key を持つ型のみを受け付ける理由はこれにある。
よくあるユースケース
少し複雑なユースケースを見ながら理解を深める。ここでは二重 for ループを回したいようなパターンを考える。 例えば組織一覧を Map 型変数で定義して、それぞれの要素で更にユーザの一覧を配列で持っているようなケース。
locals { organizations = { dev_group = { name = "Development Group" members = [ "hoo@example.org", ... ] } biz_group = { ... } ... } }
この変数を元に以下のような resource を作成したい。
- 組織を表す
organization
resource- apply 時に決まる ID と組織名を持っている
- ユーザを表す
organization_user
resource- apply 時に決まる ID と email address を持っている
- ユーザと所属組織の関連を表す
organization_membership
resource- apply 時に決まる organization の ID と user の ID を持っている
まず組織 resource はシンプルに以下のような形で作成できる。
resource "company_organization" "organization" { for_each = local.organizations name = each.value.name }
各 organization に所属するユーザは以下のように一括で作成すればいい。
data "organization_user" "user" { for_each = toset(flatten([for org in local.organizations : org.members])) email = each.key }
Organization と user の紐付けはどうするか。以下のような書き方ができる。
resource "organization_membership" "membership" { for_each = { for key, value in local.organizations : company_organization.organization[key].id => value } organization_id = each.key member_ids = [for email in each.value.members : organization_user.user[email]] }
ポイントとしては resource の作成時に他の resource、company_organization
と organization_user
が参照できること。このとき locals に定義している Map の key を index に参照先の resource を指定する。ちょっと回りくどい感じもするが、他の言語では二重 for ループで行うのと近いことができる。公式ドキュメントにも似たようなケースが紹介されている。
まとめ
Terraform で for_each を扱う際に最低限知っておく情報と実際によくあるユースケースの例を挙げた。初めて触れる Terraform のコードは一瞬困惑するものの慣れれば問題なく書けるけど、syntax だけでなく Terraform が扱ってくれる state などの構造についても理解しておく必要があるなと思った。関数や expression だけでなく Manipulating State - Terraform CLI | Terraform | HashiCorp Developer あたりもきちんと読んでおくと、ちょっと込み入った事をやろうとしても悩まなくて済みそうだ。