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