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