Terraform Splat Expressions | Brainboard Blog

Terraform Splat Expressions

Chafik Belhaoues September 16, 2025
12 min read
Expert reviewed
Terraform Splat Expressions
Terraform Splat ExpressionsMaster Terraform splat expressions to simplify complex resource iterations and streamline your infrastructure as code workflowsDevOpsChafik Belhaoues2025-09-16T00:00:00.000Z12 minutesintermediateguidedevelopers, DevOps engineers, cloud architects

Your guide to simplified resource iteration

When you’re managing infrastructure as code with Terraform, you’ll often find yourself working with lists of resources or complex data structures. This is where splat expressions come into play, offering an elegant solution to extract specific values from collections without writing verbose loops. If you’ve ever struggled with iterating through multiple resources or extracting attributes from lists, splat expressions will revolutionize how you write your Terraform configurations.

What are splat expressions in Terraform?

Splat expressions are a concise syntax feature in Terraform that allows you to extract a list of values from a complex data structure. Think of them as a shorthand way to perform what would otherwise require a for expression. The splat operator, denoted by [*], automatically iterates through a list or set of objects and retrieves specific attributes from each element.

In essence, when you use a splat expression, you’re telling Terraform to reach into each item in your collection and pull out the same piece of information from all of them. This becomes incredibly powerful when you’re dealing with multiple instances of resources, data sources, or any iterable structure in your configuration.

Understanding the syntax and mechanics

The basic syntax of a splat expression follows a simple pattern that’s easy to remember once you understand its components. When you write something like var.list[*].attribute, you’re instructing Terraform to iterate through every element in var.list and extract the attribute field from each one. The result is always a list, even if your source collection contains only one element.

Let me walk you through the two primary forms of splat expressions. The first is the attribute-only splat, which looks like values[*].attr. This form works with lists and sets of objects, extracting a specific attribute from each object. The second form is the full splat, written as values[*], which is particularly useful when you want to work with the entire collection but need to ensure it’s treated as a list.

Consider this practical example that demonstrates both forms:

variable "servers" {
  type = list(object({
    name = string
    ip   = string
    port = number
  }))
  default = [
    { name = "web-1", ip = "10.0.1.1", port = 80 },
    { name = "web-2", ip = "10.0.1.2", port = 80 },
    { name = "web-3", ip = "10.0.1.3", port = 80 }
  ]
}

# Extract all IP addresses using splat
output "server_ips" {
  value = var.servers[*].ip
}

# Extract all names
output "server_names" {
  value = var.servers[*].name
}

Common use cases and practical applications

One of the most frequent scenarios where splat expressions shine is when working with multiple AWS EC2 instances or Azure VMs. Imagine you’ve created several instances using the count parameter, and now you need to extract all their public IP addresses for a load balancer configuration. Without splat expressions, you’d need to write complex loops or reference each instance individually.

Here’s a real-world example that showcases this pattern:

resource "aws_instance" "web_servers" {
  count         = 3
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name = "web-server-${count.index + 1}"
  }
}

# Using splat to get all instance IDs
output "instance_ids" {
  value = aws_instance.web_servers[*].id
}

# Using splat to get all public IPs
output "public_ips" {
  value = aws_instance.web_servers[*].public_ip
}

# Creating a security group rule for all instances
resource "aws_security_group_rule" "allow_ssh" {
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.main.id

  # Reference all instance IDs using splat
  description = "SSH access for instances: ${join(", ", aws_instance.web_servers[*].id)}"
}

Another powerful application involves working with data sources that return multiple results. When querying existing infrastructure, you often need to extract specific attributes from the results. Splat expressions make this extraction straightforward and readable.

Working with nested structures and complex scenarios

As your infrastructure grows more complex, you’ll encounter nested data structures where splat expressions truly demonstrate their versatility. You can chain splat operators to dive deep into nested structures, though you should be mindful of readability when doing so.

Let’s explore a more sophisticated example involving nested resources:

variable "environments" {
  type = list(object({
    name = string
    regions = list(object({
      name      = string
      instances = list(string)
    }))
  }))
}

# Extract all region names across all environments
locals {
  all_regions = flatten([
    for env in var.environments : env.regions[*].name
  ])

  # Alternative using nested splat (note: this won't work directly)
  # You need to combine splat with other expressions for complex nesting
  all_instances = flatten([
    for env in var.environments : [
      for region in env.regions : region.instances
    ]
  ])
}

When dealing with conditional resources (those created with count = 0 or count = 1), splat expressions handle empty lists gracefully. If a resource doesn’t exist, the splat expression returns an empty list rather than causing an error, making your configurations more robust.

Best practices and performance considerations

While splat expressions are powerful, using them effectively requires understanding their limitations and following best practices. First, remember that splat expressions always return lists, even when applied to single-element collections. This behavior is intentional and helps maintain consistency in your configurations.

When working with large datasets, be conscious of the performance implications. Although splat expressions are generally efficient, extracting attributes from thousands of objects can impact plan and apply times. In such cases, consider whether you truly need all the data or if you can filter it first.

Here are some guidelines I recommend following:

# Good: Clear and purposeful use of splat
output "instance_private_ips" {
  value = aws_instance.web[*].private_ip
}

