如何用for_each创建多台GCP实例及对应Cloudflare隧道?
解决Terraform批量创建GCP实例与Cloudflare隧道的重复名称及DRY代码结构问题
错误原因分析
你的报错核心是Cloudflare同账号下不允许存在同名隧道,而var.servers中两台服务器的name均为terraform1,导致两个cloudflare_tunnel模块实例尝试创建同名隧道,触发Cloudflare的唯一性校验错误。
解决方案一:快速修复重复名称问题
不需要调整模块结构,只需修改Cloudflare隧道模块的名称参数,利用each.key(即服务器的唯一标识server1/server2)拼接出唯一名称:
module "cloudflare_tunnel" { source = "./modules/cloudflare/tunnel" for_each = var.servers # 用服务器唯一key拼接名称,确保隧道名称全局唯一 name = "${each.value.name}-${each.key}" cloudflare_account_id = var.cloudflare_account_id cloudflare_zone_id = var.cloudflare_zone_id cloudflare_zone = var.cloudflare_zone }
修改后,两台服务器对应的隧道名称会变成terraform1-server1和terraform1-server2,避免重复。
解决方案二:通过复合模块实现DRY代码结构
为了彻底遵循DRY原则,将GCP实例和Cloudflare隧道的创建逻辑封装到一个复合模块中,根模块只需调用一次即可完成成对资源的批量创建:
1. 创建复合模块modules/server-with-tunnel
modules/server-with-tunnel/main.tf
# 嵌套调用Cloudflare隧道模块 module "cloudflare_tunnel" { source = "../cloudflare/tunnel" name = "${var.server_name}-${var.server_key}" cloudflare_account_id = var.cloudflare_account_id cloudflare_zone_id = var.cloudflare_zone_id cloudflare_zone = var.cloudflare_zone } # 嵌套调用GCP实例模块 module "gcp_server" { source = "../gcp" name = var.server_name startup_script = var.startup_script cloudflare_tunnel_token = module.cloudflare_tunnel.tunnel_token machine_type = var.machine_type image = var.image } # 输出资源标识(按需添加) output "gcp_instance_id" { value = module.gcp_server.instance_id } output "cloudflare_tunnel_id" { value = module.cloudflare_tunnel.tunnel_id }
modules/server-with-tunnel/variables.tf
variable "server_key" { type = string description = "服务器的唯一标识(来自servers映射的key)" } variable "server_name" { type = string description = "GCP实例名称" } variable "startup_script" { type = string } # Cloudflare相关变量 variable "cloudflare_account_id" { type = string } variable "cloudflare_zone_id" { type = string } variable "cloudflare_zone" { type = string } # GCP相关变量 variable "machine_type" { type = string } variable "image" { type = string }
2. 根模块简化调用
修改根模块main.tf,只需调用复合模块一次即可批量创建所有服务器与隧道:
variable "servers" { default = { "server1" = { name = "terraform1" }, "server2" = { name = "terraform1" } } } variable "cloudflare_account_id" { type = string } variable "cloudflare_zone_id" { type = string } variable "cloudflare_zone" { type = string } variable "gcp_machine_type" { type = string } variable "gcp_instance_image" { type = string } module "server_with_tunnel" { source = "./modules/server-with-tunnel" for_each = var.servers server_key = each.key server_name = each.value.name startup_script = "server-setup.sh" cloudflare_account_id = var.cloudflare_account_id cloudflare_zone_id = var.cloudflare_zone_id cloudflare_zone = var.cloudflare_zone machine_type = var.gcp_machine_type image = var.gcp_instance_image }
这种结构既保证了资源的唯一性,又消除了根模块中的重复代码,完全符合DRY设计原则。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

