Spec-Zone.ru › OpenTofu 1.10

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

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

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

Примечание

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

Базовый синтаксис​

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

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

Map:

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

Set строк:

Блок кода
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 — ключ map (или элемент set), соответствующий данному экземпляру.
  • each.value — значение map, соответствующее данному экземпляру. (Если задан set, это значение совпадает с each.key.)

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

Ключи map (или все значения в случае set строк) должны быть известными значениями, иначе появится сообщение об ошибке: зависимости 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 принимает map или set выражений. Однако, в отличие от большинства аргументов, значение for_each должно быть известно до выполнения OpenTofu каких-либо действий с удаленными ресурсами. Это означает, что for_each не может ссылаться на атрибуты ресурсов, значения которых становятся известны только после применения конфигурации (например, уникальный идентификатор, созданный удаленным API при создании объекта).

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

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

Цепочка for_each между ресурсами​

Поскольку ресурс, использующий for_each, при использовании в выражениях представляется как map объектов, в ситуациях, когда между двумя наборами объектов существует взаимно-однозначное соответствие, можно напрямую использовать один ресурс в качестве 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 различает сам блок и связанные с ним несколько экземпляров ресурса или модуля. Экземпляры идентифицируются ключом map (или элементом set) из значения, указанного для 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 нет литерального синтаксиса для значений типа set, но с помощью функции toset можно явно преобразовать список строк в set:

Блок кода
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}"
  }
}

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

Если вы пишете модуль с входной переменной, которая будет использоваться как set строк для 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.10/language/meta-arguments/for_each/

Spec-Zone.ru

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