Terraform - использовать вложенные циклы с количеством
Я пытаюсь использовать вложенный цикл в Terraform. У меня есть две переменные списка list_of_allowed_accounts
а также list_of_images
и ищет перебрать список list_of_images
а затем перебрать список list_of_allowed_accounts
,
Вот мой терраформный код.
variable "list_of_allowed_accounts" {
type = "list"
default = ["111111111", "2222222"]
}
variable "list_of_images" {
type = "list"
default = ["alpine", "java", "jenkins"]
}
data "template_file" "ecr_policy_allowed_accounts" {
template = "${file("${path.module}/ecr_policy.tpl")}"
vars {
count = "${length(var.list_of_allowed_accounts)}"
account_id = "${element(var.list_of_allowed_accounts, count.index)}"
}
}
resource "aws_ecr_repository_policy" "repo_policy_allowed_accounts" {
count = "${length(var.list_of_images)}"
repository = "${element(aws_ecr_repository.images.*.id, count.index)}"
count = "${length(var.list_of_allowed_accounts)}"
policy = "${data.template_file.ecr_policy_allowed_accounts.rendered}"
}
Это bash эквивалент того, что я пытаюсь сделать.
for image in alpine java jenkins
do
for account_id in 111111111 2222222
do
// call template here using variable 'account_id' and 'image'
done
done
6 ответов
Terraform не имеет прямой поддержки для такого рода вложенных итераций, но мы можем подделать это с помощью некоторой арифметики.
variable "list_of_allowed_accounts" {
type = "list"
default = ["1111", "2222"]
}
variable "list_of_images" {
type = "list"
default = ["alpine", "java", "jenkins"]
}
data "template_file" "ecr_policy_allowed_accounts" {
count = "${length(var.list_of_allowed_accounts) * length(var.list_of_images)}"
template = "${file("${path.module}/ecr_policy.tpl")}"
vars {
account_id = "${var.list_of_allowed_accounts[count.index / length(var.list_of_images)]}"
image = "${var.list_of_images[count.index % length(var.list_of_images)]}"
}
}
resource "aws_ecr_repository_policy" "repo_policy_allowed_accounts" {
count = "${data.template_file.ecr_policy_allowed_accounts.count}"
repository = "${var.list_of_images[count.index % length(var.list_of_images)]}"
policy = "${data.template_file.ecr_policy_allowed_accounts.*.rendered[count.index]}"
}
Поскольку мы хотим создать шаблон политики для каждой комбинации учетной записи и изображения, count
на template_file
Блок данных - это два умноженных вместе. Затем мы можем использовать операции деления и по модулю, чтобы вернуться из count.index
отдельным индексам в каждом списке.
Так как у меня не было копии вашего шаблона политики, я просто использовал заполнитель; таким образом, эта конфигурация дала следующий план:
+ aws_ecr_respository_policy.repo_policy_allowed_accounts.0
policy: "policy allowing 1111 to access alpine"
repository: "alpine"
+ aws_ecr_respository_policy.repo_policy_allowed_accounts.1
policy: "policy allowing 1111 to access java"
repository: "java"
+ aws_ecr_respository_policy.repo_policy_allowed_accounts.2
policy: "policy allowing 1111 to access jenkins"
repository: "jenkins"
+ aws_ecr_respository_policy.repo_policy_allowed_accounts.3
policy: "policy allowing 2222 to access alpine"
repository: "alpine"
+ aws_ecr_respository_policy.repo_policy_allowed_accounts.4
policy: "policy allowing 2222 to access java"
repository: "java"
+ aws_ecr_respository_policy.repo_policy_allowed_accounts.5
policy: "policy allowing 2222 to access jenkins"
repository: "jenkins"
Каждый экземпляр политики применяется к разной паре идентификатора учетной записи и изображения, охватывающих все комбинации.
Ответы здесь работают (я использовал их изначально), но я думаю, что у меня есть лучшее решение, использующее функцию setproduct от Terraform. Я не видел много примеров его использования в паутинах, но setproduct берет два набора (или, что более важно, два списка) и создает список наборов с каждой перестановкой входных данных. В моем случае я создаю параметры SSM:
variable "list1" {
type = "list"
default = ["outer1", "outer2"]
}
variable "list2" {
type = "list"
default = ["inner1", "inner2", "inner3"]
}
locals {
product = "${setproduct(var.list1, var.list2)}"
}
resource "aws_ssm_parameter" "params" {
count = "${length(var.list1) * length(var.list2)}"
name = "/${element(local.product, count.index)[0]}/${element(local.product, count.index)[1]}"
type = "String"
value = "somevalue"
overwrite = false
lifecycle { ignore_changes = ["value"] }
}
Это создает параметры SSM с именем:
/outer1/inner1
/outer1/inner2
/outer1/inner3
/outer2/inner1
/outer2/inner2
/outer2/inner3
Мой слабый маленький мозг может разобрать это немного легче, чем магия по модулю в других ответах!
К вашему сведению, если кто-то придет сюда из Google, если вы используете terraform 0.12, вам нужно будет использовать функцию floor в любом месте, где вы делите, иначе вы получите ошибку о частичных индексах.
account_id = "$ {var.list_of_allowed_accounts [floor(count.index / length (var.list_of_images)])}"
Хотя решения с setproduct и использованием modulo работают в некоторых случаях, есть более элегантное решение: использование модулей terraform (не путать с оператором modulo). При использовании модуля terraform появляется дополнительная возможность использовать метааргумент count.
Это решение особенно полезно, если применимы не все комбинации входных данных. Например, я использовал это решение для обработки списка проектов, где каждый проект имеет список уникальных ролей.
Например, на верхнем уровне вы можете использовать это:
variable "account_images" {
type = map(list(string))
default = {
"1111" = ["alpine", "java"],
"2222" = ["jenkins"]
}
}
module "account" {
source = "../accounts"
count = length(var.account_images)
account = keys(var.account_images)[count.index]
images = var.account_images[keys(var.account_images)[count.index]]
}
А затем добавьте дополнительный модуль terraform в../accounts
каталог с этим содержимым.
variable "account" {
type = string
}
variable "images" {
type = list(string)
}
data "template_file" "ecr_policy_allowed_accounts" {
count = length(var.images)}
template = "${file("${path.module}/ecr_policy.tpl")}"
vars {
account_id = var.account
image = var.images[count.index]
}
}
Я считаю, что этот подход легче понять.
В основном проблема заключается в данных "template_file", account_id не может быть установлен так, как вы думаете, так как count в вашем случае - это просто еще одна переменная, которая никогда не будет увеличена / изменена. Просто говорю, потому что я скучаю, чтобы узнать, что именно ваш вопрос.
У меня недостаточно очков репутации, чтобы добавить комментарий к ответу, предоставленному @ Martin Atkins, поэтому я публикую его ответ с небольшим изменением, которое решает проблему Terraform 20567
variable "list_of_allowed_accounts" {
type = "list"
default = ["1111", "2222"]
}
variable "list_of_images" {
type = "list"
default = ["alpine", "java", "jenkins"]
}
# workaround for TF issue https://github.com/hashicorp/terraform/issues/20567
locals {
policy_count = "${length(var.list_of_allowed_accounts) * length(var.list_of_images)}"
}
data "template_file" "ecr_policy_allowed_accounts" {
count = "${local.policy_count}"
template = "${file("${path.module}/ecr_policy.tpl")}"
vars {
account_id = "${var.list_of_allowed_accounts[count.index / length(var.list_of_images)]}"
image = "${var.list_of_images[count.index % length(var.list_of_images)]}"
}
}
resource "aws_ecr_repository_policy" "repo_policy_allowed_accounts" {
count = "${local.policy_count}"
repository = "${var.list_of_images[count.index % length(var.list_of_images)]}"
policy = "${data.template_file.ecr_policy_allowed_accounts.*.rendered[count.index]}"
}