7

Terraform 最近引入了set数据类型,在此页面上描述为:

set(...):没有任何辅助标识符或排序的唯一值的集合。

很难找到有关如何从 Terraform 集中检索值的文档。使用地图,您可以对键进行索引:

password = var.passwords["kevin"]

使用列表,您可以索引元素编号:

a_record = var.records[1]

但是,我不能使用其中任何一种方法从集合中检索值,即使是只有一个项目的集合。

在其他地方,文档提到for_each了一种从集合中获取值的方法。

variable "subnet_ids" {
  type = list(string)
}

resource "aws_instance" "server" {
  for_each = toset(var.subnet_ids)

  ami           = "ami-a1b2c3d4"
  instance_type = "t2.micro"
  subnet_id     = each.key # note: each.key and each.value are the same for a set

  tags = {
    Name = "Server ${each.key}"
  }
}

元变量是for_each访问集合中值的唯一方法吗?

4

2 回答 2

16

您还可以通过首先将集合转换为列表然后将其作为列表访问来对集合进行切片。

举个例子:

variable "set" {
  type = set(string)
  default = [
    "foo",
    "bar",
  ]
}

output "set" {
  value = var.set
}

output "set_first_element" {
  value = var.set[0]
}

这会出错,因为索引不能直接访问集合:

Error: Invalid index

  on main.tf line 14, in output "set_first_element":
  14:   value = var.set[0]

This value does not have any indices.

如果您改为使用该tolist函数强制转换它,那么您可以按预期访问它:

output "set_to_list_first_element" {
  value = tolist(var.set)[0]
}
Outputs:

set = [
  "bar",
  "foo",
]
set_to_list_first_element = bar

请注意,这将返回bar而不是foo. 严格来说,集合在 Terraform 中是无序的,因此您根本不能依赖顺序,并且仅在一次 Terraform 运行期间保持一致,但实际上它们是稳定的,并且在set(string)按字典顺序排序的情况下:

当集合转换为列表或元组时,元素将按任意顺序排列。如果集合的元素是字符串,它们将按字典顺序排列;其他元素类型的集合不保证任何特定的元素顺序。

出现这种情况的主要地方是,如果您正在处理在 Terraform 0.12 中返回集合类型但需要使用奇异值的资源或数据源。一个基本的例子可能是这样的:

data "aws_subnet_ids" "private" {
  vpc_id = var.vpc_id

  tags = {
    Tier = "Private"
  }
}

resource "aws_instance" "app" {
  ami           = var.ami
  instance_type = "t2.micro"
  subnet_id     = tolist(data.aws_subnet_ids.example.ids)[0]
}

这将在标记为的子网中创建一个 EC2 实例,Tier = Private但不会对其应在的位置设置其他限制。

虽然您提到能够访问值,但您也可以使用表达式for_each循环遍历集合:for

variable "set_of_objects" {
  type = set(object({
    port    = number
    service = string
  }))

  default = [
    {
      port    = 22
      service = "ssh"
    },
    {
      port    = 80
      service = "http"
    },
  ]
}

output "set_of_objects" {
  value = var.set_of_objects
}

output "list_comprehension_over_set" {
  value = [ for obj in var.set_of_objects : upper(obj.service) ]
}

然后输出以下内容:

list_comprehension_over_set = [
  "SSH",
  "HTTP",
]
set_of_objects = [
  {
    "port" = 22
    "service" = "ssh"
  },
  {
    "port" = 80
    "service" = "http"
  },
]
于 2020-07-31T21:18:19.500 回答
0

In Terraform v0.15 and later, you can use one function.

If you have zero or one elements. thanks @Camilo Silva for the note

For example:

output "one_subnet" {
  value = one(data.aws_subnet_ids.public)
}

see https://www.terraform.io/docs/language/functions/one.html

于 2021-11-24T09:34:36.740 回答