在 Terraform 中构建动态列表?

Build dynamic list in Terraform?

我正在尝试使用 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 似乎不支持资源级别的通配符。

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

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

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 日更新: 要 select 只有两个列表中的一个可能为空的非空列表,请使用 ${element(concat(aws_subnet.subnet-1.*.id, aws_subnet.subnet-2.*.id), 0)}concat() 函数将接受一个空列表,而 element(..., 0) 将始终 returns 第一个元素,在本例中是非空列表。

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

例如假设我有以下列表,

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

这将创建 ["foo", "bar"]["foo", ""]。如果您需要 ["foo", "bar"]["foo"],紧凑型函数就是您的朋友。

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

对于这种情况,上述行将起作用。