【问题标题】:Build dynamic list in Terraform?在 Terraform 中构建动态列表?
【发布时间】:2017-06-05 16:09:00
【问题描述】:

我正在尝试使用 count 在 terraform 中动态创建一些 aws 资源,然后构建一个包含所有已创建资源 ID 列表的输出变量

resource "aws_subnet" "subnet-1" {
  // If subnet_id value is supplied then we set count to 1 to create otherwise skip
  count = "${replace(replace(var.primary_subnet_cidr, "/^.+/", "1"), "/^$/", "0")}"
  tags = {
    Name = "subnet-1"
  }
  cidr_block = "${var.primary_subnet_cidr}"
  vpc_id = "${var.vpc_id}"
  availability_zone = "${var.aws_region}a"
}
resource "aws_subnet" "subnet-2" {
  // If subnet_id value is supplied then we set count to 1 to create otherwise skip
  count = "${replace(replace(var.secondary_subnet_cidr, "/^.+/", "1"),"/^$/", "0")}"
  tags = {
    Name = "subnet-2"
  }
  cidr_block = "${var.secondary_subnet_cidr}"
  vpc_id = "${var.vpc_id}"
  availability_zone = "${var.aws_region}b"
}

output "ids" {
      value = ["${aws_subnet.subnet-1.id}","${aws_subnet.subnet-2.id}"]
}

这个想法是通过提供其变量(primary_subnet_cidr 或 secondary_subnet_cidr)来仅创建您想要的子网。

动态创建它们可以正常工作,但我的问题是动态列表。

当尝试在类似 aws_db_subnet_group.subnet_ids 的列表中使用此列表时,它仅在两个子网都已创建时才有效,否则会引发以下错误

InvalidSubnet: Subnet IDs are required.

我也试过了

output "ids" {
      value = ["${aws_subnet.*.id}"]
}

但 terraform 似乎不支持资源级别的通配符。

我怀疑这与我生成输出列表的方式有关。有没有更好的方法来生成该动态列表,使其仅包含创建的资源?

【问题讨论】:

    标签: amazon-web-services terraform


    【解决方案1】:

    您很接近,但您需要使用正确的 Terraform 资源语法 RESOURCE-TYPE.RESOURCE-NAME.PROPERTY_NAMERESOURCE-TYPE.RESOURCE-NAME.*.PROPERTY_NAME。因此,在您的示例中,您将输出:

    output "ids" {
       value = "${aws_subnet.subnet-1.*.id}"
    }
    

    当然,您可能希望使用concat interpolation function 加入这两个列表:

    output "ids" {
       value = "${concat(aws_subnet.subnet-1.*.id, aws_subnet.subnet-2.*.id)}"
    }
    

    但是,如果您的 count 属性在任一资源上设置为 0,则可能会引发错误。为了解决这个问题,您可以使用其他插值函数的某种组合(例如coalescelist()),但恐怕我不记得确切的组合了。

    最后,对于您的 count 属性,这些 var 定义有点难以阅读。这里有一些其他的想法可以尝试:

    // Count = 1 if var.secondary_subnet_cidr is non-empty
    resource "aws_subnet" "subnet-1" {
      count = "${signum(length(var.secondary_subnet_cidr))}"
      ...
    }
    
    // Define a separate var altogether. Ideally, you could infer this, but sometimes it's the best you can do.
    resource "aws_subnet" "subnet-1" {
      count = "${var.should_create_secondary_subnet)}"
      ...
    }
    

    2017 年 6 月 6 日更新:感谢 @MartinAtkins 指出,从 Terraform 0.9.x 开始,您不再需要将列表输出包装在 [] 中。我已经更新了我的答案。

    2017 年 6 月 8 日更新:要从两个可能为空的列表中仅选择非空列表,请使用 ${element(concat(aws_subnet.subnet-1.*.id, aws_subnet.subnet-2.*.id), 0)}concat() 函数将接受一个空列表,element(..., 0) 将始终返回第一个元素,在这种情况下是非空列表。

    【讨论】:

    • 从 Terraform 0.9.6 开始,不再需要也不推荐包裹“splat 变量”的括号,因为它们将在未来的某个版本中被淘汰。不过,这里的东西暂时会起作用! :D
    • aws_subnet.subnet-1.*.id 不存在 afaik。正确的语法应该是 aws_subnet.subnet-1.id,因此我尝试从所有资源中获取所有 id 属性的列表 aws_subnet.*.id 但这不起作用。感谢有关合并列表和语法的提示。这正是我所关注的插值函数组合。我在想我得到的错误可能是由于其中一个资源的 count = 0 时列表中的空值。也许compact 可以解决这个问题。我稍后会试试看。
    • 尝试从不存在的列表中读取,因为count = 0 是一种常见情况。如果我记得我们是如何解决这个问题的,我会更新我的回复!
    • 好的,我找到了。这是${element(concat(aws_subnet.subnet-1.*.id, aws_subnet.subnet-2.*.id), 0)}concat() 函数将接受一个空列表,而element(..., 0) 将始终返回第一个元素,在这种情况下是非空列表。这能解决您的问题吗?
    • 谢谢。实际上,我想要一种方法来获取已创建资源 ID 的列表,以便用户可以提供子网 1 和/或子网 2,这样元素就不会起作用,但是删除元素并运行 concat 就可以了。谢谢
    【解决方案2】:

    我在尝试创建动态列表时发现了这个问题,我必须创建一个列表中包含可变数量的元素的列表。我正在创建一个字符串列表。我通过使用Terraform's compact method 解决了这个问题。基本上,这个列表会过滤掉我在列表中生成的空白元素。

    例如假设我有以下列表,

    myList = ["foo", barExists ? "bar" : ""]
    

    这将创建["foo", "bar"]["foo", ""]。如果您需要["foo", "bar"]["foo"],那么compact 函数就是您的朋友。

    myList = compact(["foo", barExists ? "bar" : ""])
    

    对于这种情况,上面的行会起作用。

    【讨论】:

      猜你喜欢
      • 2020-08-07
      • 1970-01-01
      • 2021-04-07
      • 2018-05-29
      • 1970-01-01
      • 2021-12-31
      • 2013-01-21
      • 2021-05-09
      • 1970-01-01
      相关资源
      最近更新 更多