如何为google-terraform-network创建的VPC动态设置默认防火墙规则
问题描述
我需要使用terraform-google-modules/network/google模块创建VPC网络,同时为firewall_rules参数配置用户自定义规则或默认防火墙规则。核心难点在于:默认规则的源范围需要限制为动态解析的本地IP,因此无法直接在variables.tf中设置默认值——以下写法会直接报错:
variable "rules" { description = "List of rule definitions" default = [ { name = "allow-ssh-ingress" direction = "INGRESS" ranges = [format("%s/%s", data.external.my_ip_addr.result["internet_ip"], 32)] // ... } ] }
报错信息:
Error: Function calls not allowed
已尝试的无效解决方案
方法1:变量默认值设为null,使用try函数
变量定义:
variable "rules" { description = "List of rule definitions" default = null }
模块配置:
firewall_rules = try(var.rules, [{ name = "allow-ssh-ingress" direction = "INGRESS" ranges = [format("%s/%s", data.external.my_ip_addr.result["internet_ip"], 32)] allow = [{ protocol = "tcp" ports = ["22"] }] deny = [] }])
报错信息:
A null value cannot be used as the collection in a 'for' expression.
方法2:变量默认值设为null,使用三元运算符
模块配置:
firewall_rules = var.rules != null ? var.rules : [{ name = "allow-ssh-ingress" direction = "INGRESS" ranges = [format("%s/%s", data.external.my_ip_addr.result["internet_ip"], 32)] allow = [{ protocol = "tcp" ports = ["22"] }] deny = [] }]
报错信息:
The true and false result expressions must have consistent types. The 'true' value is list of object, but the 'false' value is tuple.
方法3:显式转换为列表
模块配置:
firewall_rules = var.rules != null ? var.rules : tolist([{ name = "allow-ssh-ingress" direction = "INGRESS" ranges = [format("%s/%s", data.external.my_ip_addr.result["internet_ip"], 32)] allow = [{ protocol = "tcp" ports = ["22"] }] deny = [] }])
报错信息:
The true and false result expressions must have consistent types. Mismatched list element types: Type mismatch for object attribute "allow": The 'true' value is list of object, but the 'false' value is tuple.
核心疑问
当用户未传入自定义规则时,如何为firewall_rules提供包含动态元素(如动态解析的本地IP)的默认值?
补充信息
rules变量完整定义:
variable "rules" { description = "List of rule definitions" default = null type = list(object({ name = string description = optional(string) direction = optional(string) priority = optional(number) ranges = optional(list(string)) source_tags = optional(list(string)) source_service_accounts = optional(list(string)) target_tags = optional(list(string)) target_service_accounts = optional(list(string)) allow = optional(list(object({ protocol = string ports = optional(list(string)) }))) deny = optional(list(object({ protocol = string ports = optional(list(string)) }))) log_config = optional(object({ metadata = string })) })) }
main.tf相关代码:
module "vpc" { source = "terraform-google-modules/network/google" version = "~> 7.0" project_id = var.project network_name = var.network_name routing_mode = "GLOBAL" subnets = [ { subnet_name = var.subnet_name subnet_ip = var.subnet_ip subnet_region = var.region }, ] firewall_rules = var.rules != null ? var.rules : tolist([{ name = "allow-ssh-ingress" direction = "INGRESS" ranges = [format("%s/%s", data.external.my_ip_addr.result["internet_ip"], 32)] allow = tolist([{ protocol = "tcp" ports = ["22"] }]) deny = [] }]) }
解决方案
问题根源是Terraform对类型匹配要求严格:默认规则的嵌套结构(比如allow字段)被解析为tuple,但变量定义要求的是list(object(...)),导致类型不兼容。解决思路是通过locals提前构建符合类型要求的默认规则,再结合三元运算符赋值。
步骤1:定义本地默认规则
在locals.tf中创建完全匹配变量类型的默认规则:
locals { default_firewall_rules = [ { name = "allow-ssh-ingress" direction = "INGRESS" ranges = [format("%s/32", data.external.my_ip_addr.result["internet_ip"])] allow = [ { protocol = "tcp" ports = ["22"] } ] deny = [] # 显式声明所有可选字段为null,匹配变量的optional属性 description = null priority = null source_tags = null source_service_accounts = null target_tags = null target_service_accounts = null log_config = null } ] }
步骤2:在模块中引用本地变量
修改main.tf中的firewall_rules赋值:
module "vpc" { source = "terraform-google-modules/network/google" version = "~> 7.0" project_id = var.project network_name = var.network_name routing_mode = "GLOBAL" subnets = [ { subnet_name = var.subnet_name subnet_ip = var.subnet_ip subnet_region = var.region }, ] firewall_rules = var.rules != null ? var.rules : local.default_firewall_rules }
关键说明
- 类型一致性:通过显式声明所有可选字段为
null,确保默认规则的结构完全匹配variable "rules"定义的list(object(...))类型,避免tuple与list的类型冲突。 - 动态值处理:
locals块支持使用函数和数据源,完美解决动态IP无法直接写在变量默认值的问题。 - 兼容性:既保留用户自定义规则的灵活性,又确保默认规则符合模块的类型要求。
内容的提问来源于stack exchange,提问作者SkogensKonung
相关产品推荐
相关产品推荐

