Why not arrays of objects?

Why doesn’t terraform have maps of object, so something like

variable "sites" {
    default = [
        {
            protocol    = "https",
            hostname    = "www.example1.com",
            webhook     = "https://webhook.com/sgfe78sgf78sgf78sgf78sdf78s",
            foo         = "bar"
        },
        {
            protocol    = "https",
            hostname    = "www.example2.com",
            webhook     = "https://webhook.com/as79d8fhad9fnaf9f98anf9anf9",
            foo         = "baz"
        }
    ]
}

to be used like

resource "aws_something" "something_else" {
  for_each = var.sites
  protocol  = each.protocol
  hostname = each.hostname
  ...
}

There are a lot of workarounds, but why doesn’t this exist natively? I mean, there must be a reason as to why the Terraform specifically doesn’t want this?

If there’s no reason but “We just didn’t built it (yet)”, I might have a shot at it myself and offer a pull request :wink:

Thanks,
Ingmar

It does.

That’s a tuple of objects, not a map.

for_each requires a set of strings, or a map.

This is so each member has a unique string ID, by which it can be identified to apply changes in future runs.

Ok then, how do I put maps of objects, so that I can use them with for_each? This also doesn’t seem to work:

variable "sites" {
    default = [
        "www.example1.com" = {
            protocol    = "https",
            hostname    = "www.example1.com",
            webhook     = "https://webhook.com/sgfe78sgf78sgf78sgf78sdf78s",
            foo         = "bar"
        },
        "www.example2.com" = {
            protocol    = "https",
            hostname    = "www.example2.com",
            webhook     = "https://webhook.com/as79d8fhad9fnaf9f98anf9anf9",
            foo         = "baz"
        }
    ]
}

To assign a map to a variable, use {} not []

Optionally, you could leave your default value as in your original post and do the following in your resource block:

resource "aws_something" "something_else" {
  for_each = { for index, site in var.sites: site.hostname => site }

  protocol    = each.value.protocol
  hostname    = each.key
  ...
}

In addition to the use of [] brackets vs {} brackets, you should also tell Terraform what type this is intended to be:

variable "sites" {
  type = map(object({
    protocol = string
    hostname = string
    webhook  = string
    foo      = string
  }))

  default = {
    "www.example1.com" = {
      protocol    = "https"
      hostname    = "www.example1.com"
      webhook     = "https://webhook.com/sgfe78sgf78sgf78sgf78sdf78s"
      foo         = "bar"
    }
    "www.example2.com" = {
      protocol    = "https"
      hostname    = "www.example2.com"
      webhook     = "https://webhook.com/as79d8fhad9fnaf9f98anf9anf9"
      foo         = "baz"
    }
  }
}

Then once you have the syntax correct (using the correct delimiters) Terraform will also know that it’s supposed to convert the top-level object to be a map instead, since {} is the syntax for constructing an object value, rather than a map value.

(You can read more about how map types and object types differ – and more generally, how collection and structural types differ – in under Type Constraints - Complex Types in the documentation, if you’re interested in more background information on this.)