Terraforma: use bucles anidados con recuento

18

Estoy tratando de usar un bucle anidado en terraform. Tengo dos variables de lista list_of_allowed_accountsy list_of_images, y estoy buscando iterar sobre la lista list_of_imagesy luego iterar sobre la lista list_of_allowed_accounts.

Aquí está mi código de terraform.

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}"
}

Este es un equivalente bash de lo que estoy tratando de hacer.

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
vikas027
fuente

Respuestas:

34

Terraform no tiene soporte directo para este tipo de iteración anidada, pero podemos fingirlo con algo de aritmética.

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]}"
}

Dado que queremos crear una plantilla de política para cada combinación de cuenta e imagen, counten el template_filebloque de datos se multiplican los dos. Luego podemos usar las operaciones de división y módulo para volver count.indexa los índices separados en cada lista.

Como no tenía una copia de su plantilla de póliza, simplemente utilicé una de marcador de posición; esta configuración dio el siguiente plan:

+ 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"

Cada instancia de política se aplica a un par diferente de ID e imagen de cuenta, que cubre todas las combinaciones.

Martin Atkins
fuente
2
Le causará problemas si desea ampliar la configuración, como agregar una nueva cuenta y / o una imagen, que sus recursos se asignarán a diferentes índices, sin embargo, si eliminarlos y recrearlos no es un problema, esto funciona bien.
balazs
1
@ justin-grote tiene un punto en su respuesta: en terraform 0.12 necesitará usar la función de piso en cualquier lugar donde divida, o de lo contrario obtendrá un error sobre índices parciales. account_id = var.list_of_allowed_accounts[floor(count.index / length(var.list_of_images))]
chriscatfr
7

Las respuestas aquí funcionan (las usé inicialmente), pero creo que tengo una mejor solución usando la función setproduct de Terraform . No he visto muchos ejemplos de su uso alrededor de las interwebs, pero setproduct toma dos conjuntos (o más importante, dos listas) y produce una lista de conjuntos con cada permutación de las entradas. En mi caso estoy creando parámetros 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"] }
}

Esto crea parámetros SSM llamados:

/outer1/inner1
/outer1/inner2
/outer1/inner3
/outer2/inner1
/outer2/inner2
/outer2/inner3

¡Mi pequeño cerebro debilucho puede analizar esto un poco más fácil que el módulo mágico en las otras respuestas!

Kyle
fuente
Probaré tu solución. Estoy de acuerdo en que parece mucho mejor. Pero, ¿por qué lo usas en ${length(var.list1) * length(var.list2)}lugar de ${length(local.product)}contar?
chriscatfr
Tendré que esperar hasta que mi cliente comience a usar v0.12 :( no es de extrañar por qué no encontró muchas fuentes.
chriscatfr
No hay razón, ${length(local.product)}probablemente hace más desde entonces. Además, estoy bastante seguro setproduct()existe pre-0,12, (el mensaje en la parte superior de la página enlazada es sólo una advertencia genérica para todos sus documentos 0.11, creo?)
Kyle
4

Para su información, si alguien viene de Google, si está usando terraform 0.12, necesitará usar la función de piso en cualquier lugar donde divida, o de lo contrario obtendrá un error sobre los índices parciales.

account_id = var.list_of_allowed_accounts [ floor (count.index / length (var.list_of_images))]

Justin Grote
fuente
Ojalá hubiera leído toda la página SO para descubrir esta joya antes de probar el enfoque matemático. Así es como lo hice funcionar con floor (count.index / 8). Gracias por publicar.
bytejunkie
con 0.12 setproduct () de la solución de @kyle parece más fácil.
chriscatfr
Si estás en Terraform 0,12, entonces ¿por qué no usar los recién añadidos for, for_eachy / o construcciones dinámicas anidado bloques de lenguaje para implementar algo un poco menos confuso?
TrinitronX
0

Básicamente, el problema está en los datos "template_file", el account_id no se puede configurar de la manera que cree que lo hará, ya que el recuento en su caso es solo otra var que nunca se incrementa / cambia. Solo digo ya que extraño ver cuál es exactamente tu pregunta.

IgorC
fuente
0

No tengo suficientes puntos de reputación para agregar un comentario a la respuesta proporcionada por @ Martin Atkins , por lo que estoy publicando su respuesta con una ligera modificación, que funciona alrededor del problema 20567 de Terraform

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]}"
} 
usuario9192156
fuente