在Terraform中实现两个列表笛卡尔积及合并列表创建网络ACL可行吗?
Absolutely! Terraform provides flexible functions to handle both merging lists for your network ACL configurations and generating Cartesian products of lists. Let’s walk through both scenarios with practical examples.
Merging Lists to Create Network ACLs
This is totally feasible, and it’s a common pattern when you want to combine base ACL rules with environment-specific or additional rules. The concat() function is your go-to here—it takes multiple lists and appends them into a single list.
Here’s a concrete example:
# Define your base ACL rules (e.g., default allow/deny rules) variable "base_ingress_rules" { type = list(object({ protocol = string rule_no = number action = string cidr_block = string from_port = number to_port = number })) default = [ { protocol = "tcp" rule_no = 100 action = "allow" cidr_block = "0.0.0.0/0" from_port = 80 to_port = 80 } ] } # Define additional rules you want to add variable "extra_ingress_rules" { type = list(object({ protocol = string rule_no = number action = string cidr_block = string from_port = number to_port = number })) default = [ { protocol = "tcp" rule_no = 110 action = "allow" cidr_block = "192.168.1.0/24" from_port = 22 to_port = 22 } ] } # Combine the lists and create the network ACL resource "aws_network_acl" "main" { vpc_id = aws_vpc.main.id ingress = concat(var.base_ingress_rules, var.extra_ingress_rules) egress = concat(var.base_egress_rules, var.extra_egress_rules) # Repeat for egress if needed }
Just make sure your rule numbers don’t overlap—Terraform will throw an error if duplicate rule numbers exist in the same ACL. Plan your rule number ranges ahead (e.g., base rules 100-199, extra rules 200-299) to avoid conflicts.
Generating Cartesian Products
To create a Cartesian product of two lists (all possible combinations of elements from each list), use Terraform’s setproduct() function. It accepts multiple lists/sets and returns a list of lists, where each sublist is a combination of one element from each input list.
Example 1: Simple Value Lists
Say you want to create ACL rules for every combination of subnets and allowed ports:
variable "subnet_cidrs" { type = list(string) default = ["10.0.1.0/24", "10.0.2.0/24"] } variable "allowed_ports" { type = list(number) default = [80, 443, 22] } locals { # Generate all subnet-port combinations acl_combinations = setproduct(var.subnet_cidrs, var.allowed_ports) } # Create an ACL rule for each combination resource "aws_network_acl_rule" "allowed_traffic" { count = length(local.acl_combinations) network_acl_id = aws_network_acl.main.id rule_number = 100 + count.index protocol = "tcp" rule_action = "allow" cidr_block = local.acl_combinations[count.index][0] from_port = local.acl_combinations[count.index][1] to_port = local.acl_combinations[count.index][1] egress = false }
This will create 6 rules total (2 subnets × 3 ports).
Example 2: Complex Object Lists
If you’re working with lists of objects, you can combine setproduct() with merge() to create combined objects:
variable "source_cidrs" { type = list(object({ cidr_block = string description = string })) default = [ { cidr_block = "10.0.1.0/24", description = "Internal subnet" }, { cidr_block = "0.0.0.0/0", description = "Internet" } ] } variable "dest_ports" { type = list(object({ port = number protocol = string })) default = [ { port = 80, protocol = "tcp" }, { port = 443, protocol = "tcp" } ] } locals { combined_rules = [ for combo in setproduct(var.source_cidrs, var.dest_ports) : merge( combo[0], combo[1], { action = "allow", rule_prefix = 200 } ) ] }
The local.combined_rules will be a list of 4 objects, each merging the source CIDR details, port/protocol, and the additional action/rule_prefix fields.
Note on Duplicates
setproduct() automatically removes duplicate combinations. If you need to preserve duplicates (e.g., if your input lists have duplicate elements and you want all combinations including those), use nested for loops with flatten() instead:
locals { cartesian_with_duplicates = flatten([ for source in var.source_cidrs : [ for port in var.dest_ports : { source = source port = port } ] ]) }
内容的提问来源于stack exchange,提问作者grbonk

