如何解决 for

互联网 行业动态 更新时间:2024-06-13 00:19:17

Mar*_*ins 6

这里只有部分配置可见,我猜测了一下,但让我们看看。您提到您想在本练习中了解更多有关 Terraform 的信息,因此我将在此处详细介绍有关链的详细信息,以解释为什么我要推荐我将要推荐的东西如果你觉得这个额外的细节无趣,你可以跳到最后。

我们将从第一个模块的project_id输出值定义开始:

output "project_id" {
  value = module.project-factory.project_id
}

module.project-factory这里指的是嵌套模块调用,因此我们需要在嵌套模块中terraform-google-modules/project-factory/google//modules/core_project_factory更深入地查看一层:

output "project_id" {
  value = module.project_services.project_id
  depends_on = [
    module.project_services,
    google_project.main,
    google_pute_shared_vpc_service_project.shared_vpc_attachment,
    google_pute_shared_vpc_host_project.shared_vpc_host,
  ]
}

另一个嵌套模块调用!那个声明是project_id这样的:

output "project_id" {
  description = "The GCP project you want to enable APIs on"
  value       = element(concat([for v in google_project_service.project_services : v.project], [var.project_id]), 0)
}

呸!最后是一个实际的资源。在这种情况下,此表达式似乎正在获取资源实例的project属性,或者如果该资源在该模块的此实例中被禁用,则可能从该属性中获取。让我们看一下定义:google_project_servicevar.project_idgoogle_project_service.project_services

resource "google_project_service" "project_services" {
  for_each                   = local.services
  project                    = var.project_id
  service                    = each.value
  disable_on_destroy         = var.disable_services_on_destroy
  disable_dependent_services = var.disable_dependent_services
}

project这里设置为,所以无论哪种方式,这个最里面的输出var.project_id似乎都只是反映了输入变量的值,所以我们需要跳回一级并查看对该模块的模块调用以查看设置为:project_idproject_id

module "project_services" {
  source = "../project_services"

  project_id                  = google_project.main.project_id
  activate_apis               = local.activate_apis
  activate_api_identities     = var.activate_api_identities
  disable_services_on_destroy = var.disable_services_on_destroy
  disable_dependent_services  = var.disable_dependent_services
}

project_id设置为 的project_id属性google_project.main

resource "google_project" "main" {
  name                = var.name
  project_id          = local.temp_project_id
  _id              = local.project__id
  folder_id           = local.project_folder_id
  billing_aount     = var.billing_aount
  auto_create_network = var.auto_create_network

  labels = var.labels
}

project_id这里设置为local.temp_project_id,在同一个文件中进一步声明:

  temp_project_id = var.random_project_id ? format(
    "%s-%s",
    local.base_project_id,
    random_id.random_project_id_suffix.hex,
  ) : local.base_project_id

此表达式包含对 的引用random_id.random_project_id_suffix.hex,并且.hex是来自 的结果属性,因此由于该资源类型的实现random_id方式,直到应用时间才知道其值。random_id(它在应用步骤中生成一个随机值并将其保存在状态中,以便在以后的运行中保持一致。)


这意味着(在所有这些间接之后)您的module.project-factory.project_id模块中的值不是在配置中静态定义的值,而是可能在应用步骤期间动态决定。这意味着它不适合用作资源实例键的一部分,因此不适合用作映射中的键。for_each

不幸的是,for_each这里的使用隐藏在另一个模块terraform-google-modules/service-aounts/google中,所以我们也需要看看那个模块,看看它是如何使用project_roles输入变量的。首先,让我们看一下错误消息所指的具体资源块:

resource "google_project_iam_member" "project-roles" {
  for_each = local.project_roles_map_data

  project = element(
    split(
      "=>",
      each.value.role
    ),
    0,
  )

  role = element(
    split(
      "=>",
      each.value.role
    ),
    1,
  )

  member = "serviceAount:${google_service_aount.service_aounts[each.value.name].email}"
}

