github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/website/docs/language/functions/setproduct.mdx (about) 1 --- 2 page_title: setproduct - Functions - Configuration Language 3 description: |- 4 The setproduct function finds all of the possible combinations of elements 5 from all of the given sets by computing the cartesian product. 6 --- 7 8 # `setproduct` Function 9 10 The `setproduct` function finds all of the possible combinations of elements 11 from all of the given sets by computing the 12 [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product). 13 14 ```hcl 15 setproduct(sets...) 16 ``` 17 18 This function is particularly useful for finding the exhaustive set of all 19 combinations of members of multiple sets, such as per-application-per-environment 20 resources. 21 22 ``` 23 > setproduct(["development", "staging", "production"], ["app1", "app2"]) 24 [ 25 [ 26 "development", 27 "app1", 28 ], 29 [ 30 "development", 31 "app2", 32 ], 33 [ 34 "staging", 35 "app1", 36 ], 37 [ 38 "staging", 39 "app2", 40 ], 41 [ 42 "production", 43 "app1", 44 ], 45 [ 46 "production", 47 "app2", 48 ], 49 ] 50 ``` 51 52 You must pass at least two arguments to this function. 53 54 Although defined primarily for sets, this function can also work with lists. 55 If all of the given arguments are lists then the result is a list, preserving 56 the ordering of the given lists. Otherwise the result is a set. In either case, 57 the result's element type is a list of values corresponding to each given 58 argument in turn. 59 60 ## Examples 61 62 There is an example of the common usage of this function above. There are some 63 other situations that are less common when hand-writing but may arise in 64 reusable module situations. 65 66 If any of the arguments is empty then the result is always empty itself, 67 similar to how multiplying any number by zero gives zero: 68 69 ``` 70 > setproduct(["development", "staging", "production"], []) 71 [] 72 ``` 73 74 Similarly, if all of the arguments have only one element then the result has 75 only one element, which is the first element of each argument: 76 77 ``` 78 > setproduct(["a"], ["b"]) 79 [ 80 [ 81 "a", 82 "b", 83 ], 84 ] 85 ``` 86 87 Each argument must have a consistent type for all of its elements. If not, 88 Terraform will attempt to convert to the most general type, or produce an 89 error if such a conversion is impossible. For example, mixing both strings and 90 numbers results in the numbers being converted to strings so that the result 91 elements all have a consistent type: 92 93 ``` 94 > setproduct(["staging", "production"], ["a", 2]) 95 [ 96 [ 97 "staging", 98 "a", 99 ], 100 [ 101 "staging", 102 "2", 103 ], 104 [ 105 "production", 106 "a", 107 ], 108 [ 109 "production", 110 "2", 111 ], 112 ] 113 ``` 114 115 ## Finding combinations for `for_each` 116 117 The 118 [resource `for_each`](/language/meta-arguments/for_each) 119 and 120 [`dynamic` block](/language/expressions/dynamic-blocks) 121 language features both require a collection value that has one element for 122 each repetition. 123 124 Sometimes your input data comes in separate values that cannot be directly 125 used in a `for_each` argument, and `setproduct` can be a useful helper function 126 for the situation where you want to find all unique combinations of elements in 127 a number of different collections. 128 129 For example, consider a module that declares variables like the following: 130 131 ```hcl 132 variable "networks" { 133 type = map(object({ 134 base_cidr_block = string 135 })) 136 } 137 138 variable "subnets" { 139 type = map(object({ 140 number = number 141 })) 142 } 143 ``` 144 145 If the goal is to create each of the defined subnets per each of the defined networks, creating the top-level networks can directly use `var.networks` because it is already in a form where the resulting instances match one-to-one with map elements: 146 147 ```hcl 148 resource "aws_vpc" "example" { 149 for_each = var.networks 150 151 cidr_block = each.value.base_cidr_block 152 } 153 ``` 154 155 However, to declare all of the _subnets_ with a single `resource` block, you must first produce a collection whose elements represent all of the combinations of networks and subnets, so that each element itself represents a subnet: 156 157 ```hcl 158 locals { 159 # setproduct works with sets and lists, but the variables are both maps 160 # so convert them first. 161 networks = [ 162 for key, network in var.networks : { 163 key = key 164 cidr_block = network.cidr_block 165 } 166 ] 167 subnets = [ 168 for key, subnet in var.subnets : { 169 key = key 170 number = subnet.number 171 } 172 ] 173 174 network_subnets = [ 175 # in pair, element zero is a network and element one is a subnet, 176 # in all unique combinations. 177 for pair in setproduct(local.networks, local.subnets) : { 178 network_key = pair[0].key 179 subnet_key = pair[1].key 180 network_id = aws_vpc.example[pair[0].key].id 181 182 # The cidr_block is derived from the corresponding network. Refer to the 183 # cidrsubnet function for more information on how this calculation works. 184 cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number) 185 } 186 ] 187 } 188 189 resource "aws_subnet" "example" { 190 # local.network_subnets is a list, so project it into a map 191 # where each key is unique. Combine the network and subnet keys to 192 # produce a single unique key per instance. 193 for_each = { 194 for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet 195 } 196 197 vpc_id = each.value.network_id 198 availability_zone = each.value.subnet_key 199 cidr_block = each.value.cidr_block 200 } 201 ``` 202 203 The `network_subnets` list in the example above creates one subnet instance per combination of network and subnet elements in the input variables. So for this example input: 204 205 ```hcl 206 networks = { 207 a = { 208 base_cidr_block = "10.1.0.0/16" 209 } 210 b = { 211 base_cidr_block = "10.2.0.0/16" 212 } 213 } 214 subnets = { 215 a = { 216 number = 1 217 } 218 b = { 219 number = 2 220 } 221 c = { 222 number = 3 223 } 224 } 225 ``` 226 227 The `nework_subnets` output would look similar to the following: 228 229 ```hcl 230 [ 231 { 232 "cidr_block" = "10.1.16.0/20" 233 "network_id" = "vpc-0bfb00ca6173ea5aa" 234 "network_key" = "a" 235 "subnet_key" = "a" 236 }, 237 { 238 "cidr_block" = "10.1.32.0/20" 239 "network_id" = "vpc-0bfb00ca6173ea5aa" 240 "network_key" = "a" 241 "subnet_key" = "b" 242 }, 243 { 244 "cidr_block" = "10.1.48.0/20" 245 "network_id" = "vpc-0bfb00ca6173ea5aa" 246 "network_key" = "a" 247 "subnet_key" = "c" 248 }, 249 { 250 "cidr_block" = "10.2.16.0/20" 251 "network_id" = "vpc-0d193e011f6211a7d" 252 "network_key" = "b" 253 "subnet_key" = "a" 254 }, 255 { 256 "cidr_block" = "10.2.32.0/20" 257 "network_id" = "vpc-0d193e011f6211a7d" 258 "network_key" = "b" 259 "subnet_key" = "b" 260 }, 261 { 262 "cidr_block" = "10.2.48.0/20" 263 "network_id" = "vpc-0d193e011f6211a7d" 264 "network_key" = "b" 265 "subnet_key" = "c" 266 }, 267 ] 268 ``` 269 270 ## Related Functions 271 272 - [`contains`](/language/functions/contains) tests whether a given list or set contains 273 a given element value. 274 - [`flatten`](/language/functions/flatten) is useful for flattening hierarchical data 275 into a single list, for situations where the relationships between two 276 object types are defined explicitly. 277 - [`setintersection`](/language/functions/setintersection) computes the _intersection_ of 278 multiple sets. 279 - [`setsubtract`](/language/functions/setsubtract) computes the _relative complement_ of two sets 280 - [`setunion`](/language/functions/setunion) computes the _union_ of multiple 281 sets.