在Terraform中构建动态列表?

时间:2017-06-05 16:09:00

标签: amazon-web-services 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似乎并不支持资源级别的通配符。

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

2 个答案:

答案 0 :(得分:2)

您很接近,但您需要使用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开始,您不再需要在[和{{1}中包装列表输出}。我已经更新了我的答案。

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

答案 1 :(得分:0)

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

例如假设我有以下列表,

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

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

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

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