282 lines
7.0 KiB
Plaintext
282 lines
7.0 KiB
Plaintext
---
|
|
page_title: setproduct - Functions - Configuration Language
|
|
description: |-
|
|
The setproduct function finds all of the possible combinations of elements
|
|
from all of the given sets by computing the cartesian product.
|
|
---
|
|
|
|
# `setproduct` Function
|
|
|
|
The `setproduct` function finds all of the possible combinations of elements
|
|
from all of the given sets by computing the
|
|
[Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product).
|
|
|
|
```hcl
|
|
setproduct(sets...)
|
|
```
|
|
|
|
This function is particularly useful for finding the exhaustive set of all
|
|
combinations of members of multiple sets, such as per-application-per-environment
|
|
resources.
|
|
|
|
```
|
|
> setproduct(["development", "staging", "production"], ["app1", "app2"])
|
|
[
|
|
[
|
|
"development",
|
|
"app1",
|
|
],
|
|
[
|
|
"development",
|
|
"app2",
|
|
],
|
|
[
|
|
"staging",
|
|
"app1",
|
|
],
|
|
[
|
|
"staging",
|
|
"app2",
|
|
],
|
|
[
|
|
"production",
|
|
"app1",
|
|
],
|
|
[
|
|
"production",
|
|
"app2",
|
|
],
|
|
]
|
|
```
|
|
|
|
You must pass at least two arguments to this function.
|
|
|
|
Although defined primarily for sets, this function can also work with lists.
|
|
If all of the given arguments are lists then the result is a list, preserving
|
|
the ordering of the given lists. Otherwise the result is a set. In either case,
|
|
the result's element type is a list of values corresponding to each given
|
|
argument in turn.
|
|
|
|
## Examples
|
|
|
|
There is an example of the common usage of this function above. There are some
|
|
other situations that are less common when hand-writing but may arise in
|
|
reusable module situations.
|
|
|
|
If any of the arguments is empty then the result is always empty itself,
|
|
similar to how multiplying any number by zero gives zero:
|
|
|
|
```
|
|
> setproduct(["development", "staging", "production"], [])
|
|
[]
|
|
```
|
|
|
|
Similarly, if all of the arguments have only one element then the result has
|
|
only one element, which is the first element of each argument:
|
|
|
|
```
|
|
> setproduct(["a"], ["b"])
|
|
[
|
|
[
|
|
"a",
|
|
"b",
|
|
],
|
|
]
|
|
```
|
|
|
|
Each argument must have a consistent type for all of its elements. If not,
|
|
Terraform will attempt to convert to the most general type, or produce an
|
|
error if such a conversion is impossible. For example, mixing both strings and
|
|
numbers results in the numbers being converted to strings so that the result
|
|
elements all have a consistent type:
|
|
|
|
```
|
|
> setproduct(["staging", "production"], ["a", 2])
|
|
[
|
|
[
|
|
"staging",
|
|
"a",
|
|
],
|
|
[
|
|
"staging",
|
|
"2",
|
|
],
|
|
[
|
|
"production",
|
|
"a",
|
|
],
|
|
[
|
|
"production",
|
|
"2",
|
|
],
|
|
]
|
|
```
|
|
|
|
## Finding combinations for `for_each`
|
|
|
|
The
|
|
[resource `for_each`](/language/meta-arguments/for_each)
|
|
and
|
|
[`dynamic` block](/language/expressions/dynamic-blocks)
|
|
language features both require a collection value that has one element for
|
|
each repetition.
|
|
|
|
Sometimes your input data comes in separate values that cannot be directly
|
|
used in a `for_each` argument, and `setproduct` can be a useful helper function
|
|
for the situation where you want to find all unique combinations of elements in
|
|
a number of different collections.
|
|
|
|
For example, consider a module that declares variables like the following:
|
|
|
|
```hcl
|
|
variable "networks" {
|
|
type = map(object({
|
|
base_cidr_block = string
|
|
}))
|
|
}
|
|
|
|
variable "subnets" {
|
|
type = map(object({
|
|
number = number
|
|
}))
|
|
}
|
|
```
|
|
|
|
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:
|
|
|
|
```hcl
|
|
resource "aws_vpc" "example" {
|
|
for_each = var.networks
|
|
|
|
cidr_block = each.value.base_cidr_block
|
|
}
|
|
```
|
|
|
|
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:
|
|
|
|
```hcl
|
|
locals {
|
|
# setproduct works with sets and lists, but the variables are both maps
|
|
# so convert them first.
|
|
networks = [
|
|
for key, network in var.networks : {
|
|
key = key
|
|
cidr_block = network.cidr_block
|
|
}
|
|
]
|
|
subnets = [
|
|
for key, subnet in var.subnets : {
|
|
key = key
|
|
number = subnet.number
|
|
}
|
|
]
|
|
|
|
network_subnets = [
|
|
# in pair, element zero is a network and element one is a subnet,
|
|
# in all unique combinations.
|
|
for pair in setproduct(local.networks, local.subnets) : {
|
|
network_key = pair[0].key
|
|
subnet_key = pair[1].key
|
|
network_id = aws_vpc.example[pair[0].key].id
|
|
|
|
# The cidr_block is derived from the corresponding network. Refer to the
|
|
# cidrsubnet function for more information on how this calculation works.
|
|
cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number)
|
|
}
|
|
]
|
|
}
|
|
|
|
resource "aws_subnet" "example" {
|
|
# local.network_subnets is a list, so project it into a map
|
|
# where each key is unique. Combine the network and subnet keys to
|
|
# produce a single unique key per instance.
|
|
for_each = {
|
|
for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet
|
|
}
|
|
|
|
vpc_id = each.value.network_id
|
|
availability_zone = each.value.subnet_key
|
|
cidr_block = each.value.cidr_block
|
|
}
|
|
```
|
|
|
|
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:
|
|
|
|
```hcl
|
|
networks = {
|
|
a = {
|
|
base_cidr_block = "10.1.0.0/16"
|
|
}
|
|
b = {
|
|
base_cidr_block = "10.2.0.0/16"
|
|
}
|
|
}
|
|
subnets = {
|
|
a = {
|
|
number = 1
|
|
}
|
|
b = {
|
|
number = 2
|
|
}
|
|
c = {
|
|
number = 3
|
|
}
|
|
}
|
|
```
|
|
|
|
The `nework_subnets` output would look similar to the following:
|
|
|
|
```hcl
|
|
[
|
|
{
|
|
"cidr_block" = "10.1.16.0/20"
|
|
"network_id" = "vpc-0bfb00ca6173ea5aa"
|
|
"network_key" = "a"
|
|
"subnet_key" = "a"
|
|
},
|
|
{
|
|
"cidr_block" = "10.1.32.0/20"
|
|
"network_id" = "vpc-0bfb00ca6173ea5aa"
|
|
"network_key" = "a"
|
|
"subnet_key" = "b"
|
|
},
|
|
{
|
|
"cidr_block" = "10.1.48.0/20"
|
|
"network_id" = "vpc-0bfb00ca6173ea5aa"
|
|
"network_key" = "a"
|
|
"subnet_key" = "c"
|
|
},
|
|
{
|
|
"cidr_block" = "10.2.16.0/20"
|
|
"network_id" = "vpc-0d193e011f6211a7d"
|
|
"network_key" = "b"
|
|
"subnet_key" = "a"
|
|
},
|
|
{
|
|
"cidr_block" = "10.2.32.0/20"
|
|
"network_id" = "vpc-0d193e011f6211a7d"
|
|
"network_key" = "b"
|
|
"subnet_key" = "b"
|
|
},
|
|
{
|
|
"cidr_block" = "10.2.48.0/20"
|
|
"network_id" = "vpc-0d193e011f6211a7d"
|
|
"network_key" = "b"
|
|
"subnet_key" = "c"
|
|
},
|
|
]
|
|
```
|
|
|
|
## Related Functions
|
|
|
|
- [`contains`](/language/functions/contains) tests whether a given list or set contains
|
|
a given element value.
|
|
- [`flatten`](/language/functions/flatten) is useful for flattening hierarchical data
|
|
into a single list, for situations where the relationships between two
|
|
object types are defined explicitly.
|
|
- [`setintersection`](/language/functions/setintersection) computes the _intersection_ of
|
|
multiple sets.
|
|
- [`setsubtract`](/language/functions/setsubtract) computes the _relative complement_ of two sets
|
|
- [`setunion`](/language/functions/setunion) computes the _union_ of multiple
|
|
sets.
|