Terraform - Use loops aninhados com count


18

Estou tentando usar um loop aninhado em terraform. Eu tenho duas variáveis ​​de lista list_of_allowed_accountse list_of_images, e olhando para iterar sobre a lista list_of_imagese, em seguida, iterar sobre a lista list_of_allowed_accounts.

Aqui está o meu 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}"
}

Isso é o equivalente do que estou tentando fazer.

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

Respostas:


34

O Terraform não tem suporte direto para esse tipo de iteração aninhada, mas podemos falsificá-lo com alguma 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]}"
}

Desde que nós queremos criar um modelo de política para cada combinação de conta e de imagem, o countno template_filebloco de dados é os dois juntos multiplicada. Em seguida, podemos usar as operações de divisão e módulo para retornar count.indexaos índices separados em cada lista.

Como não tinha uma cópia do seu modelo de política, usei apenas um espaço reservado; essa configuração deu o seguinte plano:

+ 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 instância da política se aplica a um par diferente de ID e imagem da conta, cobrindo todas as combinações.


2
Causará problemas se você desejar estender a configuração, como adicionar uma nova conta ou / e uma imagem, para que seus recursos sejam mapeados para índices diferentes; no entanto, se excluir e recriá-los não for um problema, isso funcionará bem.
Balazs

11
@ justin-grote tem razão em sua resposta: no terraform 0.12, você precisará usar a função floor em qualquer lugar que você dividir, ou então você receberá um erro sobre índices parciais. account_id = var.list_of_allowed_accounts[floor(count.index / length(var.list_of_images))]
Chriscatfr 18/08/19

7

As respostas aqui funcionam (eu as usei inicialmente), mas acho que tenho uma solução melhor usando a função setproduct da Terraform . Não vi muitos exemplos usados ​​nas interwebs, mas o setproduct usa dois conjuntos (ou mais importante, duas listas) e produz uma lista de conjuntos com todas as permutações das entradas. No meu caso, estou criando 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"] }
}

Isso cria parâmetros do SSM denominados:

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

Meu pequeno cérebro fraco pode analisar isso um pouco mais fácil do que a magia do módulo nas outras respostas!


Vou tentar sua solução. Eu concordo que parece muito melhor. Mas por que você usa em ${length(var.list1) * length(var.list2)}vez de ${length(local.product)}contar?
Chriscatfr 18/08/19

Eu vou ter que esperar até o meu cliente começa a usar v0.12 :( nenhuma maravilha porque você não encontrou muitas fontes.
chriscatfr

Não há razão, ${length(local.product)}provavelmente faz mais desde então. Além disso, estou bastante certo setproduct()existe pré-0,12, (a mensagem no topo da página vinculada é apenas um aviso genérico para todos os seus 0,11 docs, eu acho?)
Kyle

4

Para sua informação, se alguém vier aqui do Google, se você estiver usando o terraform 0.12, será necessário usar a função floor em qualquer lugar que você dividir, ou então você receberá um erro sobre índices parciais.

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


Eu gostaria de ler toda a página SO para descobrir essa jóia antes de tentar a abordagem matemática. Foi assim que consegui trabalhar com o floor (count.index / 8). Obrigado por publicar.
Bytejunkie

com 0,12 setproduct () da solução da @kyle parece mais fácil.
Chriscatfr 18/08/19

Se você estiver em Terraform 0,12, então porque não usar os recém-adicionado for, for_eache ou construções / dinâmicos aninhados blocos de linguagem para implementar algo um pouco menos confuso?
TrinitronX

0

Basicamente, o problema está nos dados "template_file", o account_id não pode ser definido da maneira que você pensa, uma vez que a contagem no seu caso é apenas outra var que nunca é incrementada / alterada. Só estou dizendo desde que sinto falta de ver exatamente qual é a sua pergunta.


0

Não tenho pontos de reputação suficientes para adicionar um comentário à resposta fornecida por @ Martin Atkins , por isso estou postando sua resposta com uma pequena modificação, que funciona em torno do Terraform edição 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]}"
} 
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.