Terraformの繰り返し処理の種類
Terraformにはfor
,for_each
,count
の3つの繰り返し処理があります。
要約
分類 | 説明 | |
---|---|---|
式 | for | 繰り返し処理を行える。感覚的にはPythonなどと同じイメージ。今回は説明しない。 |
メタ引数 | for_each | mapを利用した繰り返し処理。 |
count | 単純な指定値による繰り返し処理。もっともイメージがつきやすい。 |
今回はfor
には触れません。
count
countはもっとも理解しやすい繰り返し処理ではなかろーか。
例えば以下のようにします。(コードは先日書いた記事のものから。Providerは省略)
# リージョン指定
variable "location" {
type = string
default = "japaneast"
}
# サブネットの名前リスト
variable "subnet_name" {
description = ""
default = [
"subnet-1",
"subnet-2",
"subnet-3",
"subnet-4"
]
}
# サブネットのIPリスト
variable "subnet_address" {
default = [
"10.0.0.0/26",
"10.0.0.64/26",
"10.0.0.128/26",
"10.0.0.192/26",
]
}
# リソースグループを作成する
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"]
}
# サブネットを作成する
resource "azurerm_subnet" "subnet" {
count = length(var.subnet_name)
name = element(var.subnet_name, count.index)
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [element(var.subnet_address, count.index)]
resource_group_name = azurerm_resource_group.rg.name
}
countを利用しているのは最後のsubnet作成のときです。このコードを実行すると、4つサブネットが生成されます。
count
は単純に 数字の回数分だけ そのブロックの処理を繰り返します。
count.index
を利用するとその繰り返した回数を取得できます。
要素がlistで管理されている場合は、countで指定するとリストの長さの分だけ繰り返す(countとlengthを合わせて利用する)ので簡単に作成できます。
以下plan結果。(subnetのみ抜粋)
# 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-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[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-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"
}
…(以下省略)
しかしながら、count
だと困ることがあります。
例えばcount
で作成したサブネットの名前を別リソースで呼び出したいとき、それぞれ以下のように呼び出すのですが…
# 一つ目のサブネット
azurerm_subnet.subnet[0].name
# 二つ目のサブネット
azurerm_subnet.subnet[1].name
# 三つ目のサブネット
azurerm_subnet.subnet[2].name
# 四つ目のサブネット
azurerm_subnet.subnet[3].name
これだと、ソースコード上はazurerm_subnet.subnet[0]
とazurerm_subnet.subnet[3]
の違いが一目でわからない。
「このサブネットってなんのリソースで利用するサブネットだっけ…?」 などなど、コードの可読性に大きく影響します。
また、もしsubnet-2
が不要になり、length(var.subnet_name)
が3となった場合、順番的にazurerm_subnet.subnet[3]
が生成されなくなります。
コードの修正はそれほど時間がかからないかもしれないですが、すでにapply済みであった場合、azurerm_subnet.subnet[3]
がなくなることでその上に構築されたリソースも含めて再作成・再設定が行われることになり、大きな変更となってしまいます。
そのため、昨今では基本的にはfor_each
を利用すべき、といわれています。
for_each
for_each
はmap
を定義する前提で、そのmap
の要素分繰り返すことができます。
イメージつきにくいかと思いますが、上の例と同じことをやると、以下のようになります。
variable "location" {
type = string
default = "japaneast"
}
# 名前とIPのセットのmap
variable "subnet" {
type = map(string)
default = {
subnet-1 = "10.0.0.0/26"
subnet-2 = "10.0.0.64/26"
subnet-3 = "10.0.0.128/26"
subnet-4 = "10.0.0.192/26"
}
}
# リソースグループを作成する
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"]
}
resource "azurerm_subnet" "subnet" {
# var.subnet_nameの数分だけ繰り返す
for_each = var.subnet
name = each.key
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [each.value]
resource_group_name = azurerm_resource_group.rg.name
}
subnet-1 = "10.0.0.0/26"
のうち、右側の「subnet-1」がkey,左側の「"10.0.0.0/26"」がvalue。
これらをeach.key
やeach.value
で参照できます。
出力
(前略)
# azurerm_subnet.subnet["subnet-1"] 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-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.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-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"
}
…(以下略)
countの場合はazurerm_subnet.subnet[0].name
で指定が必要でしたが、for_each
の場合はazurerm_subnet.subnet["subnet-1"].name
で指定できます。
つまり、サブネット名のKeyで区別がつきやすいです。これがfor_each
の利点になります。また、特定の要素を削除したとしても他への影響が少ないです。
for
と合わせると、また違った繰り返し処理が実装できますが…。それはまた今度ということで。
(追記)以下で紹介しています。
参考(公式ドキュメント)
count
https://developer.hashicorp.com/terraform/language/meta-arguments/count
for_each
https://developer.hashicorp.com/terraform/language/meta-arguments/for_each