Spec-Zone.ru › OpenTofu 1.11

Метааргумент for_each

По умолчанию блок ресурса настраивает один реальный объект инфраструктуры (аналогично, блок модуля однократно включает содержимое дочернего модуля в конфигурацию). Однако иногда возникает необходимость управлять несколькими похожими объектами (например, фиксированным пулом вычислительных экземпляров), не создавая для каждого отдельный блок. OpenTofu предоставляет два способа сделать это: count и for_each.

Если блок ресурса или модуля содержит аргумент for_each, значением которого является карта или набор строк, OpenTofu создаёт по одному экземпляру для каждого элемента этой карты или набора.

Примечание

В одном блоке ресурса или модуля нельзя использовать for_each вместе с count или enabled.

Основной синтаксис​

for_each — это метааргумент, определённый языком OpenTofu. Его можно использовать с модулями и ресурсами любого типа.

Метааргумент for_each принимает карту или набор строк и создаёт экземпляр для каждого элемента этой карты или набора. С каждым экземпляром связан отдельный объект инфраструктуры; при применении конфигурации каждый экземпляр создаётся, обновляется или удаляется независимо.

Карта:

Блок кода
resource "azurerm_resource_group" "rg" {
  for_each = {
    a_group = "eastus"
    another_group = "westus2"
  }
  name     = each.key
  location = each.value
}

Набор строк:

Блок кода
resource "aws_iam_user" "the-accounts" {
  for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
  name     = each.key
}

Дочерний модуль:

Блок кода
# my_buckets.tf
module "bucket" {
  for_each = toset(["assets", "media"])
  source   = "./publish_bucket"
  name     = "${each.key}_bucket"
}
Блок кода
# publish_bucket/bucket-and-cloudfront.tf
variable "name" {} # this is the input parameter of the module

resource "aws_s3_bucket" "example" {
  # Because var.name includes each.key in the calling
  # module block, its value will be different for
  # each instance of this module.
  bucket = var.name

  # ...
}

resource "aws_iam_user" "deploy_user" {
  # ...
}

Объект each​

В блоках, где задано for_each, в выражениях доступен дополнительный объект each, позволяющий изменять конфигурацию каждого экземпляра. У этого объекта есть два атрибута:

  • each.key — ключ карты (или элемент набора), соответствующий этому экземпляру.
  • each.value — значение карты, соответствующее этому экземпляру. (Если задан набор, это то же самое, что и each.key.)

Ограничения на значения, используемые в for_each​

Ключи карты (или все значения в случае набора строк) должны быть известными значениями. В противном случае появится сообщение об ошибке, указывающее, что у for_each есть зависимости, которые нельзя определить до применения конфигурации, и может потребоваться -target/-exclude.

Ключи for_each не могут быть результатом нечистых функций или зависеть от него, включая uuid, bcrypt и timestamp, поскольку их вычисление откладывается на этапе основной оценки.

Конфиденциальные значения, такие как конфиденциальные входные переменные, конфиденциальные выходные значения или конфиденциальные атрибуты ресурсов, нельзя использовать в качестве аргументов для for_each. Значение, используемое в for_each, служит для идентификации экземпляра ресурса и всегда отображается в выводе пользовательского интерфейса, поэтому конфиденциальные значения не допускаются. Попытка использовать конфиденциальные значения в качестве аргументов for_each приведёт к ошибке.

Если вы преобразуете значение, содержащее конфиденциальные данные, в аргумент для for_each, учтите, что большинство функций OpenTofu возвращают конфиденциальный результат, если им передан аргумент, содержащий конфиденциальные данные. Во многих случаях аналогичного результата можно добиться с помощью выражения for вместо функции, предназначенной для этой цели. Например, если нужно вызвать keys(local.map), где local.map — объект с конфиденциальными значениями (но неконфиденциальными ключами), можно создать значение для передачи в for_each с помощью toset([for k,v in local.map : k]).

Использование выражений в for_each​

Метааргумент for_each принимает карты или наборы, заданные с помощью выражений. Однако, в отличие от большинства аргументов, значение for_each должно быть известно до того, как OpenTofu выполнит какие-либо действия с удалёнными ресурсами. Это означает, что for_each не может ссылаться на атрибуты ресурсов, значения которых становятся известны только после применения конфигурации (например, на уникальный идентификатор, сгенерированный удалённым API при создании объекта).

