Terraform, an Infrastructure as Code tool, allows the use of various data types for variables. Two commonly used types are lists and maps. Understanding when and how to use each can greatly enhance your Terraform configurations. Let's delve into their usage, benefits, and limitations, using a practical example.

Understanding Lists and Maps in Terraform

Lists in Terraform

A list is an ordered collection of values where each item can be accessed by its index. In Terraform, lists are declared using square brackets [].


variable "example_list" {
  type    = list(string)
  default = ["item1", "item2", "item3"]
}

Use Cases for Lists:

  • When the order of elements is important.
  • For a homogeneous collection of items (all items of the same type).
  • When you need to iterate over values with functions like count.index.

Maps in Terraform

A map is a collection of key-value pairs. Maps are useful when you want to associate each item in a collection with a unique key.


variable "example_map" {
  type = map(string)
  default = {
    key1 = "value1"
    key2 = "value2"
  }
}

Use Cases for Maps:

  • When you need to retrieve values based on keys.
  • For heterogeneous data types or when each item in the collection has additional attributes.
  • When manipulating specific elements without affecting the entire structure.

Practical Example: Azure SQL DB Metric Alerts

Consider the scenario of configuring Azure SQL DB metric alerts in Terraform. Initially, a list of objects was used:


variable "alert_metrics" {
  type = list(object({
    metric_type       = string
    metric_name       = string
    // Other attributes...
  }))
  // Default values...
}

Challenges with Lists

  • Direct Access: Accessing a specific alert configuration requires knowing its index, which is not intuitive.
  • Scalability: As the list grows, managing and referencing specific elements becomes cumbersome.
  • Iteration Complexity: Iterating over lists, especially with nested structures, can get complex.

Transitioning to Maps

To overcome these challenges, the structure was changed to a map:


variable "alert_metrics" {
  type = map(object({
    metric_type       = string
    metric_name       = string
    // Other attributes...
  }))
  default = {
    sql_instance_memory_percent = { /* ... */ }
    sql_instance_cpu_percent = { /* ... */ }
    // More metrics...
  }
}

Advantages of Using Maps:

  • Clarity: Each metric configuration is associated with a clear, meaningful key.
  • Ease of Access: Directly access or modify a metric configuration using its key.
  • Scalability: More manageable as the number of metrics grows.

Terraform Configuration with Maps

With the map approach, for_each can be used more effectively in resources:


resource "azurerm_monitor_metric_alert" "db_metrics_alert" {
  for_each = var.alert_metrics

  name                = "${each.value.metric_type} - ${var.database_name}"
  criteria {
    metric_name      = each.value.metric_name
    // Other configurations...
  }
  // Additional settings...
}

JSON Example

Terraform also supports JSON syntax, offering an alternative way to define configurations. Here’s how our map variable can be represented in JSON:


{
  "alert_metrics": {
    "sql_instance_memory_percent": {
      "metric_type": "SQL DB Instance Memory Usage",
      "metric_name": "sql_instance_memory_percent",
      // Other attributes...
    },
    "sql_instance_cpu_percent": {
      // Attributes...
    }
    // More metrics...
  }
}

Conclusion

Choosing between lists and maps in Terraform depends on the specific requirements of your infrastructure configuration. Maps offer more flexibility and direct access capabilities, making them suitable for complex structures like our Azure SQL DB metric alerts. As your Terraform configurations evolve, consider the scalability and manageability of your variables to select the most appropriate data type.