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.