Working with multiple resources in Terraform often calls for clean, organized, and flexible configurations. Grouping related data in a structured way helps manage repeating sets of values, like server definitions or security rules.
In this article, we’ll cover the Terraform list of object constructs and how to use it in different scenarios.
A Terraform list of objects is a collection data type that allows you to group related sets of values (objects) into an ordered sequence (list). Each object in the list can contain multiple named attributes, allowing you to model complex infrastructure configurations in a clean and scalable way.
This is useful when managing resources with a similar structure but varying values. Lists of objects are especially helpful for defining multiple similar resources with different parameters in modules or resource blocks.
In this scenario, we’re provisioning multiple DNS records in AWS Route 53 using a single aws_route53_record resource. The input variable dns_records holds a list of objects, each representing a DNS entry with the following fields:
name: the subdomain (e.g.,www,api)type: the DNS record type (e.g.,A,CNAME)ttl: the time-to-live valuerecords: a list of IPs or domains the record points to
variable "dns_records" {
type = list(object({
name = string
type = string
ttl = number
records = list(string)
}))
default = [
{
name = "www"
type = "A"
ttl = 300
records = ["192.0.2.1"]
},
{
name = "api"
type = "CNAME"
ttl = 300
records = ["example.com"]
}
]
}
resource "aws_route53_record" "this" {
for_each = { for record in var.dns_records : record.name => record }
zone_id = "Z3P5QSUBK4POTI" # Replace with your actual hosted zone ID
name = "${each.key}.example.com"
type = each.value.type
ttl = each.value.ttl
records = each.value.records
}We then use for_each to iterate over this list. Each object is keyed by its name so Terraform can manage each DNS record distinctly. For example, www.example.com points to an IP address, and api.example.com is a CNAME pointing to another domain.
This example is perfect for managing a growing number of DNS entries without duplicating code. As your app evolves and new services are added (e.g., auth, blog, docs), you can simply update the dns_records list without touching the resource definition itself.
Imagine you’re working for a company that needs to spin up a set of EC2 instances in AWS to serve different purposes — like a web server, a database server, and a logging server. Each instance has different instance types, tags, and AMIs, but they all live in the same VPC and availability zone.
Traditionally, you might write separate aws_instance blocks for each server, repeating a lot of configuration. But using a list of objects makes it clean, scalable, and DRY (Don’t Repeat Yourself).
variable "ec2_instances" {
description = "List of EC2 instances to create"
type = list(object({
name = string
instance_type = string
ami = string
}))
default = [
{
name = "web-server"
instance_type = "t2.micro"
ami = "ami-0abcdef1234567890" # Placeholder AMI
},
{
name = "db-server"
instance_type = "t3.medium"
ami = "ami-0abcdef1234567890"
},
{
name = "log-server"
instance_type = "t3.small"
ami = "ami-0abcdef1234567890"
}
]
}
resource "aws_instance" "servers" {
for_each = { for inst in var.ec2_instances : inst.name => inst }
ami = each.value.ami
instance_type = each.value.instance_type
tags = {
Name = each.value.name
Role = each.value.name
}
}This configuration starts by defining a variable called ec2_instances, which holds a list of objects. Each object represents one EC2 instance and defines its name, instance_type, and ami.
Instead of writing three separate aws_instance resource blocks, we use for_each with a derived map where each object is keyed by its name. This ensures that each instance is uniquely identified and can be created or destroyed independently.
Within the aws_instance block, each.value gives us access to the current object’s data, its AMI, instance type, and name. The tags section uses this data to label the instance appropriately.
Terraform’s list of objects construct helps manage complex infrastructure definitions more effectively by:
- Enabling parametrization of resources
- Supporting reusability through modules
- Reducing code duplication
- Making configurations clearer and easier to maintain
Terraform is really powerful, but to achieve an end-to-end secure GitOps approach, you need to use a product that can run your Terraform workflows. Spacelift takes managing Terraform to the next level by giving you access to a powerful CI/CD workflow and unlocking features such as:
- Policies (based on Open Policy Agent)
- Multi-IaC workflows
- Self-service infrastructure
- Integrations with any third-party tools
If you want to learn more about Spacelift, create a free account today or book a demo with one of our engineers.
Note: New versions of Terraform are placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.
Automate Terraform deployments with Spacelift
Automate your infrastructure provisioning, and build more complex workflows based on Terraform using policy as code, programmatic configuration, context sharing, drift detection, resource visualization, and many more.
