此对象没有名为 yaml 结构的属性

This object does not have an attribute named yaml structures

我想在一个 yaml 文件中构建我的组织结构,供 github 提供者管理团队、成员,一些团队包含子元素,如 subteams,有些不包含,因为循环错误“This object does not有一个名为“的属性,当 yaml 中的结构不相同时,我尝试使用 lookup()、contains()、if() 但我才刚刚开始使用 terraform,所以也许你可以帮助我如何实现这一点?

# cat locals.tf

locals {

  teams = yamldecode(file("teams.yaml"))["teams"]
  teams_flatten = flatten([
    for team in local.teams :
    {
      team_name   = team.name
      description = lookup(team, "description", "")
      privacy     = lookup(team, "privacy", "closed")
    }
  ])

  subteams_flatten = flatten([
    for team in local.teams : [
      for subteam in team.subteams : {
        team_name    = team.name
        subteam_name = subteam.name
        description  = lookup(subteam, "description", "")
        privacy      = lookup(subteam, "privacy", "closed")
      }
    ]
  ])
}

│   on locals.tf line 16, in locals:
│   16:       for subteam in team.subteams : {
│
│ This object does not have an attribute named "subteams".

# cat teams.yaml
---
teams:
  # Parent team
  - name: Engineering
    # This team has sub-teams
    subteams:
      - name: access
        members:
          - member: alex
            role: maintainer
          - member: bob
            role: maintainer
      - name: payments
        members:
          - member: alice
            role: maintainer

  # Parent team
  - name: Marketing
    # This team does not have any sub-teams
    members:
      - member: bob
        role: maintainer
      - member: alex
        role: maintainer


# cat teams.tf

resource "github_team" "teams" {

  for_each = {
    for team in local.teams_flatten : team.team_name => team
  }

  name        = each.value.team_name
  description = each.value.description
  privacy     = each.value.privacy
}

resource "github_team" "subteams" {
  depends_on = [github_team.teams]
  for_each = {
    for subteam in local.subteams_flatten : subteam.subteam_name => subteam
  }

  name           = each.value.subteam_name
  parent_team_id = lookup(github_team.teams, each.value.team_name)["id"]
  description    = each.value.description
  privacy        = each.value.privacy
}

当处理像这样结构不一定一致的原始人工编辑文件时,有时可以帮助首先明确规范化数据结构作为一个单独的步骤,然后在其他地方使用更一致的数据结构。这样就可以将密集的条件代码隔离在一个地方,从而有望使模块的其余部分更易于阅读。

例如:

locals {
  teams_raw = yamldecode(file("${path.module}/teams.yaml"))["teams"]

  teams = {
    for team_raw in local.teams_raw : tostring(team_raw.name) => {
      name = tostring(team_raw.name)
      subteams = tomap({
        for subteam_raw in try(team_raw.subteams, []) :
        tostring(subteam_raw.name) => {
          name = tostring(subteam_raw.name)
          members = tomap({
            for member_raw in subteam_raw.members :
            tostring(member_raw.member) => {
              name = tostring(member_raw.member)
              role = tostring(member_raw.role)
            }
          })
        }
      })
      members = tomap({
        for member_raw in try(team_raw.members, []) :
        tostring(member_raw.member) => {
          name = tostring(member_raw.member)
          role = tostring(member_raw.role)
        }
      })
    }
  }
}

上述 local.teams 定义的两个主要相关特征是:

  • 它在几个地方使用 try 来为特定键不可用时提供备用值。这意味着生成的数据结构将始终具有该类型的属性,但它可能引用一个空映射,这比可能根本不存在的属性更容易处理。
  • 它自由地使用 tomaptostring 来断言每个属性的预期类型。因此,如果给定值不适合这些约束之一,这将允许 Terraform 尽早引发错误,并确保 Terraform 可以为您可以在其他地方依赖的数据结构推断出合适的静态类型。

有了这个新的规范化数据结构,您可以在所有元素上引用 memberssubteams,并且知道它总是被设置但可能被设置为空映射。