Terraform: usa loop annidati con conteggio


18

Sto cercando di utilizzare un loop nidificato in terraform. Ho due variabili di elenco list_of_allowed_accountse list_of_images, e cercando di scorrere su elenco list_of_imagese quindi scorrere su elenco list_of_allowed_accounts.

Ecco il mio codice 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}"
}

Questo è un equivalente bash di quello che sto cercando di fare.

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

Risposte:


34

Terraform non ha un supporto diretto per questo tipo di iterazione nidificata, ma possiamo falsificarlo con un po 'di aritmetica.

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

Dal momento che vogliamo creare un modello di criteri per ogni combinazione di conto e dell'immagine, il countsul template_fileblocco di dati è il due moltiplicato insieme. Possiamo quindi utilizzare le operazioni di divisione e modulo per tornare dagli count.indexindici separati in ciascun elenco.

Dal momento che non avevo una copia del tuo modello di politica ho appena usato un segnaposto; questa configurazione ha quindi fornito il seguente piano:

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

Ogni istanza della politica si applica a una diversa coppia di ID account e immagine, coprendo tutte le combinazioni.


2
Ti causerà problemi se vuoi estendere la configurazione, come aggiungere un nuovo account o / e un'immagine, che le tue risorse verranno mappate su indici diversi, tuttavia se eliminarli e ricrearli non è un problema questo funziona bene.
balazs

1
@ justin-grote ha un punto nella sua risposta: in terraform 0.12 dovrai usare la funzione floor ovunque tu divida, altrimenti otterrai un errore sugli indici parziali. account_id = var.list_of_allowed_accounts[floor(count.index / length(var.list_of_images))]
Chriscatfr,

7

Le risposte qui funzionano (le ho usate inizialmente), ma penso di avere una soluzione migliore usando la funzione setproduct di Terraform . Non ne ho visti molti esempi usati attorno agli interwebs, ma setproduct prende due set (o, soprattutto, due elenchi) e produce un elenco di set con ogni permutazione degli input. Nel mio caso sto creando parametri 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"] }
}

Questo crea parametri SSM denominati:

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

Il mio piccolo cervello wimpy può analizzarlo un po 'più facilmente del modulo magico nelle altre risposte!


Proverò la tua soluzione. Sono d'accordo che sembra molto meglio. Ma perché usi ${length(var.list1) * length(var.list2)}invece che ${length(local.product)}per il conteggio?
chriscatfr,

Dovrò aspettare fino a quando il mio cliente inizierà a utilizzare v0.12 :( non c'è da stupirsi perché non hai trovato molte fonti.
Chriscatfr,

Nessun motivo, ${length(local.product)}probabilmente ne guadagna di più da allora. Inoltre, sono abbastanza certo che setproduct()esiste prima dello 0.12, (il messaggio nella parte superiore della pagina collegata è solo un avvertimento generico per tutti i loro 0,11 documenti, penso?)
Kyle

4

Cordiali saluti, se qualcuno viene qui da Google, se si utilizza terraform 0.12, sarà necessario utilizzare la funzione floor ovunque si divida, altrimenti verrà visualizzato un errore relativo agli indici parziali.

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


Vorrei aver letto fino in fondo la pagina SO per scoprire questa gemma prima di provare l'approccio matematico. È così che l'ho fatto funzionare con floor (count.index / 8). Grazie per la pubblicazione.
bytejunkie,

con 0.12 setproduct () dalla soluzione di @kyle sembra più facile.
chriscatfr,

Se siete su Terraform 0,12, allora perché non utilizzare le aggiunte di recente for, for_eache / o costrutti dinamici nidificato blocchi lingua di implementare qualcosa di un po 'meno confusione?
TrinitronX,

0

Fondamentalmente il problema è nei dati "template_file", l'account_id non può essere impostato come pensi poiché il conteggio nel tuo caso è solo un altro var che non viene mai incrementato / modificato. Sto solo dicendo che mi manca vedere esattamente qual è la tua domanda.


0

Non ho abbastanza punti reputazione per aggiungere un commento alla risposta fornita da @ Martin Atkins , quindi sto postando la sua risposta con una leggera modifica, che funziona attorno al problema 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]}"
} 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.