github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/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 148 networks, creating the top-level networks can directly use `var.networks` 149 because it's already in a form where the resulting instances match one-to-one 150 with map elements: 151 152 ```hcl 153 resource "aws_vpc" "example" { 154 for_each = var.networks 155 156 cidr_block = each.value.base_cidr_block 157 } 158 ``` 159 160 However, in order to declare all of the _subnets_ with a single `resource` 161 block, we must first produce a collection whose elements represent all of 162 the combinations of networks and subnets, so that each element itself 163 represents a subnet: 164 165 ```hcl 166 locals { 167 # setproduct works with sets and lists, but our variables are both maps 168 # so we'll need to convert them first. 169 networks = [ 170 for key, network in var.networks : { 171 key = key 172 cidr_block = network.cidr_block 173 } 174 ] 175 subnets = [ 176 for key, subnet in var.subnets : { 177 key = key 178 number = subnet.number 179 } 180 ] 181 182 network_subnets = [ 183 # in pair, element zero is a network and element one is a subnet, 184 # in all unique combinations. 185 for pair in setproduct(local.networks, local.subnets) : { 186 network_key = pair[0].key 187 subnet_key = pair[1].key 188 network_id = aws_vpc.example[pair[0].key].id 189 190 # The cidr_block is derived from the corresponding network. See the 191 # cidrsubnet function for more information on how this calculation works. 192 cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number) 193 } 194 ] 195 } 196 197 resource "aws_subnet" "example" { 198 # local.network_subnets is a list, so we must now project it into a map 199 # where each key is unique. We'll combine the network and subnet keys to 200 # produce a single unique key per instance. 201 for_each = { 202 for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet 203 } 204 205 vpc_id = each.value.network_id 206 availability_zone = each.value.subnet_key 207 cidr_block = each.value.cidr_block 208 } 209 ``` 210 211 The above results in one subnet instance per combination of network and subnet 212 elements in the input variables. 213 214 ## Related Functions 215 216 * [`contains`](./contains.html) tests whether a given list or set contains 217 a given element value. 218 * [`flatten`](./flatten.html) is useful for flattening hierarchical data 219 into a single list, for situations where the relationships between two 220 object types are defined explicitly. 221 * [`setintersection`](./setintersection.html) computes the _intersection_ of 222 multiple sets. 223 * [`setsubtract`](./setsubtract.html) computes the _relative complement_ of two sets 224 * [`setunion`](./setunion.html) computes the _union_ of multiple 225 sets.