Why is Terraform ordering my input variable alphabetically?

Viewed 41

I have the following input variable that is being passed into a module:

    environments = {
      preprod = ["test"]
      qat     = ["test"]
      prod    = ["test"]
      alpha   = ["test"]
    }

And I am outputting that value for debugging purposes from the module and the output is shown below:

Changes to Outputs:
  + test = {
      + alpha   = [
          + "test",
        ]
      + preprod = [
          + "test",
        ]
      + prod    = [
          + "test",
        ]
      + qat     = [
          + "test",
        ]
    }

The ordering is important because the module is performing dynamic blocks but it needs to be in a particular order - has anyone seen this behavior before?

I'm not transforming the input variable in any way, in this instant, it is being passed to the module and then output. In the module, a for_each loop is being used but as you can see the ordering is not correct.

1 Answers

Map and object types in Terraform are not order-preserving data types. They only "remember" which keys/attributes are present, and not which order they were written when you constructed the object. (Indeed, some map or object values are not constructed using the { ... } syntax and so there isn't a clear "order they were written" in that case.)

If you need to describe a sequence of values where the order is significant then the appropriate type kinds are list and tuple.

For example, you could declare this input variable as being a list of objects:

variable "environments" {
  type = list(object({
    name      = string
    something = set(string)
  }))
}

(I used "something" above because it isn't clear from your example what those ["test"] expressions represent.)

In the calling module block it would then be written this way:

  environments = [
    {
      name      = "preprod"
      something = ["test"]
    },
    {
      name      = "qat"
      something = ["test"]
    },
    {
      name      = "prod"
      something = ["test"]
    },
    {
      name      = "alpha"
      something = ["test"]
    },
  ]

If you need to look the environments up by their names inside your module then you can still construct a mapping in a local value:

locals {
  environments = tomap({
    for e in var.environments : e.name => e
  })
}

With both the variable block and the locals block I showed above you can refer to var.environments to access the ordered list of environments, and you can refer to local.environments to look up a particular environment by its name.

Related