github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/website/docs/configuration/functions/setproduct.html.md (about) 1 --- 2 layout: "functions" 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 -> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and 13 earlier, see 14 [0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). 15 16 The `setproduct` function finds all of the possible combinations of elements 17 from all of the given sets by computing the 18 [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product). 19 20 ```hcl 21 setproduct(sets...) 22 ``` 23 24 This function is particularly useful for finding the exhaustive set of all 25 combinations of members of multiple sets, such as per-application-per-environment 26 resources. 27 28 ``` 29 > setproduct(["development", "staging", "production"], ["app1", "app2"]) 30 [ 31 [ 32 "development", 33 "app1", 34 ], 35 [ 36 "development", 37 "app2", 38 ], 39 [ 40 "staging", 41 "app1", 42 ], 43 [ 44 "staging", 45 "app2", 46 ], 47 [ 48 "production", 49 "app1", 50 ], 51 [ 52 "production", 53 "app2", 54 ], 55 ] 56 ``` 57 58 You must past at least two arguments to this function. 59 60 Although defined primarily for sets, this function can also work with lists. 61 If all of the given arguments are lists then the result is a list, preserving 62 the ordering of the given lists. Otherwise the result is a set. In either case, 63 the result's element type is a list of values corresponding to each given 64 argument in turn. 65 66 ## Examples 67 68 There is an example of the common usage of this function above. There are some 69 other situations that are less common when hand-writing but may arise in 70 reusable module situations. 71 72 If any of the arguments is empty then the result is always empty itself, 73 similar to how multiplying any number by zero gives zero: 74 75 ``` 76 > setproduct(["development", "staging", "production"], []) 77 [] 78 ``` 79 80 Similarly, if all of the arguments have only one element then the result has 81 only one element, which is the first element of each argument: 82 83 ``` 84 > setproduct(["a"], ["b"]) 85 [ 86 [ 87 "a", 88 "b", 89 ], 90 ] 91 ``` 92 93 Each argument must have a consistent type for all of its elements. If not, 94 Terraform will attempt to convert to the most general type, or produce an 95 error if such a conversion is impossible. For example, mixing both strings and 96 numbers results in the numbers being converted to strings so that the result 97 elements all have a consistent type: 98 99 ``` 100 > setproduct(["staging", "production"], ["a", 2]) 101 [ 102 [ 103 "staging", 104 "a", 105 ], 106 [ 107 "staging", 108 "2", 109 ], 110 [ 111 "production", 112 "a", 113 ], 114 [ 115 "production", 116 "2", 117 ], 118 ] 119 ``` 120 121 ## Finding combinations for `for_each` 122 123 The 124 [resource `for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) 125 and 126 [`dynamic` block](/docs/configuration/expressions.html#dynamic-blocks) 127 language features both require a collection value that has one element for 128 each repetition. 129 130 Sometimes your input data comes in separate values that cannot be directly 131 used in a `for_each` argument, and `setproduct` can be a useful helper function 132 for the situation where you want to find all unique combinations of elements in 133 a number of different collections. 134 135 For example, consider a module that declares variables like the following: 136 137 ```hcl 138 variable "networks" { 139 type = map(object({ 140 base_cidr_block = string 141 })) 142 } 143 144 variable "subnets" { 145 type = map(object({ 146 number = number 147 })) 148 } 149 ``` 150 151 If the goal is to create each of the defined subnets per each of the defined 152 networks, creating the top-level networks can directly use `var.networks` 153 because it's already in a form where the resulting instances match one-to-one 154 with map elements: 155 156 ```hcl 157 resource "aws_vpc" "example" { 158 for_each = var.networks 159 160 cidr_block = each.value.base_cidr_block 161 } 162 ``` 163 164 However, in order to declare all of the _subnets_ with a single `resource` 165 block, we must first produce a collection whose elements represent all of 166 the combinations of networks and subnets, so that each element itself 167 represents a subnet: 168 169 ```hcl 170 locals { 171 # setproduct works with sets and lists, but our variables are both maps 172 # so we'll need to convert them first. 173 networks = [ 174 for key, network in var.networks : { 175 key = key 176 cidr_block = network.cidr_block 177 } 178 ] 179 subnets = [ 180 for key, subnet in var.subnets : { 181 key = key 182 number = subnet.number 183 } 184 ] 185 186 network_subnets = [ 187 # in pair, element zero is a network and element one is a subnet, 188 # in all unique combinations. 189 for pair in setproduct(local.networks, local.subnets) : { 190 network_key = pair[0].key 191 subnet_key = pair[1].key 192 network_id = aws_vpc.example[pair[0].key].id 193 194 # The cidr_block is derived from the corresponding network. See the 195 # cidrsubnet function for more information on how this calculation works. 196 cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number) 197 } 198 ] 199 } 200 201 resource "aws_subnet" "example" { 202 # local.network_subnets is a list, so we must now project it into a map 203 # where each key is unique. We'll combine the network and subnet keys to 204 # produce a single unique key per instance. 205 for_each = { 206 for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet 207 } 208 209 vpc_id = each.value.network_id 210 availability_zone = each.value.subnet_key 211 cidr_block = each.value_cidr_block 212 } 213 ``` 214 215 The above results in one subnet instance per combination of network and subnet 216 elements in the input variables. 217 218 ## Related Functions 219 220 * [`contains`](./contains.html) tests whether a given list or set contains 221 a given element value. 222 * [`flatten`](./flatten.html) is useful for flattening heirarchical data 223 into a single list, for situations where the relationships between two 224 object types are defined explicitly. 225 * [`setintersection`](./setintersection.html) computes the _intersection_ of 226 multiple sets. 227 * [`setsubtract`](./setsubtract.html) computes the _relative complement_ of two sets 228 * [`setunion`](./setunion.html) computes the _union_ of multiple 229 sets.