При использовании ресурса for_each
наша задача всегда состоит в том, чтобы написать выражение, которое создает карту, в которой есть один элемент на экземпляр, который мы хотим создать. В этом случае это, кажется, выражение, которое может расширить один объект, содержащий счет, чтобы вместо этого быть несколькими объектами из числа, указанного в счетчике.
Строительные блоки, которые мы можем использовать для этого в Terraform: :
for
выражений для проецирования одного значения коллекции в другое. - Функция
range
для генерации последовательностей целых чисел с учетом числа. - Функция
flatten
для преобразования нескольких вложенных списков в один плоский список.
Давайте рассмотрим этот шаг шаг за шагом. Мы начнем с вашего существующего выражения для загрузки данных из файлов JSON:
locals {
services = [
jsondecode(file("${path.module}/test_service_1.json")),
jsondecode(file("${path.module}/test_service_2.json")),
]
}
Результатом этого выражения является список объектов, по одному на файл. Далее мы расширим каждый из этих объектов в список объектов, длина которых указана в instance_count
:
locals {
service_instance_groups = [
for svc in local.services : [
for i in range(1, svc.instance_count+1) : {
instance_name = "${svc.instance_name}-${i}"
instance_type = svc.instance_type
subnet_type = svc.subnet_type
elb = svc.elb
data_volume = svc.data_volume
}
]
]
}
Результатом этого является список списков объектов, каждый из которых будет иметь уникальное значение instance_name
из-за конкатенации значения i
до конца.
Чтобы использовать for_each
, хотя нам понадобится плоская коллекция с одним элементом на экземпляр, поэтому мы будем использовать flatten
функция для достижения этой цели:
locals {
service_instances = flatten(local.service_instance_groups)
}
Теперь у нас снова есть список объектов, но с шестью элементами (по три от каждого из двух входных объектов) вместо двух.
Наконец, нам нужно спроецировать этот список на карту, ключами которой являются уникальные идентификаторы, которые Terraform будет использовать для отслеживания экземпляров. Я обычно предпочитаю делать этот последний шаг непосредственно внутри аргумента for_each
, потому что этот результат указывает c на этот вариант использования и вряд ли будет использоваться где-либо еще в модуле:
resource "aws_instance" "test_instance" {
for_each = {
for inst in local.service_instances : inst.instance_name => inst
}
ami = "amzn-ami-hvm-2018.03.0.20200206.0-x86_64-gp2"
instance_type = each.value.instance_type
tags = {
Name = "prod-app-${each.key}"
Env = "prod"
}
}
Это должно В результате Terraform планирует создавать экземпляры с адресами, такими как aws_instance.test_instance["front-2"]
.
. Я написал каждый из вышеперечисленных шагов отдельно, чтобы объяснить, чего достиг каждый, но на практике я обычно делал service_instance_groups
и service_instances
объединяется в одном выражении, потому что этот промежуточный service_instance_groups
результат вряд ли будет использоваться в другом месте. Объединяя все это в один пример, тогда:
locals {
services = [
jsondecode(file("${path.module}/test_service_1.json")),
jsondecode(file("${path.module}/test_service_2.json")),
]
service_instances = flatten([
for svc in local.services : [
for i in range(1, svc.instance_count+1) : {
instance_name = "${svc.instance_name}-${i}"
instance_type = svc.instance_type
subnet_type = svc.subnet_type
elb = svc.elb
data_volume = svc.data_volume
}
]
])
}
resource "aws_instance" "test_instance" {
for_each = {
for inst in local.service_instances : inst.instance_name => inst
}
ami = "amzn-ami-hvm-2018.03.0.20200206.0-x86_64-gp2"
instance_type = each.value.instance_type
tags = {
Name = "prod-app-${each.key}"
Env = "prod"
}
}
В качестве бонуса, помимо того, о чем вы здесь спрашивали, если вы дадите этим JSON файлам систематические c имена и группу их можно объединить в подкаталог, после чего вы можете использовать fileset
функцию *1056* Terraform , чтобы автоматически выбирать любые новые файлы, добавленные в этот каталог позже, без изменения конфигурации Terraform. Например:
locals {
services = [
for fn in fileset("${path.module}", "services/*.json") :
jsondecode(file("${path.module}/${fn}"))
]
}
В приведенном выше примере будет создан список, содержащий объект для каждого из файлов в подкаталоге services
, имена которых заканчиваются на .json
.