这里发生了一些有些复杂的事情,但与我们在这里看到的最相关的是这个资源配置正在创建多个基于local.project_roles_map_data. 现在让我们看一下local.project_roles_map_data

  project_roles_map_data = zipmap(
    [for pair in local.name_role_pairs : "${pair[0]}-${pair[1]}"],
    [for pair in local.name_role_pairs : {
      name = pair[0]
      role = pair[1]
    }]
  )

这里稍微复杂一点,这对我们正在寻找的东西并不重要;这里要考虑的主要事情是,这是构造一个映射,其键是从元素 0 和元素 1 构建的local.name_role_pairs,它直接在上面声明,以及local.names它所指的:

  names                 = toset(var.names)
  name_role_pairs       = setproduct(local.names, toset(var.project_roles))

因此,我们在这里了解到的是,invar.names和 in 的值var.project_roles都有助于for_each该资源上的键,这意味着这些变量值都不应该包含在应用步骤期间动态决定的任何内容。

但是,我们还(上面)了解到,project和的role参数google_project_iam_member.project-roles是从您提供的两个列表中的元素的前缀派生的,names以及project_roles您自己的模块调用中的元素的前缀。


让我们回到我们开始的地方,记住所有这些额外的信息:

module "service_aounts" {
  source  = "terraform-google-modules/service-aounts/google"
  version = "4.0.3"

  project_id    = module.project-factory.project_id
  generate_keys = "true"
  names         = ["backend-runner"]
  project_roles = [
    "${module.project-factory.project_id}=>roles/cloudsql.client",
    "${module.project-factory.project_id}=>roles/pubsub.publisher"
  ]
}

我们已经了解到,names并且project_roles两者都必须只包含配置中决定的静态值,因此不适合使用module.project-factory.project_id,因为直到在应用步骤期间生成随机项目 ID 后才会知道。

但是,我们知道该模块期望project_roles( 之前的部分=>)中每个项目的前缀是有效的项目 ID,因此没有任何其他值可以合理使用。

因此我们有点陷入困境:第二个模块有一个相当尴尬的设计决策,它试图从同一值派生本地实例键对真实远程对象的引用,这两种情况有冲突要求。但这不是您创建的模块,因此您无法轻松修改它以解决该设计怪癖。

鉴于此,我看到了两种可能的前进方法,虽然都不理想,但都有一些警告是可行的:

您可以采用提供的错误消息作为解决方法,首先要求 Terraform 计划和应用第一个模块中的资源,然后在项目 ID 已经确定并记录在状态:

terraform apply -target=module.factory
terraform apply

尽管必须分两步进行初始创建很烦人,但它至少只对初始创建此基础架构很重要。如果您稍后对其进行更新,则无需重复此两步过程,除非您以需要生成新项目 ID 的方式更改了配置。

在完成上述操作时,我们看到这种生成和返回随机项目 ID 的方法是基于您在配置中var.random_project_id设置的第一个模块的可选方法。"true"没有它,project_id输出将只是您给定name参数的副本,这似乎是通过引用根模块变量来静态定义的。

除非您特别需要项目 ID 上的随机后缀,否则您可以不random_project_id设置,因此只需将项目 ID 设置为与您的 相同的静态值var.project_name,这应该是可以用作for_each键的可接受值。

理想情况下,第二个模块将设计为将其使用的值(例如键)与用于引用真实远程对象的值分开,因此可以使用远程对象的随机后缀名称,但静态定义本地对象的名称。如果这是您控制下的模块,那么我会建议进行类似的设计更改,但我认为该第三方模块当前不寻常的设计(将多个值打包到带有分隔符的单个字符串中)是一种折衷方案希望保持与模块早期迭代的向后兼容性。

更多推荐

如何解决

本文发布于:2023-04-21 01:37:57,感谢您对本站的认可!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:如何解决

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!