Terraform: объединяйте карты списков и объединяйте списки из повторяющихся ключей - PullRequest
1 голос
/ 27 мая 2020

У меня что-то вроде этого

locals {
  roles = [
    {
      role = "roles/admin"
      members = ["user:user@example.com"]
    },
    {
      role = "roles/viewer"
      members = ["user:user2@example.com"]
    }
  ]
}

Я хочу, чтобы мои модули могли экспортировать свой собственный список ролей, например

roles = [
    {
      role = "roles/viewer"
      members = ["user:newperson@example.com"]
    }
]

, а затем иметь возможность объединять эти вещи вместе чтобы получить

locals {
  roles = [
    {
      role = "roles/admin"
      members = ["user:user@example.com"]
    },
    {
      role = "roles/viewer"
      members = ["user:user2@example.com", "user:newperson@example.com"]
    }
  ]
}

Я знаю, что если бы я сохранил все как карты, я мог бы объединить карты, и это будет работать нормально, пока нет повторяющихся ключей. Но в этом случае я явно хочу иметь возможность иметь повторяющиеся ключи, чтобы одному модулю не нужно было заботиться о каких-либо других модулях.

Обновление Мне удалось добиться этого сделав что-то вроде этого:

roles = distinct(flatten([
    for rm in local.role_maps : [
      for role, members in rm :
      {
        role = role
        members = sort(distinct(flatten([
          for m in local.role_maps :
          m[role] if lookup(m, role, null) != null
        ])))
      }
    ]
  ]))

1 Ответ

1 голос
/ 28 мая 2020

Кажется, это хорошее приложение для for выражения с использованием модификатора группировки ..., который может создавать карту списков путем группировки по ключам.

variable "custom_roles" {
  type = list(object({
    role    = string
    members = list(string)
  })
}

locals {
  default_roles = [
    {
      role    = "roles/admin"
      members = ["user:user@example.com"]
    },
    {
      role    = "roles/viewer"
      members = ["user:user2@example.com"]
    }
  ]
  all_roles = concat(
    local.default_roles,
    var.custom_roles,
  )

  # First we'll project the inputs so that we have one
  # role/member pair per element.
  flat_roles = flatten([
    for r in locals.all_roles : [
      for m in r.members : {
        role   = r.role
        member = m
      }
    ]
  ])

  # Then we can use that flattened list to produce a map
  # grouped by unique role key.
  merged_roles = {
    for k, v in local.all_roles : k => v...
  }

  # Finally, if the list-of-objects representation was
  # important then we can recover it by projecting that
  # merged_roles map back into the list shape.
  merged_roles_list = tolist([
    for role, members in local.merged_roles : {
      role    = role
      members = tolist(members)
    }
  ])
}

В приведенном выше примере local.merged_roles представляет собой карту списков, например:

{
  "roles/admin"  = ["user:user@example.com"]
  "roles/viewer" = ["user:user2@example.com", "user:newperson@example.com"]
}

Вы можете использовать эту карту напрямую, но на всякий случай, если форма списка объектов была важна, я включил local.merged_roles_list, которая должна соответствовать структуре, которую вы задали для своего вопроса.

...