LoginSignup
19
1

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_eachmapを定義する前提で、その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.keyeach.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

19
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
1