【问题标题】:Terraform - Iterate over a List of Objects in a TemplateTerraform - 迭代模板中的对象列表
【发布时间】:2020-11-06 09:53:31
【问题描述】:

我在迭代由templatefile 函数解释的模板中的对象列表时遇到问题。

我有以下变量:

variable "destinations" {
  description = "A list of EML Channel Destinations."

  type = list(object({
    id  = string
    url = string
  }))
}

这将作为destinations 传递给templatefile 函数。模板相关的sn-p是这样的:

Destinations:
  %{ for dest in destinations ~}
  - Id: ${dest.id}
    Settings:
      URL: ${dest.url}
  %{ endfor }

在规划 Terraform 时会出现以下错误:

Error: "template_body" contains an invalid YAML: yaml: line 26: did not find expected key

我已尝试将模板代码切换为以下内容:

Destinations:
  %{ for id, url in destinations ~}
  - Id: ${id}
    Settings:
      URL: ${url}
  %{ endfor }

这给出了不同的错误:

Call to function "templatefile" failed:
../../local-tfmodules/eml/templates/eml.yaml.tmpl:25,20-23: Invalid template
interpolation value; Cannot include the given value in a string template:
string required., and 2 other diagnostic(s).

[!] something went wrong when creating the environment TF plan

我觉得我在这里对数据类型的迭代是不正确的,但我无法理解怎么做,而且我根本找不到任何关于此的文档。

这是我如何调用此模块的简化示例:

module "eml" {
  source = "../../local-tfmodules/eml"

  name = "my_eml"

  destinations = [
    {
      id  = "6"
      url = "https://example.com"
    },
    {
      id  = "7"
      url = "https://example.net"
    }
  ]
<cut>
}

【问题讨论】:

    标签: function terraform terraform0.12+


    【解决方案1】:

    我刚刚发现(在制作了一个小型 Terraform 模块以仅测试 templatefile 输出之后)原始配置确实有效(至少在 TF v0.12.29 中)。

    给出的错误有点像红鲱鱼 - 问题与模板中的缩进有关,例如而不是:

    Destinations:
      %{ for destination in destinations  ~}
      - Id: ${destination.id}
        Settings:
          URL: ${destination.url}
      %{ endfor ~}
    

    应该是:

    Destinations:
      %{~ for destination in destinations  ~}
      - Id: ${destination.id}
        Settings:
          URL: ${destination.url}
      %{~ endfor ~}
    

    请注意 Terraform 指令开头的额外波浪号 (~)。这使得 Yaml 对齐工作正常(你得到一些错误的缩进行和一些空白行)。在此之后,我的问题中的原始代码按我的预期工作并生成有效的 yaml。

    【讨论】:

    • 谢谢!我花了几个小时试图理解为什么我的输出结果格式错误,而你的回答救了我!
    【解决方案2】:

    您不能将var.destinations 作为地图列表传递给模板。它必须是字符串列表/集合。

    但您可以执行以下操作:

    templatefile("eml.yaml.tmpl", 
              { 
                ids =  [for v in var.destinations: v.id]
                urls =  [for v in var.destinations: v.url] 
              }
        )
    

    eml.yaml.tmpl 在哪里

    Destinations:
      %{ for id, url in zipmap(ids, urls)  ~}
      - Id: ${id}
        Settings:
          URL: ${url}
      %{ endfor ~}
    

    【讨论】:

      【解决方案3】:

      由于您的目标是生成 YAML 结果,我建议您遵循the templatefile documentation 中关于generating JSON or YAML from a template 的建议。

      使用the yamlencode function 将保证结果始终是有效的 YAML,您不必担心正确定位换行符或引用/转义可能包含特殊字符的字符串。

      像这样写你的templatefile电话:

      templatefile("${path.module}/templates/eml.yaml.tmpl", {
        destinations = var.destinations
      })
      

      然后,在eml.yaml.tmpl中,使整个模板成为调用yamlencode的结果,像这样:

      ${yamlencode({
        Destinations = [
          for dest in destinations : {
            Id = dest.id
            Settings = {
              URL = dest.url
            }
          }
        ]
      })
      

      注意yamlencode 的参数是Terraform expression syntax 而不是 YAML 语法,因为在这种情况下,Terraform 负责进行 YAML 编码,您需要做的就是为 Terraform 提供一个合适的值来编码,如下yamldecode 文档中给出的从 Terraform 类型到 YAML 类型的映射。

      【讨论】:

      • 虽然这可能是技术上“正确”的方式,但它太复杂而无用。即使是像使用变量的值作为标签名称这样的简单要求,对于 yamlencode 函数也会成为挑战。
      猜你喜欢
      • 1970-01-01
      • 2021-08-08
      • 1970-01-01
      • 2017-07-25
      • 1970-01-01
      • 1970-01-01
      • 2014-10-18
      • 2021-10-28
      • 2013-12-10
      相关资源
      最近更新 更多