はじめに
terraformでAWSインフラを構築しようとしたとき,いろいろ調べるとfor_each
を使うとコードの量の少なくて済むことに行きつくかと思います.
ただ,私が業務でfor_each
を使用していると実際に使うには勘所を抑える必要があると思ったので,まとめてみました.
これからterraformを使うエンジニアの方の助けになれば嬉しいです.
for_eachの記法
for_each
は要はループ処理です.私の個人的な解釈ですが,for_each
はプログラミング言語(Python)でいうwhile len(num)
に近いものだと思います.入力の中身の最後まで繰り返し処理するイメージです(伝われ).
VPCの作成を例とすると下記のように書けます.
set(string)を入力とした場合
locals{
param = toset(["vpc-a", "vpc-b"])
}
resource "aws_vpc" "vpc" {
for_each = local.param
cidr_block = "10.0.0.0/16"
tags = {
name = each.value
}
}
ここで入力がset(string)の時限定で,name = each.value
はname = each.key
とも書けます.set(string)は要は配列なのでkey
=value
の概念がないためどちらでもよいのです.大体の場合はvalue
に設定値を書くので,コード全体で統一するためにeach.value
を書くことが多いです.
上記を実行すると以下の2つのVPCが作成されます.
- CIDR
10.0.0.0/16
のvpc-a
- CIDR
10.0.0.0/16
のvpc-b
つまり,同じset(string)を入力としたfor_each
は同じパラメータのリソースを複数作成する場合に適しているといえます.
Tips
同じ設定値のリソースを複数作成したい場合等は,set(string)
を入力としてfor_each
に渡すと作りやすい
しかし実際は2つのVPCに同じCIDRを使うのではなく,別のCIDRかつ名前も変えたいというシーンが多いのではないかと思います.
そんな時に使えるのがmap型を使用した作り方です.
mapを入力とした場合①
locals{
param = tomap({
vpc-a = {
# vpc-aのパラメータをkey=value形式で書く
cidr = "10.0.0.0/16"
},
vpc-b = {
# vpc-bのパラメータをkey=value形式で書く
cidr = "10.1.0.0/16"
}
})
}
resource "aws_vpc" "vpc" {
for_each = local.param
cidr_block = each.value.cidr
tags = {
name = each.key
}
}
上記を実行すると以下の2つのVPCが作成されます.
- CIDR
10.0.0.0/16
のvpc-a
- CIDR
10.1.0.0/16
のvpc-b
このようにmapを入力に使用することで個々に設定値が異なるリソースを複数作成することができます.
肝心のリソースセクションはコードの中に一つのためコード全体が短くなりやすくなり,個々のリソースの設定値はlocal
or variable
の中に集約できるため保守性も高くなります.
Tips
異なる設定値のリソースを複数作成したい場合等は,map
を入力としてfor_each
に渡すと作りやすい
コードの中ではeach.value
で統一した方がいいと言っているのにeach.key
を使用しているじゃないか!!
という 几帳面 完璧主義の方向けに以下の書き方を紹介します.
mapを入力とした場合②
locals{
param = [
{
name = "vpc-a"
cidr = "10.0.0.0/16"
},
{
name = "vpc-b"
cidr = "10.1.0.0/16"
}
]
}
resource "aws_vpc" "vpc" {
for_each = {for i in local.param : i.name => i}
cidr_block = each.value.cidr
tags = {
name = each.value.name
}
}
作成されるリソースは"mapを入力とした場合①"の時と同じですが
for_each = {for i in local.param : i.name => i}
ここが難読だと思います.
ここで何が起きているのかというと,for_each
が読み込める形式のobject
に変換しているのです.
今までのfor_each = local.param
では,すでにfor_each
が読み込める形式であったため変換は必要ありませんでした.
今回のlocal.param
をよく見るとkey
がないんです.だから変換してあげる必要があります.
-
:
の前for i in local.param
→local.param
の値を変数i
に格納します -
:
の後i.name => i
→i.name
をkey,i
をvalueとして変換
その結果以下のようなobject
が内部的に出来上がります.
{
"vpc-a" = {
name = "vpc-a"
cidr = "10.0.0.0/16"
},
"vpc-b" = {
name = "vpc-b"
cidr = "10.1.0.0/16"
}
}
なんかみたことありますね.
そう!mapを入力とした場合①で紹介した書き方と同じです.ここではその変換をしているんですね.
こんな書き方もあるよと覚えておくといつか助かることがあるかもしれません.
Tips
{for i in local.param : i.name => i}
はfor_each
で使える形に変換している!
まとめ
for_each
をうまく使うことでコードサイズの削減,保守性の向上が見込めます.
はじめのうちは慣れない記法でてこずるかもしれませんが,使いこなせるとコードの可読性がグン!と上がるのでぜひ積極的に使うようにしてみてはいかがでしょうか?