github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/website/docs/language/functions/setproduct.mdx (about)

     1  ---
     2  page_title: setproduct - Functions - Configuration Language
     3  description: |-
     4    The setproduct function finds all of the possible combinations of elements
     5    from all of the given sets by computing the cartesian product.
     6  ---
     7  
     8  # `setproduct` Function
     9  
    10  The `setproduct` function finds all of the possible combinations of elements
    11  from all of the given sets by computing the
    12  [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product).
    13  
    14  ```hcl
    15  setproduct(sets...)
    16  ```
    17  
    18  This function is particularly useful for finding the exhaustive set of all
    19  combinations of members of multiple sets, such as per-application-per-environment
    20  resources.
    21  
    22  ```
    23  > setproduct(["development", "staging", "production"], ["app1", "app2"])
    24  [
    25    [
    26      "development",
    27      "app1",
    28    ],
    29    [
    30      "development",
    31      "app2",
    32    ],
    33    [
    34      "staging",
    35      "app1",
    36    ],
    37    [
    38      "staging",
    39      "app2",
    40    ],
    41    [
    42      "production",
    43      "app1",
    44    ],
    45    [
    46      "production",
    47      "app2",
    48    ],
    49  ]
    50  ```
    51  
    52  You must pass at least two arguments to this function.
    53  
    54  Although defined primarily for sets, this function can also work with lists.
    55  If all of the given arguments are lists then the result is a list, preserving
    56  the ordering of the given lists. Otherwise the result is a set. In either case,
    57  the result's element type is a list of values corresponding to each given
    58  argument in turn.
    59  
    60  ## Examples
    61  
    62  There is an example of the common usage of this function above. There are some
    63  other situations that are less common when hand-writing but may arise in
    64  reusable module situations.
    65  
    66  If any of the arguments is empty then the result is always empty itself,
    67  similar to how multiplying any number by zero gives zero:
    68  
    69  ```
    70  > setproduct(["development", "staging", "production"], [])
    71  []
    72  ```
    73  
    74  Similarly, if all of the arguments have only one element then the result has
    75  only one element, which is the first element of each argument:
    76  
    77  ```
    78  > setproduct(["a"], ["b"])
    79  [
    80    [
    81      "a",
    82      "b",
    83    ],
    84  ]
    85  ```
    86  
    87  Each argument must have a consistent type for all of its elements. If not,
    88  Terraform will attempt to convert to the most general type, or produce an
    89  error if such a conversion is impossible. For example, mixing both strings and
    90  numbers results in the numbers being converted to strings so that the result
    91  elements all have a consistent type:
    92  
    93  ```
    94  > setproduct(["staging", "production"], ["a", 2])
    95  [
    96    [
    97      "staging",
    98      "a",
    99    ],
   100    [
   101      "staging",
   102      "2",
   103    ],
   104    [
   105      "production",
   106      "a",
   107    ],
   108    [
   109      "production",
   110      "2",
   111    ],
   112  ]
   113  ```
   114  
   115  ## Finding combinations for `for_each`
   116  
   117  The
   118  [resource `for_each`](/language/meta-arguments/for_each)
   119  and
   120  [`dynamic` block](/language/expressions/dynamic-blocks)
   121  language features both require a collection value that has one element for
   122  each repetition.
   123  
   124  Sometimes your input data comes in separate values that cannot be directly
   125  used in a `for_each` argument, and `setproduct` can be a useful helper function
   126  for the situation where you want to find all unique combinations of elements in
   127  a number of different collections.
   128  
   129  For example, consider a module that declares variables like the following:
   130  
   131  ```hcl
   132  variable "networks" {
   133    type = map(object({
   134      base_cidr_block = string
   135    }))
   136  }
   137  
   138  variable "subnets" {
   139    type = map(object({
   140      number = number
   141    }))
   142  }
   143  ```
   144  
   145  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:
   146  
   147  ```hcl
   148  resource "aws_vpc" "example" {
   149    for_each = var.networks
   150  
   151    cidr_block = each.value.base_cidr_block
   152  }
   153  ```
   154  
   155  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:
   156  
   157  ```hcl
   158  locals {
   159    # setproduct works with sets and lists, but the variables are both maps
   160    # so convert them first.
   161    networks = [
   162      for key, network in var.networks : {
   163        key        = key
   164        cidr_block = network.cidr_block
   165      }
   166    ]
   167    subnets = [
   168      for key, subnet in var.subnets : {
   169        key    = key
   170        number = subnet.number
   171      }
   172    ]
   173  
   174    network_subnets = [
   175      # in pair, element zero is a network and element one is a subnet,
   176      # in all unique combinations.
   177      for pair in setproduct(local.networks, local.subnets) : {
   178        network_key = pair[0].key
   179        subnet_key  = pair[1].key
   180        network_id  = aws_vpc.example[pair[0].key].id
   181  
   182        # The cidr_block is derived from the corresponding network. Refer to the
   183        # cidrsubnet function for more information on how this calculation works.
   184        cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number)
   185      }
   186    ]
   187  }
   188  
   189  resource "aws_subnet" "example" {
   190    # local.network_subnets is a list, so project it into a map
   191    # where each key is unique. Combine the network and subnet keys to
   192    # produce a single unique key per instance.
   193    for_each = {
   194      for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet
   195    }
   196  
   197    vpc_id            = each.value.network_id
   198    availability_zone = each.value.subnet_key
   199    cidr_block        = each.value.cidr_block
   200  }
   201  ```
   202  
   203  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:
   204  
   205  ```hcl
   206  networks = {
   207    a = {
   208      base_cidr_block = "10.1.0.0/16"
   209    }
   210    b = {
   211      base_cidr_block = "10.2.0.0/16"
   212    }
   213  }
   214  subnets = {
   215    a = {
   216      number = 1
   217    }
   218    b = {
   219      number = 2
   220    }
   221    c = {
   222      number = 3
   223    }
   224  }
   225  ```
   226  
   227  The `nework_subnets` output would look similar to the following:
   228  
   229  ```hcl
   230  [
   231    {
   232      "cidr_block" = "10.1.16.0/20"
   233      "network_id" = "vpc-0bfb00ca6173ea5aa"
   234      "network_key" = "a"
   235      "subnet_key" = "a"
   236    },
   237    {
   238      "cidr_block" = "10.1.32.0/20"
   239      "network_id" = "vpc-0bfb00ca6173ea5aa"
   240      "network_key" = "a"
   241      "subnet_key" = "b"
   242    },
   243    {
   244      "cidr_block" = "10.1.48.0/20"
   245      "network_id" = "vpc-0bfb00ca6173ea5aa"
   246      "network_key" = "a"
   247      "subnet_key" = "c"
   248    },
   249    {
   250      "cidr_block" = "10.2.16.0/20"
   251      "network_id" = "vpc-0d193e011f6211a7d"
   252      "network_key" = "b"
   253      "subnet_key" = "a"
   254    },
   255    {
   256      "cidr_block" = "10.2.32.0/20"
   257      "network_id" = "vpc-0d193e011f6211a7d"
   258      "network_key" = "b"
   259      "subnet_key" = "b"
   260    },
   261    {
   262      "cidr_block" = "10.2.48.0/20"
   263      "network_id" = "vpc-0d193e011f6211a7d"
   264      "network_key" = "b"
   265      "subnet_key" = "c"
   266    },
   267  ]
   268  ```
   269  
   270  ## Related Functions
   271  
   272  - [`contains`](/language/functions/contains) tests whether a given list or set contains
   273    a given element value.
   274  - [`flatten`](/language/functions/flatten) is useful for flattening hierarchical data
   275    into a single list, for situations where the relationships between two
   276    object types are defined explicitly.
   277  - [`setintersection`](/language/functions/setintersection) computes the _intersection_ of
   278    multiple sets.
   279  - [`setsubtract`](/language/functions/setsubtract) computes the _relative complement_ of two sets
   280  - [`setunion`](/language/functions/setunion) computes the _union_ of multiple
   281    sets.