Skip to content

Instantly share code, notes, and snippets.

@chanux

chanux/main.tf Secret

Last active July 27, 2024 02:24
Show Gist options
  • Save chanux/e9ebabb46169b9d2c46c331f56da4800 to your computer and use it in GitHub Desktop.
Save chanux/e9ebabb46169b9d2c46c331f56da4800 to your computer and use it in GitHub Desktop.
Demystifying nested loops in Terraform
locals {
# You have configuration in a data structure, a list of objects where the
# object defines an instance of connections with the sources and the
# desitnations it is supposed to connect to.
# However, it does not make sense to establish a connection between 'the same
# endpoints' so we need to exclude those.
data = [
{
name = "foo"
sources = ["a", "b",]
dests = ["x", "y", "a",]
},
{
name = "bar"
sources = ["a", "b",]
dests = ["x", "b",]
},
]
# We would need nested loops to come up with all the connection objects. For
# readability, let's build (sculpt, if you may) the final data structure
# which is a group of 'atomic' data needed for each connection in locals.
res = merge(flatten([
for o in local.data :
[
for s in o.sources :
{
for d in o.dests :
"${o.name}_${s}_${d}" => {
source : s
dest : d
} if s != d
}
]
])...)
# In the first two loops, we make two lists of lists (Please note that this
# is just for the sake of iterating). At the bottom level, we build a map of
# objects. This is the meat of it. Here we build the final data set needed
# for a single connection. The map key is designed to be unique across
# elments. It's also best to make it descriptive, so when you see it in the
# state, it makes
# sense to you!
#
# We exclude the case where source and destination are the same with
# `if s != d`
#
# At the top level, we flatten the list of lists we built just and then we
# use ellipsis(...) on the resulting list. This is to dynamically split the
# list of maps into a bunch of map elements so that we can merge it all into
# a single map (The lists are just means to an end. Remember? they were just
# for the sake of iterating).
}
output "res" {
value = local.res
}
resource "local_file" "main" {
for_each = local.res
content = jsonencode(each.value)
filename = "${path.module}/data/${each.key}"
}
@chanux
Copy link
Author

chanux commented Jul 27, 2024

Example output, for reference:

res = {
  "bar_a_b" = {
    "dest" = "b"
    "source" = "a"
  }
  "bar_a_x" = {
    "dest" = "x"
    "source" = "a"
  }
  "bar_b_x" = {
    "dest" = "x"
    "source" = "b"
  }
  "foo_a_x" = {
    "dest" = "x"
    "source" = "a"
  }
  "foo_a_y" = {
    "dest" = "y"
    "source" = "a"
  }
  "foo_b_a" = {
    "dest" = "a"
    "source" = "b"
  }
  "foo_b_x" = {
    "dest" = "x"
    "source" = "b"
  }
  "foo_b_y" = {
    "dest" = "y"
    "source" = "b"
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment