Best Practice: Maps vs Lists for input variables

Hey everyone,

I have a simple question regarding best practices of using map vs list as input variables for terraform modules.

Consider this example which uses list(object({})) for input variables

paths = [
  {
    id      = "my_id1"
    path    = "/path1"
  },
  {  
    id      = "my_id2"
    path    = "/path2"
  }
]

Now consider the same example which uses map(object({})) for input variables

paths = {
  id1 = {
    id      = "my_id1"
    path    = "/path1"
  }
  id2 = {
    id      = "my_id2"
    path    = "/path2"
  }
}

I’ve used the first approach using a list of objects in a few modules, as the key in the approach using a map of objects is most of the time not necessary for anything apart from iterating over the keys, which makes the approach using lists often easier to read (in my opinion). I know you can also assign the key of each map to a specific input argument, but I this makes the usage less intuitive as it’s not clear how the key is used within the module.

In most (official) Terraform modules I’ve seen, almost always the approach using map(object({})) seems to be used, even though the key of the map remains unused.

Is this all about personal preference or is there any best practice or reason why using maps instead of lists is so popular and used most of the time?

I think the first concern is always about the characteristics of the data type in question. For example, if you consider the order of the elements to be significant then you must use a list, because that’s the only collection type that preserves element order.

Beyond that are secondary considerations. For example, it’s common to use a map of objects as input if the module needs to be able to “talk about” the declared objects in its output values: if you have an input variable declaring a bunch of objects that should exist then there’s often an output value reflecting back the results of those declarations – what id each one was assigned by the remote system, for example – in which case using a map as input and a map with the same keys as output makes it easier for the user of the module to make use of those results individually in other parts of the configuration.

When nothing is forcing or strongly encouraging a particular collection type, it becomes a tradeoff of usability and maintainability. Using a map might give you more freedom to evolve the module in future in ways that require correlating input objects with output objects, but using a set of objects might just seem subjectively more ergonomic. At that point you need to decide what to prioritize if the different concerns are suggesting conflicting approaches.

Ultimately the choice of collection type gives the user of the module a clue about how future changes to the collection are likely to be interpreted:

  • Using a list suggests that the caller must carefully decide what order to write the elements in, and that reordering the elements later will cause that reordering to be reflected in the remote system too.
  • Using a map suggests that the objects are not considered to be in any particular order but that changing the value assigned to a particular key will be understood as updating an existing object, while adding or removing elements is equivalent to creating or deleting remote objects respectively.
  • Using a set suggests that the objects are not ordered but they have no identity beyond their direct value, and so updating an existing element is not possible and any change will be some combination of creating and deleting objects.

Modules that use a collection type that doesn’t match well with the remote system design (in the ways I’ve described above) tend to be brittle and hard to reconfigure over time as requirements change, because changes to the value assigned to the variable cause surprising and potentially-disruptive changes to the remote objects.

2 Likes

Thank you very much for this detailed and comprehensive response!

I was aware about the preserved element order in lists which just seemed like another advantage, but it makes sense that a list suggests to the caller that the order of element is important and reordering may be reflected in the remote system. Using a map for input and another map for outputs with the same keys is another good point which I haven’t thought about yet.

I think this pretty much answers my question and I can see why using a map of objects may be preferred in a lot of use-cases. I will consider these arguments in future modules.