How to Convert a List to a String in Terraform
Terraform often requires converting lists to strings for various purposes like building command-line arguments, creating comma-separated values for tags, or formatting output for external tools. While Terraform works primarily with structured data types, many resources and outputs need string representations of list data.
Understanding the different functions and techniques for list-to-string conversion helps you handle diverse data transformation scenarios.
TLDR: Use join(separator, list) to convert a list to a string with elements separated by a delimiter. For comma-separated values, use join(",", list). For JSON representation, use jsonencode(list). For more complex formatting, combine join with other string functions. The join function is the most common and straightforward method for list-to-string conversion in Terraform.
Basic List to String With join()
The join() function is the primary way to convert a list to a string:
locals {
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
# Convert to comma-separated string
az_string = join(",", local.availability_zones)
# Result: "us-east-1a,us-east-1b,us-east-1c"
}
output "availability_zones_string" {
value = local.az_string
}
The first argument is the separator, and the second is the list.
Different Separators
Use different separators depending on your needs:
locals {
servers = ["web-1", "web-2", "api-1"]
# Comma-separated
comma_separated = join(",", local.servers)
# Result: "web-1,web-2,api-1"
# Space-separated
space_separated = join(" ", local.servers)
# Result: "web-1 web-2 api-1"
# Newline-separated
newline_separated = join("\n", local.servers)
# Result: "web-1\nweb-2\napi-1"
# Pipe-separated
pipe_separated = join("|", local.servers)
# Result: "web-1|web-2|api-1"
# No separator (concatenate)
concatenated = join("", local.servers)
# Result: "web-1web-2api-1"
}
Converting for Resource Tags
A common use case is creating tag strings from lists:
locals {
environment_list = ["production", "web", "critical"]
# Convert to comma-separated string for tags
environment_tags = join(",", local.environment_list)
}
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = "t3.medium"
tags = {
Name = "app-server"
Environments = local.environment_tags # "production,web,critical"
}
}
Creating Command-Line Arguments
Build command-line argument strings from lists:
variable "allowed_ips" {
type = list(string)
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
locals {
# Create space-separated IP list for command
ip_args = join(" ", [
for ip in var.allowed_ips : "--allow-ip=${ip}"
])
# Result: "--allow-ip=10.0.1.0/24 --allow-ip=10.0.2.0/24 --allow-ip=10.0.3.0/24"
}
resource "null_resource" "configure" {
provisioner "local-exec" {
command = "configure-firewall ${local.ip_args}"
}
}
JSON String Representation
For JSON format, use jsonencode():
locals {
security_groups = ["sg-123", "sg-456", "sg-789"]
# Convert to JSON array string
sg_json = jsonencode(local.security_groups)
# Result: "[\"sg-123\",\"sg-456\",\"sg-789\"]"
}
resource "aws_ssm_parameter" "security_groups" {
name = "/app/security-groups"
type = "String"
value = local.sg_json
}
Formatting With Custom Prefixes and Suffixes
Add formatting around each element before joining:
locals {
bucket_names = ["logs", "backups", "archives"]
# Add ARN prefix to each bucket name
bucket_arns = join(",", [
for name in local.bucket_names :
"arn:aws:s3:::${name}"
])
# Result: "arn:aws:s3:::logs,arn:aws:s3:::backups,arn:aws:s3:::archives"
}
Quoted CSV Format
Create a properly quoted CSV string:
locals {
usernames = ["alice", "bob", "charlie"]
# Create quoted CSV
quoted_csv = join(",", [
for user in local.usernames :
"\"${user}\""
])
# Result: "\"alice\",\"bob\",\"charlie\""
}
Converting List of Objects
When working with lists of objects, extract specific fields first:
variable "instances" {
type = list(object({
name = string
ip = string
}))
default = [
{ name = "web-1", ip = "10.0.1.10" },
{ name = "web-2", ip = "10.0.1.20" },
{ name = "api-1", ip = "10.0.2.10" }
]
}
locals {
# Extract IP addresses and convert to string
ip_list = join(",", [
for instance in var.instances : instance.ip
])
# Result: "10.0.1.10,10.0.1.20,10.0.2.10"
# Create name=ip pairs
instance_pairs = join(" ", [
for instance in var.instances :
"${instance.name}=${instance.ip}"
])
# Result: "web-1=10.0.1.10 web-2=10.0.1.20 api-1=10.0.2.10"
}
Creating SQL IN Clause
Format a list for SQL queries:
locals {
user_ids = [123, 456, 789]
# Create SQL IN clause
sql_in_clause = "IN (${join(",", local.user_ids)})"
# Result: "IN (123,456,789)"
# For string values, add quotes
usernames = ["alice", "bob", "charlie"]
sql_in_usernames = "IN (${join(",", [
for user in local.usernames : "'${user}'"
])})"
# Result: "IN ('alice','bob','charlie')"
}
Environment Variable Format
Create environment variable strings:
variable "env_vars" {
type = map(string)
default = {
DATABASE_URL = "postgresql://localhost/mydb"
API_KEY = "secret-key"
DEBUG = "true"
}
}
locals {
# Convert to KEY=VALUE format, joined by spaces
env_string = join(" ", [
for key, value in var.env_vars :
"${key}=${value}"
])
# Result: "DATABASE_URL=postgresql://localhost/mydb API_KEY=secret-key DEBUG=true"
# Or newline-separated for .env file format
env_file = join("\n", [
for key, value in var.env_vars :
"${key}=${value}"
])
}
Converting Nested Lists
Flatten and convert nested lists:
locals {
subnets_by_az = {
"us-east-1a" = ["subnet-111", "subnet-222"]
"us-east-1b" = ["subnet-333", "subnet-444"]
"us-east-1c" = ["subnet-555", "subnet-666"]
}
# Flatten to single list, then convert to string
all_subnets = join(",", flatten([
for az, subnets in local.subnets_by_az : subnets
]))
# Result: "subnet-111,subnet-222,subnet-333,subnet-444,subnet-555,subnet-666"
}
Empty List Handling
Handle empty lists gracefully:
variable "tags" {
type = list(string)
default = []
}
locals {
# join returns empty string for empty list
tags_string = join(",", var.tags)
# Result: "" (empty string)
# Provide a default value for empty lists
tags_with_default = length(var.tags) > 0 ? join(",", var.tags) : "untagged"
}
Formatting for URL Query Parameters
Create URL query parameter strings:
variable "filters" {
type = map(string)
default = {
status = "active"
region = "us-east-1"
type = "web"
}
}
locals {
# Create URL query string
query_params = join("&", [
for key, value in var.filters :
"${key}=${urlencode(value)}"
])
# Result: "status=active®ion=us-east-1&type=web"
full_url = "https://api.example.com/instances?${local.query_params}"
}
Converting for CloudFormation Parameters
Format list values for CloudFormation:
locals {
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
# CloudFormation expects comma-delimited strings
az_parameter = join(",", local.availability_zones)
}
resource "aws_cloudformation_stack" "vpc" {
name = "vpc-stack"
parameters = {
AvailabilityZones = local.az_parameter
}
template_body = file("${path.module}/vpc-template.yaml")
}
Creating Terraform Output
Format lists for readable output:
locals {
instance_ids = ["i-123", "i-456", "i-789"]
# Pretty formatted output
formatted_instances = join("\n - ", concat([""], local.instance_ids))
# Result: "\n - i-123\n - i-456\n - i-789"
}
output "instance_list" {
value = "Created instances:${local.formatted_instances}"
description = "List of created instance IDs"
}
The output appears as:
Created instances:
- i-123
- i-456
- i-789
Combining Multiple Lists
Join multiple lists before converting to string:
locals {
public_ips = ["1.2.3.4", "5.6.7.8"]
private_ips = ["10.0.1.10", "10.0.1.20"]
# Combine and convert
all_ips = join(",", concat(local.public_ips, local.private_ips))
# Result: "1.2.3.4,5.6.7.8,10.0.1.10,10.0.1.20"
}
Using format() for Complex Strings
For more control, use format():
locals {
servers = ["web-1", "web-2", "api-1"]
# Create formatted string with brackets
formatted = format("[%s]", join(", ", local.servers))
# Result: "[web-1, web-2, api-1]"
# Create numbered list
numbered = join("\n", [
for i, server in local.servers :
format("%d. %s", i + 1, server)
])
# Result: "1. web-1\n2. web-2\n3. api-1"
}
Type Conversion Before Joining
Convert non-string elements before joining:
locals {
port_numbers = [80, 443, 8080]
# Convert numbers to strings, then join
ports_string = join(",", [
for port in local.port_numbers : tostring(port)
])
# Result: "80,443,8080"
# Also works with booleans
flags = [true, false, true]
flags_string = join(",", [
for flag in local.flags : tostring(flag)
])
# Result: "true,false,true"
}
Conditional List to String
Convert lists to strings based on conditions:
variable "environment" {
type = string
}
locals {
dev_servers = ["dev-1", "dev-2"]
prod_servers = ["prod-1", "prod-2", "prod-3"]
active_servers = var.environment == "production" ? local.prod_servers : local.dev_servers
servers_string = join(",", local.active_servers)
}
The join() function is the primary tool for converting lists to strings in Terraform. Choose your separator based on the target format - commas for CSV, spaces for command arguments, newlines for file formats, or no separator for concatenation. For structured formats like JSON, use jsonencode(). Combine join with for expressions to add formatting or transformations before conversion.
Found an issue?