More examples with terraform for and for_each loops

Loops in terraform is not something new. There is a bunch of articles with examples in the internet. However I had a number of cases when I could not find the example of my particular case so “d like to add my own article with my own examples.

Loop over list.

Let’s sasy we have a list and want to build the dynamic block for some resources using the list of values. Hoever for_each is applicable to the map or set only. If you are going to ship the list to foreach, than terraform will fail with the following error

is a tuple with X elements

Convert your list tolist() to fix it. Here is an example where I build the dynamic block of rules in gcp firewall using the list of protocols:

locals {
    protocols = tolist(["tcp","udp"])
}

resource "google_compute_firewall" "foo" {
  name      = "example"
  network   = "default"
  dynamic "allow" {
    for_each = local.protocols
    content {
      protocol = allow.value
    }
  }
  source_ranges = "0.0.0.0/0"
}

Here is another example with ports

locals {
    ports = tolist(["22","80","443"])
}

resource "google_compute_firewall" "foo" {
  name      = "example"
  network   = "default"

  allow {
    protocol = var.gcp_firewall_protocol
    ports    = [for port in local.ports : port.value]
  }

  source_ranges = "0.0.0.0/0"
}

More complicated example. Here I loop over the map of string values and build dynamic block:

locals {
  firewall_rules = {
    ssh = {
      port = "22",
      protocol = "tcp"
    },
    http = {
      port = "80",
      protocol = "tcp"
    },
    https = {
      port = "443",
      protocol = "tcp"
    },
    dns = {
      port = "53",
      protocol = "udp"
    }
  }
}

resource "google_compute_firewall" "foo" {
  name      = "example"
  network   = "default"

  dynamic "allow" {
    for_each = local.firewall_rules
    content {
      protocol = allow.value.protocol
      ports    = [allow.value.port]
    }
  }

  source_ranges = ["0.0.0.0/0"]
}

And even more complicated example. Here I loop over the map of strings and lists and build the dinamic block:

locals {
  firewall_rules = {
    tcp = {
      ports = ["22", "80", "443"]
      protocol = "tcp"
    },
    udp = {
      ports = ["53"],
      protocol = "udp"
    }
  }
}

resource "google_compute_firewall" "foo" {
  name      = "example"
  network   = "default"

  dynamic "allow" {
    for_each = local.firewall_rules
    content {
      protocol = allow.value.protocol
      ports    = [for port in allow.value.ports : port]
    }
  }

  source_ranges = ["0.0.0.0/0"]
}

Create list from map indexes

locals {
  test_map = {
    "key1" = {
      "var1" = "val1"
    },
    "key2" = {
      "var2" = "val2"
    },
    "key3" = {
      "var3" = "val3"
    }
  }

  test_map_keys = [for k, v in local.test_map : k]
}

output "test_map_keys" {
  value = local.test_map_keys
}

Output instance ids

Let’s say you created the list of instances with for_each loop

resource "aws_instance" "ec2" {
  for_each      = local.map_of_instances
  ....
}

Possible output options are the following:

key=>value map

output instance_ids {
  value = { for key, value in aws_instance.ec2 : key => value.id }
}

list of IDs

output instance_ids {
  value =  [ for instance in aws_instance.ec2 : instance.id ]
}

External links: