如何在Terraform中合并多文件的AWS Network Firewall stateful_rule
解决AWS Network Firewall规则组拆分规则文件的问题
直接在多个TF文件里写同名的stateful_rule块会被Terraform覆盖,这是因为Terraform对同类型块的处理逻辑是最后定义的覆盖之前的。要实现每个规则单独存文件再合并到同一个规则组,得换个思路——用动态块(dynamic block)结合变量列表的方式,或者用模块拆分规则,下面是具体实现:
方法一:本地变量+动态块快速实现
1. 编写单个规则文件
在项目里新建一个rules目录,每个规则单独存一个.tf文件,用locals定义规则内容:
比如rules/allow_http.tf:
locals { allow_http = { action = "PASS" header = { destination = "10.0.0.0/24" destination_port = "80" direction = "FORWARD" protocol = "TCP" source = "0.0.0.0/0" source_port = "*" } rule_options = [ { keyword = "sid" settings = ["1001"] } ] } }
再比如rules/drop_https.tf:
locals { drop_https = { action = "DROP" header = { destination = "192.168.1.0/24" destination_port = "443" direction = "FORWARD" protocol = "TCP" source = "0.0.0.0/0" source_port = "*" } rule_options = [ { keyword = "sid" settings = ["1002"] } ] } }
2. 收集规则并生成规则组
在根目录的main.tf里,把所有规则收集成列表,然后用dynamic "stateful_rule"块遍历列表生成所有规则:
# 把所有单个规则收集成一个列表 locals { all_stateful_rules = [local.allow_http, local.drop_https] } resource "aws_networkfirewall_rule_group" "my_rule_group" { name = "my-stateful-rule-group" capacity = 100 # 根据规则数量调整容量 type = "STATEFUL" rule_group { rules_source { stateful_rules { # 动态生成每个stateful_rule块 dynamic "stateful_rule" { for_each = local.all_stateful_rules content { action = stateful_rule.value.action header { destination = stateful_rule.value.header.destination destination_port = stateful_rule.value.header.destination_port direction = stateful_rule.value.header.direction protocol = stateful_rule.value.header.protocol source = stateful_rule.value.header.source source_port = stateful_rule.value.header.source_port } # 动态生成rule_option块(如果规则有多个选项) dynamic "rule_option" { for_each = stateful_rule.value.rule_options content { keyword = rule_option.value.keyword settings = rule_option.value.settings } } } } } } } tags = { Name = "MyStatefulRuleGroup" } }
3. 新增规则的方式
要加新规则,只需要在rules目录下新建一个.tf文件,定义一个新的local规则,然后把它加到local.all_stateful_rules列表里就行,比如新增rules/allow_dns.tf后,修改列表:
locals { all_stateful_rules = [local.allow_http, local.drop_https, local.allow_dns] }
方法二:Terraform模块拆分规则(适合大量规则)
如果规则数量很多,用模块的方式更易于维护,每个规则做成一个可复用的小模块:
1. 创建规则模块
新建modules/stateful-rule目录,包含以下文件:
variables.tf:定义规则的输入参数
variable "action" { type = string description = "Action for the rule (PASS, DROP, ALERT)" } variable "header" { type = object({ destination = string destination_port = string direction = string protocol = string source = string source_port = string }) description = "Header details for the stateful rule" } variable "rule_options" { type = list(object({ keyword = string settings = optional(list(string)) })) description = "List of rule options (like sid, msg)" }
outputs.tf:输出规则的完整结构
output "rule_definition" { type = object({ action = string header = object({ destination = string destination_port = string direction = string protocol = string source = string source_port = string }) rule_options = list(object({ keyword = string settings = optional(list(string)) })) }) value = { action = var.action header = var.header rule_options = var.rule_options } }
2. 在根模块调用规则模块
在根目录的main.tf里,每个规则单独调用模块:
module "allow_http" { source = "./modules/stateful-rule" action = "PASS" header = { destination = "10.0.0.0/24" destination_port = "80" direction = "FORWARD" protocol = "TCP" source = "0.0.0.0/0" source_port = "*" } rule_options = [ { keyword = "sid" settings = ["1001"] } ] } module "drop_https" { source = "./modules/stateful-rule" action = "DROP" header = { destination = "192.168.1.0/24" destination_port = "443" direction = "FORWARD" protocol = "TCP" source = "0.0.0.0/0" source_port = "*" } rule_options = [ { keyword = "sid" settings = ["1002"] } ] } # 收集所有模块输出的规则 locals { all_stateful_rules = [module.allow_http.rule_definition, module.drop_https.rule_definition] } # 生成规则组,和方法一的动态块部分完全一样 resource "aws_networkfirewall_rule_group" "my_rule_group" { name = "my-stateful-rule-group" capacity = 100 type = "STATEFUL" rule_group { rules_source { stateful_rules { dynamic "stateful_rule" { for_each = local.all_stateful_rules content { action = stateful_rule.value.action header { destination = stateful_rule.value.header.destination destination_port = stateful_rule.value.header.destination_port direction = stateful_rule.value.header.direction protocol = stateful_rule.value.header.protocol source = stateful_rule.value.header.source source_port = stateful_rule.value.header.source_port } dynamic "rule_option" { for_each = stateful_rule.value.rule_options content { keyword = rule_option.value.keyword settings = rule_option.value.settings } } } } } } } tags = { Name = "MyStatefulRuleGroup" } }
这种方式下,新增规则只需要添加一个module块,不需要修改规则列表(如果用Terraform 0.14+,也可以用flatten([for m in modules : m.rule_definition])自动收集,但手动添加更清晰可控)。
内容的提问来源于stack exchange,提问作者Ariel Davidov
相关产品推荐
相关产品推荐

