Often, resources or data sources may produce values in a set or tuple type, which cannot be indexed directly. Since lists are indexable and ordered, converting to a list allows you to perform operations like element access, iteration with indices, or passing to resources that explicitly require a list input.
The tolist
function in Terraform takes a single argument and returns a list containing the same elements in order. It can accept a tuple, set, or list as input (not maps/objects). This function is especially useful when working with complex data types that may not support list operations directly.
For example, when a module output returns a tuple, tolist
ensures compatibility with functions or resources expecting a list. Lists in Terraform are homogeneous (all elements of the same type), whereas tuples can contain mixed types.
If a tuple contains mixed types, Terraform will attempt to coerce them to a single element type; if it can’t, the conversion fails.
locals {
example_tuple = [1, 2, 3]
example_list = tolist(local.example_tuple)
}
Terraform will often auto-convert between compatible collection types for you. Use tolist()
when you need to force list semantics (e.g., to satisfy a strict list(...)
type constraint or to make intent explicit) or to satisfy a strict type constraint.
When converting from a set, prefer sort(tolist(...))
if you require a stable, deterministic order across runs.
In this example, the variable availability_zones
is defined as a set of strings. Sets in Terraform are unordered collections, meaning their order is not guaranteed and they do not support indexing directly (e.g., var.availability_zones[0]
would fail).
variable "availability_zones" {
type = set(string)
default = toset(["us-east-1a", "us-east-1b", "us-east-1c"])
}
output "az_list" {
value = tolist(var.availability_zones)
}
By applying tolist(var.availability_zones)
, Terraform converts the set into a list, but the element order is undefined. If you need deterministic indexing, wrap it with sort(...)
:
locals {
az_list_sorted = sort(tolist(var.availability_zones))
}
output "first_az" {
value = local.az_list_sorted[0]
}
This is helpful when you need deterministic placement or when passing zones to a resource that requires a list input. Without sort
, the list order can vary between plans/applies even though membership stays the same.
Here, the aws_subnet.example[*].id
expression creates a tuple of subnet IDs, since multiple resources are generated with count
.
resource "aws_subnet" "example" {
count = 2
vpc_id = aws_vpc.example.id
cidr_block = cidrsubnet(aws_vpc.example.cidr_block, 8, count.index)
availability_zone = element(["us-east-1a", "us-east-1b"], count.index)
}
output "subnet_ids" {
value = tolist(aws_subnet.example[*].id)
}
Tuples behave like lists but are fixed-length and typed according to their elements. Some modules or outputs may require an actual list, not just a tuple, for compatibility. By wrapping the expression in tolist()
, Terraform explicitly converts the tuple into a list type.
In many situations, Terraform will auto-convert a tuple to a list for you. Keep tolist()
when you strictly require list(...)
(not a tuple). Avoid implying “list-only” operations like indexing — tuples can be indexed too.
Often, a module or data source will output security group IDs as a set. While sets are fine for uniqueness, AWS EC2 instances (via vpc_security_group_ids
) expect a list of security group IDs.
Terraform will usually auto-convert a set(string)
to a list(string)
, but the resulting order is undefined. Passing a set directly won’t typically cause a type mismatch error, but it can lead to noisy diffs due to unstable ordering. If you need deterministic behavior, convert and then sort.
Using tolist
converts the value to a list, but it does not guarantee order; prefer sort(tolist(...))
when stability matters.
module "sg" {
source = "./modules/security-groups"
vpc_id = aws_vpc.main.id
}
resource "aws_instance" "app" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
# Make ordering deterministic if the module returns a set(string):
vpc_security_group_ids = sort(tolist(module.sg.security_group_ids))
}
In this example, the module.sg.security_group_ids
output is (commonly) a set of strings. Providers like aws_instance
accept list(string)
. Auto-conversion from a set can produce unstable ordering and noisy diffs, so sort(tolist(...))
is recommended.
Wrapping it with sort(tolist(...))
converts it into a list and applies a stable, lexicographic order, which the EC2 instance accepts as input. This avoids plan/apply churn from element reordering and still lets you use list operations (like indexing) later if needed.
Note: If your module already outputs list(string)
, you can pass it directly (no tolist
needed). If you truly don’t care about order, tolist(...)
alone is fine, but remember that set→list ordering is not guaranteed across runs.
The tolist()
function converts its argument into a list. It is often used when Terraform produces a tuple (a fixed-length ordered sequence) or when you want to ensure a value is explicitly a list so it can be iterated over consistently.
We encourage you to explore how Spacelift makes it easy to work with Terraform. If you need help managing your Terraform infrastructure, building more complex workflows based on Terraform, and managing AWS credentials per run, instead of using a static pair on your local machine, Spacelift is a fantastic tool for this.
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.
Build more complex workflows based on Terraform using policy as code, programmatic configuration, context sharing, drift detection, resource visualization and many more.