Значение for_each должно быть картой или набором, содержащим по одному элементу для каждого требуемого экземпляра ресурса. Чтобы использовать последовательность в качестве значения for_each, необходимо применить выражение, явно возвращающее набор, например функцию toset. Чтобы избежать неожиданностей при преобразовании, аргумент for_each не преобразует списки или кортежи в наборы автоматически. Если требуется объявить экземпляры ресурсов на основе вложенной структуры данных или комбинаций элементов из нескольких структур данных, можно использовать выражения и функции OpenTofu, чтобы получить подходящее значение. Например:

  • Преобразовать многоуровневую вложенную структуру в плоский список, используя вложенные выражения for с функцией flatten.
  • Создать полный список комбинаций элементов из двух или более коллекций, используя функцию setproduct внутри выражения for.

Связывание for_each между ресурсами​

Поскольку ресурс, использующий for_each, в выражениях отображается как карта объектов, в ситуациях, когда между двумя наборами объектов существует взаимно-однозначное соответствие, можно напрямую использовать один ресурс в качестве for_each другого.

Например, в AWS с объектом aws_vpc часто связаны другие объекты, предоставляющие дополнительные службы для этой VPC, например «шлюз Интернета». Если вы объявляете несколько экземпляров VPC с помощью for_each, то можете связать этот for_each с другим ресурсом, чтобы объявить шлюз Интернета для каждой VPC:

Блок кода
variable "vpcs" {
  type = map(object({
    cidr_block = string
  }))
}

resource "aws_vpc" "example" {
  # One VPC for each element of var.vpcs
  for_each = var.vpcs

  # each.value here is a value from var.vpcs
  cidr_block = each.value.cidr_block
}

resource "aws_internet_gateway" "example" {
  # One Internet Gateway per VPC
  for_each = aws_vpc.example

  # each.value here is a full aws_vpc object
  vpc_id = each.value.id
}

output "vpc_ids" {
  value = {
    for k, v in aws_vpc.example : k => v.id
  }

  # The VPCs aren't fully functional until their
  # internet gateways are running.
  depends_on = [aws_internet_gateway.example]
}

Этот шаблон связывания явно и лаконично описывает отношение между экземплярами шлюза Интернета и экземплярами VPC, указывая OpenTofu, что ключи экземпляров обоих ресурсов всегда должны изменяться согласованно. Кроме того, обычно такая конфигурация становится понятнее тем, кто её сопровождает.

Ссылки на экземпляры​

Если задано for_each, OpenTofu различает сам блок и несколько связанных с ним экземпляров ресурсов или модулей. Экземпляры идентифицируются ключом карты (или элементом набора) из значения, переданного в for_each.

  • <TYPE>.<NAME> или module.<NAME> (например, azurerm_resource_group.rg) обозначает блок.
  • <TYPE>.<NAME>[<KEY>] или module.<NAME>[<KEY>] (например, azurerm_resource_group.rg["a_group"], azurerm_resource_group.rg["another_group"] и т. д.) обозначает отдельные экземпляры.

Это отличается от ресурсов и модулей без count или for_each, на которые можно ссылаться без индекса или ключа.

Аналогично, ресурсы из дочерних модулей с несколькими экземплярами в выводе плана и других местах пользовательского интерфейса получают префикс module.<NAME>[<KEY>]. Для модуля без count или for_each адрес не содержит индекс модуля, поскольку для ссылки на него достаточно имени модуля.

Примечание

Во вложенных блоках provisioner или connection специальный объект self ссылается на текущий экземпляр ресурса, а не на блок ресурса целиком.

Использование наборов​

В языке OpenTofu нет литерального синтаксиса для значений-наборов, однако можно использовать функцию toset, чтобы явно преобразовать список строк в набор:

Блок кода
locals {
  subnet_ids = toset([
    "subnet-abcdef",
    "subnet-012345",
  ])
}

resource "aws_instance" "server" {
  for_each = local.subnet_ids

  ami           = "ami-a1b2c3d4"
  instance_type = "t2.micro"
  subnet_id     = each.key # note: each.key and each.value are the same for a set

  tags = {
    Name = "Server ${each.key}"
  }
}

При преобразовании списка в набор порядок элементов списка теряется, а повторяющиеся элементы удаляются. toset(["b", "a", "b"]) создаст набор, содержащий только "a" и "b", в произвольном порядке; второй "b" будет отброшен.

Если вы пишете модуль с входной переменной, которая будет использоваться в for_each как набор строк, можно задать для неё тип set(string), чтобы не выполнять явное преобразование типа:

Блок кода
variable "subnet_ids" {
  type = set(string)
}

resource "aws_instance" "server" {
  for_each = var.subnet_ids

  # (and the other arguments as above)
}

Copyright (c) The OpenTofu Authors
Copyright (c) 2014 HashiCorp, Inc.
Mozilla Public License, version 2.0
https://opentofu.org/docs/v1.11/language/meta-arguments/for_each/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API