2019-01-15 18:47:14 +01:00
|
|
|
---
|
|
|
|
layout: "functions"
|
|
|
|
page_title: "setproduct - Functions - Configuration Language"
|
|
|
|
sidebar_current: "docs-funcs-collection-setproduct"
|
|
|
|
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
|
|
|
|
|
2019-01-17 01:33:57 +01:00
|
|
|
-> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and
|
|
|
|
earlier, see
|
|
|
|
[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html).
|
|
|
|
|
2019-01-15 18:47:14 +01:00
|
|
|
The `setproduct` function finds all of the possible combinations of elements
|
|
|
|
from all of the given sets by computing the
|
2019-09-05 19:08:34 +02:00
|
|
|
[Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product).
|
2019-01-15 18:47:14 +01:00
|
|
|
|
|
|
|
```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",
|
|
|
|
],
|
|
|
|
]
|
|
|
|
```
|
|
|
|
|
2020-03-23 13:55:08 +01:00
|
|
|
You must pass at least two arguments to this function.
|
2019-01-15 18:47:14 +01:00
|
|
|
|
|
|
|
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",
|
|
|
|
],
|
|
|
|
]
|
|
|
|
```
|
2019-01-15 20:51:21 +01:00
|
|
|
|
2019-10-11 21:02:24 +02:00
|
|
|
## Finding combinations for `for_each`
|
|
|
|
|
|
|
|
The
|
|
|
|
[resource `for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings)
|
|
|
|
and
|
|
|
|
[`dynamic` block](/docs/configuration/expressions.html#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's 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, in order to declare all of the _subnets_ with a single `resource`
|
|
|
|
block, we 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 our variables are both maps
|
|
|
|
# so we'll need to 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. See 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 we must now project it into a map
|
|
|
|
# where each key is unique. We'll 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 above results in one subnet instance per combination of network and subnet
|
|
|
|
elements in the input variables.
|
|
|
|
|
2019-01-15 20:51:21 +01:00
|
|
|
## Related Functions
|
|
|
|
|
2019-05-02 16:47:19 +02:00
|
|
|
* [`contains`](./contains.html) tests whether a given list or set contains
|
2019-01-15 20:51:21 +01:00
|
|
|
a given element value.
|
2019-10-11 21:02:24 +02:00
|
|
|
* [`flatten`](./flatten.html) is useful for flattening heirarchical data
|
|
|
|
into a single list, for situations where the relationships between two
|
|
|
|
object types are defined explicitly.
|
2019-01-15 20:51:21 +01:00
|
|
|
* [`setintersection`](./setintersection.html) computes the _intersection_ of
|
|
|
|
multiple sets.
|
2020-02-06 18:49:11 +01:00
|
|
|
* [`setsubtract`](./setsubtract.html) computes the _relative complement_ of two sets
|
2019-01-15 20:51:21 +01:00
|
|
|
* [`setunion`](./setunion.html) computes the _union_ of multiple
|
|
|
|
sets.
|