for_each 是从 Terraform 集中检索值的唯一方法吗?

Kev*_*rke 4 terraform hcl

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

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

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

password = var.passwords["kevin"]
Run Code Online (Sandbox Code Playgroud)

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

a_record = var.records[1]
Run Code Online (Sandbox Code Playgroud)

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

在其他地方,文档提到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}"
  }
}
Run Code Online (Sandbox Code Playgroud)

for_each元变量访问一组值的唯一途径?

yda*_*coR 11

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

举个例子:

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

output "set" {
  value = var.set
}

output "set_first_element" {
  value = var.set[0]
}
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

如果您改为使用该tolist函数进行转换,则可以按预期访问它:

output "set_to_list_first_element" {
  value = tolist(var.set)[0]
}
Run Code Online (Sandbox Code Playgroud)
Outputs:

set = [
  "bar",
  "foo",
]
set_to_list_first_element = bar
Run Code Online (Sandbox Code Playgroud)

请注意,这将返回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]
}
Run Code Online (Sandbox Code Playgroud)

这将在标记为 的子网中创建一个 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) ]
}
Run Code Online (Sandbox Code Playgroud)

然后输出以下内容:

list_comprehension_over_set = [
  "SSH",
  "HTTP",
]
set_of_objects = [
  {
    "port" = 22
    "service" = "ssh"
  },
  {
    "port" = 80
    "service" = "http"
  },
]
Run Code Online (Sandbox Code Playgroud)