首页 > 解决方案 > 使用 terraform yamldecode 访问多级元素

问题描述

我有一个 yaml 文件(也用于 azure devops 管道,因此需要采用这种格式),其中包含一些我想直接从我的 terraform 模块访问的设置。

该文件类似于:

variables:
  - name: tenantsList
    value: tenanta,tenantb
  - name: unitName
    value: canary

我想要一个这样的模块来访问设置,但我看不到如何进入底层:

locals {
  settings = yamldecode(file("../settings.yml"))
}

module "infra" {
  source = "../../../infra/terraform/"
  unitname = local.settings.variables.unitName
}

但是这样的terraform plan错误:

Error: Unsupported attribute

  on canary.tf line 16, in module "infra":
  16:   unitname  = local.settings.variables.unitName
    |----------------
    | local.settings.variables is tuple with 2 elements

This value does not have any attributes.

标签: yamlterraform

解决方案


看起来这很困难的主要原因是因为这个 YAML 文件在逻辑上表示单个映射,但在物理上表示为映射的 YAML 列表。

当从这样的单独文件中读取数据时,我喜欢编写一个显式表达式来对其进行规范化,并可以选择对其进行转换,以便在 Terraform 模块的其余部分中更方便地使用。在这种情况下,似乎variables将地图作为 Terraform 值是最有用的表示形式,因此我们可以编写如下转换表达式:

locals {
  raw_settings = yamldecode(file("${path.module}/../settings.yml"))
  settings = {
    variables = tomap({
      for v in local.raw_settings.variables : v.name => v.value
    })
  }
}

上面使用for表达式name将映射列表投影到使用值作为键的单个映射中。

将地图列表转换为单个地图后,您可以按照最初尝试的方式访问它:

module "infra" {
  source = "../../../infra/terraform/"
  unitname = local.settings.variables.unitName
}

如果您要将转换后的值输出local.settings为 YAML,它看起来像这样,这就是为什么现在可以直接访问地图元素的原因:

variables:
  tenantsList: tenanta,tenantb
  unitName: canary

这仅name在您输入中的所有字符串都是唯一的情况下才有效,因为否则每个元素都不会有唯一的映射键。


(编写这样的规范化表达式也可以作为对该 YAML 文件形状的一些隐式验证:如果variables不是列表或值不是全部相同类型,则 Terraform 会引发类型错误评估该表达式。即使不需要转换,我还是喜欢写出这种表达式,因为它可以作为 YAML 文件预期具有的形状的一些文档,而不是在整个配置的其余部分中研究对它的所有引用。 )


推荐阅读