Terraformの繰り返し処理の種類
Terraformにはfor
,for_each
,count
の3つの繰り返し処理があります。
今回はfor
。
for式は、別の複合型の値(例えばList)を変換して複合型の値を生成します。
count
,for_each
はこちら。
分類
分類 | 説明 | |
---|---|---|
式 | for | 繰り返し処理を行える。 |
メタ引数 | for_each | mapを利用した繰り返し処理。 |
count | 単純な指定値による繰り返し処理。もっともイメージがつきやすい。 |
for文の書き方
出力形式
for
では、書き方によって生成されるタイプがlist
,set
,tuple
,map
,object
に変わります。
例えば以下のようなリストがあったとして、それぞれの出力は以下の通りになります。
variable "list" {
type = list(string)
default = [
"apple",
"orange",
"banana"
]
}
パターン1:list,set
output "list" {
value = [for s in var.list : s]
}
Changes to Outputs:
+ list = [
+ "apple",
+ "orange",
+ "banana",
]
list
,set
で出力したい場合は[]
で囲う必要があります。
もちろん結果から特定の値を参照することもできます。
locals {
list = [for s in var.list : s]
}
output "list-locals" {
value = local.list[0]
}
Changes to Outputs:
+ list-locals = "apple"
パターン2:tuple,map,object
output "map" {
value = {for s in var.list : s => s}
}
Changes to Outputs:
+ map = {
+ apple = "apple"
+ banana = "banana"
+ orange = "orange"
}
一方tuple
,map
,object
で出力したい場合は{}
で囲うのと、=>
を入れてあげる必要があります。
入力形式
index
を利用したい場合は、以下のように複数シンボルを利用します。
output "list-index" {
value = [for s,v in var.list : "${s}_${v}"]
}
output "tuple-index" {
value = {for s,v in var.list : s => v}
}
Changes to Outputs:
+ list-index = [
+ "0_apple",
+ "1_orange",
+ "2_banana",
]
+ tuple-index = {
+ "0" = "apple"
+ "1" = "orange"
+ "2" = "banana"
}
for文でできること
さて、ここまではfor文の入出力についてお話しました。
実際どう使うの?っていうとこになりますが、複合型から別の複合型に変える ことができると、いろいろなことが実現できます。
リストを特定の書式に変更する
forを書いてそれを書式変換の関数をかませれば、簡単に変換できちゃいます。
# すべて大文字に変換
output "list-upper" {
value = [for s in var.list : upper(s)]
}
# すべて小文字に変換(最初から小文字だけど)
output "list-lower" {
value = [for s in var.list : lower(s)]
}
# 最初の文字だけ大文字に変換
output "list-title" {
value = [for s in var.list : title(s)]
}
Changes to Outputs:
+ list-lower = [
+ "apple",
+ "orange",
+ "banana",
]
+ list-title = [
+ "Apple",
+ "Orange",
+ "Banana",
]
+ list-upper = [
+ "APPLE",
+ "ORANGE",
+ "BANANA",
]
mapを作る
これは例に書いた通りですが、簡単にlistからmapが生成できちゃいます。
んで、生成したmap
を何に使うかというと…
for_eachを回す
はい、for_each
に使います。
for_eachはmap
であることが前提なので、list
をループで処理することはできません。(toset
してあげれば別ですが)
そんな時にfor
でmap
を生成してあげれば…一網打尽ってわけですね(?)
以下はちょっと複雑だけどAzureでサブネットを生成する場合にlist
からmap
を生成してfor_each
で回してます。
variable "location" {
type = string
default = "japaneast"
}
resource "azurerm_resource_group" "rg" {
name = "rg-test"
location = var.location
}
# vNetを作成する
resource "azurerm_virtual_network" "vnet" {
name = "vnet-test-01"
location = var.location
resource_group_name = azurerm_resource_group.rg.name
address_space = ["10.0.0.0/16"]
}
variable "subnet_address" {
type = list(string)
default = [
"10.0.0.0/26",
"10.0.0.64/26",
"10.0.0.128/26",
"10.0.0.192/26",
]
}
resource "azurerm_subnet" "subnet" {
for_each = { for s, v in var.subnet_address : s => v }
name = "subnet-${each.key}"
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [each.value]
resource_group_name = azurerm_resource_group.rg.name
}
出力
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "rg-test"
}
# azurerm_subnet.subnet["0"] will be created
+ resource "azurerm_subnet" "subnet" {
+ address_prefixes = [
+ "10.0.0.0/26",
]
+ enforce_private_link_endpoint_network_policies = (known after apply)
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "subnet-0"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "rg-test"
+ virtual_network_name = "vnet-test-01"
}
# azurerm_subnet.subnet["1"] will be created
+ resource "azurerm_subnet" "subnet" {
+ address_prefixes = [
+ "10.0.0.64/26",
]
+ enforce_private_link_endpoint_network_policies = (known after apply)
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "subnet-1"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "rg-test"
+ virtual_network_name = "vnet-test-01"
}
# azurerm_subnet.subnet["2"] will be created
+ resource "azurerm_subnet" "subnet" {
+ address_prefixes = [
+ "10.0.0.128/26",
]
+ enforce_private_link_endpoint_network_policies = (known after apply)
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "subnet-2"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "rg-test"
+ virtual_network_name = "vnet-test-01"
}
# azurerm_subnet.subnet["3"] will be created
+ resource "azurerm_subnet" "subnet" {
+ address_prefixes = [
+ "10.0.0.192/26",
]
+ enforce_private_link_endpoint_network_policies = (known after apply)
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "subnet-3"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "rg-test"
+ virtual_network_name = "vnet-test-01"
}
# azurerm_virtual_network.vnet will be created
+ resource "azurerm_virtual_network" "vnet" {
+ address_space = [
+ "10.0.0.0/16",
]
+ dns_servers = (known after apply)
+ guid = (known after apply)
+ id = (known after apply)
+ location = "japaneast"
+ name = "vnet-test-01"
+ resource_group_name = "rg-test"
+ subnet = (known after apply)
}
Plan: 6 to add, 0 to change, 0 to destroy.
まぁでもこれははっきり言ってcount
でやっていることと同じなので、あまりよいとは言えないですね。最初からcount
で実装したほうが楽にできます。
mapのlistをfor_eachで回す
これが一番利用するかもしれません。
for_each
はmap
でないとダメなのは前述した通りですが、map
をlist
にして(tuple
にして)ループさせたいことってありますよね?
そういう場合に利用します。以下は同じようにサブネットを作る例。
variable "location" {
type = string
default = "japaneast"
}
resource "azurerm_resource_group" "rg" {
name = "rg-test"
location = var.location
}
# vNetを作成する
resource "azurerm_virtual_network" "vnet" {
name = "vnet-test-01"
location = var.location
resource_group_name = azurerm_resource_group.rg.name
address_space = ["10.0.0.0/16"]
}
# 各サブネットに対するパラメータのmapを作成する
locals {
subnet = [{
name = "subnet-0",
address_prefixes = "10.0.0.0/26"
}, {
name = "subnet-1",
address_prefixes = "10.0.0.64/26"
}, {
name = "subnet-2",
address_prefixes = "10.0.0.128/26"
}, {
name = "subnet-3",
address_prefixes = "10.0.0.192/26"
}]
}
resource "azurerm_subnet" "subnet" {
# 作成したmapを呼び出して処理
for_each = { for s in local.subnet : s.name => s }
name = each.value.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [each.value.address_prefixes]
resource_group_name = azurerm_resource_group.rg.name
}
出力
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "rg-test"
}
# azurerm_subnet.subnet["subnet-0"] will be created
+ resource "azurerm_subnet" "subnet" {
+ address_prefixes = [
+ "10.0.0.0/26",
]
+ enforce_private_link_endpoint_network_policies = (known after apply)
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "subnet-0"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "rg-test"
+ virtual_network_name = "vnet-test-01"
}
# azurerm_subnet.subnet["subnet-1"] will be created
+ resource "azurerm_subnet" "subnet" {
+ address_prefixes = [
+ "10.0.0.64/26",
]
+ enforce_private_link_endpoint_network_policies = (known after apply)
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "subnet-1"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "rg-test"
+ virtual_network_name = "vnet-test-01"
}
# azurerm_subnet.subnet["subnet-2"] will be created
+ resource "azurerm_subnet" "subnet" {
+ address_prefixes = [
+ "10.0.0.128/26",
]
+ enforce_private_link_endpoint_network_policies = (known after apply)
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "subnet-2"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "rg-test"
+ virtual_network_name = "vnet-test-01"
}
# azurerm_subnet.subnet["subnet-3"] will be created
+ resource "azurerm_subnet" "subnet" {
+ address_prefixes = [
+ "10.0.0.192/26",
]
+ enforce_private_link_endpoint_network_policies = (known after apply)
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "subnet-3"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "rg-test"
+ virtual_network_name = "vnet-test-01"
}
# azurerm_virtual_network.vnet will be created
+ resource "azurerm_virtual_network" "vnet" {
+ address_space = [
+ "10.0.0.0/16",
]
+ dns_servers = (known after apply)
+ guid = (known after apply)
+ id = (known after apply)
+ location = "japaneast"
+ name = "vnet-test-01"
+ resource_group_name = "rg-test"
+ subnet = (known after apply)
}
Plan: 6 to add, 0 to change, 0 to destroy.
今回はサブネットに利用するパラメータが少ないのであれですが、これがもっとパラメーターのあるリソースの場合で想像してください。すごく捗ることがイメージできると思います。
ちょっとだけ解説すると、
{ for s in local.subnet : s.name => s }
はsubnet-1 = {name = "subnet-0",address_prefixes = "10.0.0.0/26"}
…というMapを生成しています。これがループで繰り返され、subnet-1,subnet-2…と処理されるわけですね。
はい、これでメジャーな使い方は以上です!
for
はほかにもフィルターを設定するとかの機能もあるので、活用していきましょう。
参考