# Better: Add description for clarity
output "instance_private_ips" {
  description = "List of private IP addresses for all web instances"
  value       = aws_instance.web[*].private_ip
}

# Avoid: Unnecessary nesting that reduces readability
output "complex_extraction" {
  value = var.data[*].level1[*].level2[*].attribute  # This won't work as expected
}

# Instead: Use for expressions for complex iterations
output "complex_extraction_better" {
  value = flatten([
    for item in var.data : [
      for l1 in item.level1 : [
        for l2 in l1.level2 : l2.attribute
      ]
    ]
  ])
}

Comparing splat expressions with for expressions

You might wonder when to use splat expressions versus for expressions. The rule of thumb is straightforward: use splat expressions for simple attribute extraction from lists, and use for expressions when you need filtering, transformation, or complex iterations.

Splat expressions excel at simplicity and readability when you’re performing straightforward extractions. However, for expressions offer more flexibility when you need conditional logic or want to transform the data while iterating. Here’s a comparison:

# Splat expression: Simple and clean
instance_ids = aws_instance.web[*].id

# Equivalent for expression: More verbose
instance_ids = [for instance in aws_instance.web : instance.id]

# When you need filtering, for expressions are necessary
running_instance_ids = [
  for instance in aws_instance.web : instance.id
  if instance.instance_state == "running"
]

Troubleshooting common issues

When working with splat expressions, you’ll occasionally encounter errors or unexpected behavior. One common mistake is trying to use splat on non-iterable values. If you attempt to apply a splat expression to a single object that isn’t wrapped in a list, Terraform will throw an error.

Another frequent issue occurs when dealing with null or undefined values within your collections. Splat expressions will include null values in the resulting list, which might not be what you expect. To handle this, you can combine splat expressions with compact() or other functions to clean up your data:

# Remove null values from splat result
clean_ips = compact(aws_instance.web[*].public_ip)

# Filter out empty strings
non_empty_names = [
  for name in var.resources[*].name : name
  if name != ""
]

Integration with Terraform modules and workspaces

Splat expressions become even more valuable when building reusable modules. They allow module consumers to easily extract information from module outputs without knowing the internal structure. This abstraction is crucial for maintaining clean interfaces between modules.

When working with Terraform workspaces, splat expressions help you manage resources across different environments efficiently. You can use them to aggregate information from workspace-specific resources without duplicating code:

module "vpc" {
  source = "./modules/vpc"
  count  = var.create_vpc ? 1 : 0

  cidr_block = var.vpc_cidr
}

# Safe extraction even if module count is 0
output "vpc_id" {
  value = try(module.vpc[0].vpc_id, null)
}

# Get all subnet IDs if VPC exists
output "subnet_ids" {
  value = module.vpc[*].subnet_ids
}

Conclusion

Splat expressions are an indispensable tool in your Terraform toolkit, offering a clean and efficient way to work with collections of resources. They simplify your code, improve readability, and reduce the likelihood of errors when extracting attributes from lists. By mastering splat expressions, you’ll write more maintainable infrastructure code and handle complex resource relationships with ease. Whether you’re managing a handful of instances or orchestrating enterprise-scale infrastructure, understanding and properly utilizing splat expressions will make your Terraform configurations more elegant and efficient.

FAQs

Can I use multiple splat expressions in a single statement?

While you can chain splat expressions in certain contexts, Terraform doesn’t support multiple splat operators in a single path like var.list[*].nested[*].attribute. Instead, you need to use flatten() with for expressions to achieve nested iteration. This limitation exists to maintain clarity and prevent ambiguous expressions that could be difficult to debug.

How do splat expressions handle empty lists or null values?

Splat expressions gracefully handle empty lists by returning an empty list as the result. When encountering null values within a collection, the splat expression includes them in the output list. If you need to filter out nulls, wrap your splat expression with the compact() function to remove null values from the resulting list.

What’s the performance impact of using splat expressions on large datasets?

Splat expressions are generally efficient, but when working with thousands of resources, they can impact Terraform’s plan and apply times. The performance impact is usually negligible for collections under 1000 elements. For larger datasets, consider whether you need all the data or if you can filter the collection first using for expressions with conditions.

Can I use splat expressions with map data types?

Splat expressions don’t work directly with maps since maps aren’t ordered collections. However, you can use the values() function to convert a map to a list of values, then apply a splat expression: values(var.my_map)[*].attribute. For more complex map operations, for expressions offer better flexibility and control.

How do splat expressions differ between Terraform 0.11 and newer versions?

In Terraform 0.11, the splat syntax was limited and only worked with list types. Modern Terraform versions (0.12+) introduced the generalized splat operator [*] which works with any collection type, including sets and tuples. The new syntax is more consistent and powerful, automatically handling single values by wrapping them in a list when necessary.

Chafik Belhaoues

Cloud Architect and Former SRE
LinkedIn

Chafik has more than 20 years of experience in the IT industry, including 12 years as a Senior Site Reliability Engineer (SRE) and 8 years as a Cloud Architect. He is the founder of Brainboard, focusing on cloud infrastructure and DevOps automation.

Back to Blog