Expanding Function Arguments in Terraform

The other day I was wondering if it was possble to unpack a list of elements as args to a function in Terraform similar to * in Python or splat in Ruby? Because this code will give invalid function argument errors:

locals {
  new_bits = [4,4,8,4]
  subnets = cidrsubnets("10.1.0.0/16", local.new_bits) # <= can i unpack?
}

It turns out you can do this with the expansion symbol ... (three periods). Not to be confused with the Unicode ellipsis character. In the Terraform docs it’s used for Expanding Function Arguments:

locals {
  new_bits = [4,4,8,4]
  subnets = cidrsubnets("10.1.0.0/16", local.new_bits...)
}

Resulting subnets list:

local.subnets = [
  "10.1.0.0/20",
  "10.1.16.0/20",
  "10.1.32.0/24",
  "10.1.48.0/20",
]

It’s pretty straight forward now that we know to use the expansion symbol ... for the “unpacking” behavior. But wait, there’s more! While reading through the For Expressions section I came accross using an expansion symbol in a for loop “if the result type is an object (using { and } delimiters) then the value result expression can be followed by the … symbol to group together results that have a common key”.

Apparently, if we attempt to group values with the same key using for without ... to generate a new map then you get a Duplicate object key error:

$ terraform console
> { for s in ["here", "there", "this", "that"] : substr(s, 0, 1) => s }

Error: Duplicate object key

  on <console-input> line 1:
  (source code not available)

Two different items produced the key "t" in this 'for' expression. If
duplicates are expected, use the ellipsis (...) after the value expression to
enable grouping by key.

The error message says to use the ellipsis but it’s not clear on how to use it. And it’s weird that the error refers to ... as an ellipsis because the docs call it an expansion symbol.

Now ... enters the ring! And we’re able to group each word in the list by the same key, the first letter in each word, in the new map!

$ terraform console
> { for s in ["here", "there", "this", "that"] : substr(s, 0, 1) => s... }
{
  "h" = [
    "here",
  ]
  "t" = [
    "there",
    "this",
    "that"
  ]
}

I haven’t needed a for filter with group by behavior yet but this will definitely come in handy when I do!

RTFM is a WIP.

~jq1


Update:

Here’s another great unpacking example from Stack Overflow.

Feedback

What did you think about this post? jude@jq1.io