Post

Terraform Iterators

  1. Using for_each on a list of strings:

    1
    2
    3
    4
    5
    6
    7
    8
    
     locals {
       ip_addresses = ["10.0.0.1", "10.0.0.2"]
     }
    
     resource "example" "example" {
       for_each   = toset(local.ip_addresses)
       ip_address = each.key
     }
    
  2. Using for_each on a list of objects

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
     locals {
       virtual_machines = [
         {
           ip_address = "10.0.0.1"
           name       = "vm-1"
         },
         {
           ip_address = "10.0.0.1"
           name       = "vm-2"
         }
       ]
     }    
    
     resource "example" "example" {
       for_each   = {
         for index, vm in local.virtual_machines:
         vm.name => vm
       }
       name       = each.value.name
       ip_address = each.value.ip_address
     }
    
  3. Using for_each to combine two lists

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
     locals {
       domains = [
         "https://abc.com",
         "https://def.com"
       ]
       paths = [
         "/one",
         "/two",
         "/three"
       ]
     }
            
     resource "example" "example" {
       # Loop over both lists and flatten the result
       urls = flatten([
         for domain in local.domains : [
           for path in local.paths : {
             domain = domain
             path   = path
           }
         ]
       ])
     }
    
  4. Using for_each on a nested block

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
     # Using the optional() keyword makes fields null if not present
     variable "routes" {
       type = list(
         name   = string
         path   = string
         config = optional(object({
           cache_enabled = bool
           https_only    = bool
         }))
       default = []
     }
    
     resource "example" "example" {
       name = ...
          
       dynamic "route" {
         for_each = {
           for route in var.routes :
           route.name => route
         }
         content {
           # Note: <top_level_block>.value.<object_key>
           name = route.value.name
         }
            
         dynamic "configuration" {
           # Note: <top_level_block>.value.<optional_object_key>
           for_each = route.value.config != null ? [1] : []
           content {
             cache_enabled = route.value.config.cache_enabled
             https_only    = route.value.config.https_only
           }
         }
       }
    
  5. Using for_each as a conditional

``` variable “deploy_example” { type = bool description = “Indicates whether to deploy something.” default = true }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Using count and a conditional, for_each is also possible here.
# See the next solution using a for_each with a conditional.
resource "example" "example" {
  count      = var.deploy_example ? 0 : 1
  name       = ...
  ip_address = ...
}

variable "enable_logs" {
  type        = bool
  description = "Indicates whether to enable something."
  default     = false
}

resource "example" "example" {
  name       = ...
  ip_address = ...

  # Note: dynamic blocks cannot use count!
  # Using for_each with an empty list and list(1) as a readable alternative. 
  dynamic "logs" {
    for_each = var.enable_logs ? [] : [1]
    content {
      name     = "logging"
    }
  }
}
```
This post is licensed under CC BY 4.0 by the author.