- OpenTofu 语言
- 表达式
- For 表达式
for
表达式
for
表达式通过转换另一个复杂类型值来创建复杂类型值。输入值中的每个元素都可以对应于结果中的一到零个值,并且可以使用任意表达式将每个输入元素转换为输出元素。
例如,如果 var.list
是一个字符串列表,那么以下表达式将生成一个包含所有大写字母的字符串元组
[for s in var.list : upper(s)]
此 for
表达式遍历 var.list
的每个元素,然后使用 s
设置为每个相应元素来计算表达式 upper(s)
。然后,它使用以相同顺序执行该表达式的所有结果构建一个新的元组值。
输入类型
for
表达式的输入(在 in
关键字之后给出)可以是列表、集合、元组、映射或对象。
上面的示例显示了一个只有一个临时符号 s
的 for
表达式,但是 for
表达式可以选择声明一对临时符号以使用每个项目的键或索引
[for k, v in var.map : length(k) + length(v)]
对于映射或对象类型(如上所示),k
符号指的是当前元素的键或属性名称。您也可以将此双符号形式与列表和元组一起使用,在这种情况下,其他符号是从零开始的每个元素的索引,按照惯例,除非选择更具体的名称很有帮助,否则符号名称为 i
或 idx
[for i, v in var.list : "${i} is ${v}"]
索引或键符号始终是可选的。如果您仅在 for
关键字之后指定一个符号,则该符号将始终表示输入集合中每个元素的值。
结果类型
for
表达式周围括号的类型决定了它生成的结果类型。
上面的示例使用 [
和 ]
,它生成一个元组。如果您改用 {
和 }
,则结果为对象,并且您必须提供两个由 =>
符号分隔的结果表达式
{for s in var.list : s => upper(s)}
此表达式生成一个对象,其属性是来自 var.list
的原始元素,其对应值是大写版本。例如,结果值可能如下所示
{
foo = "FOO"
bar = "BAR"
baz = "BAZ"
}
单独的 for
表达式只能生成对象值或元组值,但是 OpenTofu 的自动类型转换规则意味着您通常可以在预期列表、映射和集合的位置使用结果。
过滤元素
for
表达式还可以包含一个可选的 if
子句来过滤源集合中的元素,生成一个元素少于源值的元素。
[for s in var.list : upper(s) if s != ""]
在 for
表达式中过滤集合的一个常见原因是根据某些条件将单个源集合拆分为两个单独的集合。例如,如果输入 var.users
是一个对象的映射,其中每个对象都具有一个属性 is_admin
,那么您可能希望生成包含管理员与非管理员对象的单独映射
variable "users" {
type = map(object({
is_admin = bool
}))
}
locals {
admin_users = {
for name, user in var.users : name => user
if user.is_admin
}
regular_users = {
for name, user in var.users : name => user
if !user.is_admin
}
}
元素排序
因为 for
表达式可以从无序类型(映射、对象、集合)转换为有序类型(列表、元组),所以 OpenTofu 必须为无序集合的元素选择隐式排序。
对于映射和对象,OpenTofu 按键或属性名称排序元素,使用词法排序。
对于字符串集合,OpenTofu 按其值排序元素,使用词法排序。
对于其他类型的集合,OpenTofu 使用任意排序,该排序可能在将来的版本中发生变化。我们建议将表达式结果转换为集合,以使其在配置的其他地方清楚地表明结果是无序的。您可以使用 toset
函数 将 for
表达式结果简洁地转换为集合类型。
toset([for e in var.set : e.example])
分组结果
如果结果类型为对象(使用 {
和 }
定界符),则通常给定的键表达式在结果中的所有元素中必须是唯一的,否则 OpenTofu 将返回错误。
有时结果键不是唯一的,因此为了支持这种情况,OpenTofu 支持一种特殊的分组模式,该模式将结果更改为支持每个键的多个元素。
要激活分组模式,在值表达式后添加符号...
。例如
variable "users" {
type = map(object({
role = string
}))
}
locals {
users_by_role = {
for name, user in var.users : user.role => name...
}
}
以上表示模块期望一个描述各种用户的映射的情况,每个用户都有一个唯一的“角色”,其中映射键是用户名。用户名保证唯一,因为它们是输入中的映射键,但许多用户可能共享同一个角色名称。
local.users_by_role
表达式反转输入映射,使键成为角色名称,值成为用户名,但表达式处于分组模式(由于name
后面的...
),因此结果将是一个字符串列表的映射,例如以下所示
{
"admin": [
"ps",
],
"maintainer": [
"am",
"jb",
"kl",
"ma",
],
"viewer": [
"st",
"zq",
],
}
由于元素排序规则,OpenTofu 会在计算 for
表达式时按字典顺序对用户名进行排序,因此每个角色关联的用户名在分组后将按字典顺序排序。
重复配置块
for
表达式机制用于从表达式中的其他集合值构建集合值,然后可以将其分配给期望复杂值的单个资源参数。
某些资源类型还定义了嵌套块类型,这些类型通常表示以某种方式属于包含资源的单独对象。您无法使用for
表达式动态生成嵌套块,但您可以使用dynamic
块动态生成资源的嵌套块。