Соберем все это вместе и динамически сгенерируем блоки tag в ресурсе aws_autoscaling_group с помощью for_each:
resource "aws_autoscaling_group" "example" {
launch_configuration = aws_launch_configuration.example.name
vpc_zone_identifier = data.aws_subnet_ids.default.ids
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
min_size = var.min_size
max_size = var.max_size
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
dynamic "tag" {
for_each = var.custom_tags
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
Теперь, если выполнить terraformapply, план действий будет выглядеть примерно так:
$ terraform apply
Terraform will perform the following actions:
# aws_autoscaling_group.example will be updated in-place
~ resource "aws_autoscaling_group" "example" {
(...)
tag {
key = "Name"
propagate_at_launch = true
value = "webservers-prod"
}
+ tag {
+ key = "Owner"
+ propagate_at_launch = true
+ value = "team-foo"
}
+ tag {
+ key = "DeployedBy"
+ propagate_at_launch = true
+ value = "terraform"
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
Введите yes, чтобы развернуть изменения. В веб-консоли EC2 должны появиться новые теги, как показано на рис. 5.1.
Рис. 5.1. Динамические теги группы автомасштабирования
Циклы на основе выражений for
Вы уже знаете, как циклически перебирать ресурсы и вложенные блоки. Но если с помощью цикла нужно сгенерировать лишь одно значение? Немного отвлечемся и рассмотрим некоторые примеры, не связанные с кластером веб-серверов. Представьте, что вы написали код Terraform, который принимает на вход список имен:
variable "names" {
description = "A list of names"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
Как бы вы перевели все эти имена в верхний регистр? В языке программирования общего назначения, таком как Python, можно было бы написать следующий цикл:
names = ["neo", "trinity", "morpheus"]
upper_case_names = []
for name in names:
upper_case_names.append(name.upper())
print upper_case_names
# Выводит: ['NEO', 'TRINITY', 'MORPHEUS']
Python позволяет выразить точно такой же код одной строчкой, используя синтаксис под названием «абстракция списков» (list comprehension):
names = ["neo", "trinity", "morpheus"]
upper_case_names = [name.upper() for name in names]
print upper_case_names
# Выводит: ['NEO', 'TRINITY', 'MORPHEUS']
Python также позволяет отфильтровать итоговый список по заданному выражению:
names = ["neo", "trinity", "morpheus"]
short_upper_case_names = [name.upper() for name in names if len(name) < 5]
print short_upper_case_names
# Выводит: ['NEO']
Terraform предлагает похожие возможности в виде выражения for (не путать с выражением for_each из предыдущего раздела). У него следующий базовый синтаксис:
[for :
LIST — это список, который нужно перебрать, ITEM — имя локальной переменной, которое будет назначено каждому элементу списка, а OUTPUT — выражение, которое каким-то образом преобразует ITEM. Например, вот код Terraform для перевода списка имен в var.names в верхний регистр:
variable "names" {
description = "A list of names"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
output "upper_names" {
value = [for name in var.names : upper(name)]
}
Если выполнить для этого кода команду terraformapply, получится следующий вывод:
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
upper_names = [
"NEO",
"TRINITY",
"MORPHEUS",
]
Как и с абстракциями списков в Python, вы можете задать выражение для фильтрации полученного результата:
variable "names" {
description = "A list of names"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
output "short_upper_names" {
value = [for name in var.names : upper(name) if length(name) < 5]
}
Выполнив terraformapply для этого кода, вы получите следующее:
short_upper_names = [
"NEO",
]
Выражение for в Terraform также поддерживает циклический перебор ассоциативных массивов с использованием такого синтаксиса:
[for
MAP — это ассоциативный массив, который нужно перебрать, KEY и VALUE — имена локальных переменных, которые назначаются каждой паре «ключ — значение» в MAP, а OUTPUT — выражение, которое каким-то образом преобразует KEY и VALUE. Например:
variable "hero_thousand_faces" {
description = "map"
type = map(string)
default = {
neo = "hero"
trinity = "love interest"
morpheus = "mentor"
}
}
output "bios" {
value = [for name, role in var.hero_thousand_faces : "${name} is the ${role}"]
}
Если выполнить terraformapply для этого кода, получится следующее:
map_example = [
"morpheus is the mentor",
"neo is the hero",
"trinity is the love interest",
]
Выражение for может вернуть ассоциативный массив вместо списка, используя следующий синтаксис:
# Циклический перебор списков
[for
# Циклический перебор ассоциативных массивов
{for
Разница лишь в том, что: а) выражение помещается в фигурные скобки вместо прямоугольных; б) на каждой итерации выводится не только значение, но еще и ключ, отделенный от него стрелкой. Например, так можно перевести в верхний регистр все ключи и значения ассоциативного массива:
variable "hero_thousand_faces" {
description = "map"
type = map(string)
default = {
neo = "hero"
trinity = "love interest"
morpheus = "mentor"
}
}
output "upper_roles" {
value = {for name, role in var.hero_thousand_faces : upper(name) => upper(role)}
}
При выполнении этот код возвращает такой вывод:
upper_roles = {
"MORPHEUS" = "MENTOR"
"NEO" = "HERO"
"TRINITY" = "LOVE INTEREST"
}
Циклы с использованием строковой директивы for
Ранее в этой книге вы узнали о строковой интерполяции, которая позволяет ссылаться на код Terraform внутри строки:
"Hello, ${var.name}"
С помощью строковых директив подобный синтаксис можно использовать и для управляющих конструкций (вроде циклов for и выражения if), но вместо знака доллара (${…}) перед фигурными скобками указывается знак процента (%{…}).
Строковая директива for имеет следующий синтаксис:
%{ for
COLLECTION — список или ассоциативный массив, который нужно перебрать, ITEM — имя локальной переменной, которое назначается каждому элементу COLLECTION, а BODY — это то, что выводится на каждой итерации (здесь можно ссылаться на ITEM). Например:
variable "names" {
description = "Names to render"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
output "for_directive" {
value = < %{ for name in var.names } ${name} %{ endfor } EOF } Выполнив terraformapply, вы получите следующий вывод: $ terraform apply Apply complete! Resources: 0 added, 0 changed, 0 destroyed. Outputs: for_directive = neo trinity morpheus Обратите внимание на дополнительные символы перехода на новую строку. В строковой директиве можно указать маркер ~, чтобы удалить все пробельные символы (пробелы и перенос строки) перед ней (если маркер находится в начале директивы) или после нее (если маркер находится в конце директивы): output "for_directive_strip_marker" { value = < %{~ for name in var.names } ${name} %{~ endfor } EOF } Эта обновленная версия дает такой вывод: for_directive_strip_marker = neo trinity morpheus Условные выражения Как и циклы, условные выражения в Terraform бывают нескольких видов, каждый из которых рассчитан немного на другой сценарий использования. •Параметр count для условных ресурсов. • Выраженияfor_eachиfor для условных ресурсов и их вложенных блоков. • Строковая директива if для условных выражений внутри строк. Рассмотрим их все по очереди. Условные выражения с использованием параметра count