Terraform-使用带计数的嵌套循环


18

我正在尝试在Terraform中使用嵌套循环。我有两个列表变量list_of_allowed_accountslist_of_images,并且希望遍历list list_of_images,然后遍历list list_of_allowed_accounts

这是我的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}"
}

这相当于我正在尝试做的重击。

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

Answers:


34

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"

每个策略实例都适用于不同的帐户ID和图像对,涵盖所有组合。


2
如果您想扩展配置(例如添加新帐户或/和映像),将使您麻烦,而资源将映射到不同的索引,但是,如果删除并重新创建它们不是问题,则可以正常工作。
balazs

1
@ justin-grote的回答很明确:在terraform 0.12中,您需要在要进行除法的任何位置使用floor函数,否则您将得到有关部分索引的错误。account_id = var.list_of_allowed_accounts[floor(count.index / length(var.list_of_images))]
chriscatfr

7

答案确实有效(我最初使用它们),但是我认为使用Terraform的setproduct函数可以提供更好的解决方案。我还没有看到在互连网上使用它的许多示例,但是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"] }
}

这将创建名为:

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

我w弱的小脑袋比其他答案中的模魔术更容易解析它!


我将尝试您的解决方案。我同意这似乎要好得多。但是,为什么要使用${length(var.list1) * length(var.list2)}而不是${length(local.product)}计数呢?
chriscatfr

我必须等到我的客户开始使用v0.12为止:(怪不得您为什么找不到很多资源
。– chriscatfr

没有理由,${length(local.product)}可能还会有更多。另外,我可以肯定地确定setproduct()0.12之前的版本存在((我认为链接页面顶部的消息只是针对所有0.11文档的通用警告吗?)
Kyle

4

仅供参考,如果有人来自Google,如果您使用的是terraform 0.12,则需要在要进行除法的任何位置使用floor函数,否则会出现关于部分索引的错误。

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


我希望在尝试数学方法之前,请一直阅读SO页的所有内容,以发现这一瑰宝。这就是我如何使其与floor(count.index / 8)一起使用。感谢您的发布。
bytejunkie

@kyle解决方案中的0.12 setproduct()似乎更容易。
chriscatfr

如果你在Terraform 0.12,那么为什么不使用新加入的forfor_each和/或动态嵌套块的语言结构来实现的东西少一点混乱?
TrinitronX

0

基本上,问题出在数据“ template_file”中,无法以您认为的方式设置account_id,因为您所用的计数只是另一个永远不会增加/更改的变量。只是说,因为我很想知道您的问题是什么。


0

我的信誉点不足,无法在@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]}"
} 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.