Terraform 因 for_each 参数无效/给定的“for_each”参数值不合适而失败

Posted

技术标签:

【中文标题】Terraform 因 for_each 参数无效/给定的“for_each”参数值不合适而失败【英文标题】:Terraform failing with Invalid for_each argument / The given "for_each" argument value is unsuitable 【发布时间】:2020-09-27 12:47:54 【问题描述】:

当运行terraform planterraform apply 并提供给for_each 的列表时,会出现错误提示

Error: Invalid for_each argument

  on main.tf line 2, in resource "aws_ssm_parameter" "foo":
  2:   for_each = ["a", "b"]

The given "for_each" argument value is unsuitable: the "for_each" argument
must be a map, or set of strings, and you have provided a value of type tuple.

重现此错误的最小示例如下:

resource "aws_ssm_parameter" "foo" 
  for_each = ["a", "b"]

  name  = "foo-$each.value"
  type  = "String"
  value = "bar-$each.value"

【问题讨论】:

【参考方案1】:

说明

此错误通常是由将列表传递给 for_each 引起的,但 for_each 仅适用于无序数据类型,即适用于集合和映射。

解决方案

解决办法视情况而定。

字符串列表

如果列表只是一个字符串列表,最简单的解决方法是添加一个toset()-call 将列表转换为可以由for_each处理的集合,像这样

resource "aws_ssm_parameter" "foo" 
  for_each = toset(["a", "b"])

  name  = "foo-$each.value"
  type  = "String"
  value = "bar-$each.value"

可以重新排列为地图的列表

如果输入是一个列表,但很容易重新排列为地图,这通常是最好的方法。 假设我们有一个这样的列表

locals 
  animals = [
    
      name = "Bello"
      age = 3
      type = "dog"
    ,
    
      name = "Minga"
      age = 4
      type = "cat"
    ,
  ]

那么适当的重组可能是这样的

locals 
  animals = 
    Bello : 
      age = 3
      type = "dog"
    ,
    Minga : 
      age = 4
      type = "cat"
    
  

然后允许您定义

resource "aws_ssm_parameter" "foo" 
  for_each = local.animals

  name  = each.key
  type  = string
  value = "This is a $each.value.type, $each.value.age years old."

不想重新排列的列表

有时有一个列表是很自然的,例如来自不受控制的模块的输出或来自使用count 定义的资源。在这种情况下,可以像这样使用 count

resource "aws_ssm_parameter" "foo" 
  count = length(local.my_list)

  name  = my_list[count.index].name
  type  = "String"
  value = my_list[count.index].value

适用于包含名称和值作为键的映射列表。但是,通常情况下,将列表转换为地图更合适,而不是像这样

resource "aws_ssm_parameter" "foo" 
  for_each =  for x in local.my_list: x.id => x 

  name  = each.value.name
  type  = "String"
  value = each.value.value

这里应该选择任何合适的东西来代替x.id。如果my_list 是一个对象列表,那么通常有一些通用字段(例如名称或键)可以使用。这种方法有利于使用count 的优点是,在从列表中插入或删除元素时表现更好。 count 不会注意到插入或删除,因此会更新插入发生位置之后的所有资源,而 for_each 实际上只添加或删除具有新或已删除 id 的资源。

【讨论】:

我会更正错误不是“由将列表传递给 for_each 引起的”,而是因为 for_each 仅适用于无序数据类型。列表是有序类型,集合或映射不是。 这很奇怪,您可以在动态块内列出地图列表,但如果您在外部使用 for_each 则会出现错误:( 我同意;这很奇怪。如果该语言能够迭代无序集合,那么世界上有什么可能阻止对有序集合的迭代呢? Terraform 不符合最小惊讶法则。

以上是关于Terraform 因 for_each 参数无效/给定的“for_each”参数值不合适而失败的主要内容,如果未能解决你的问题,请参考以下文章

具有下游依赖项的 Terraform 条件“for_each”

Terraform : for_each 一个一个

将 aws 资源导入具有 for_each 的 terraform 模块

使用 terraform 在 for_each 嵌套资源中循环

Terraform for_each - each.key 在哪里定义?

如何在 terraform 中正确使用 for_each 中的 each.value?