In Terraform, functions serve as building blocks for manipulating and transforming data within your configuration files. They add a programmability element to an otherwise declarative language.
When I first started with Terraform to write configuration to deploy infrastructure on AWS and GCP, I did not know how powerful Terraform is.
I wrote configuration code like an armature — repeated code blocks, hardcoded values, and bloated and inflexible infrastructure configuration code.
It took me a while to start using Terraform’s built-in functions, and everything changed. I learned to leverage Terraform functions in my configuration code. My terraform code got 10x more flexible, manageable, readable, scalable, and DRY.
Complete guide to using Terraform functions in your infrastructure code
The Terraform language includes several built-in functions you could call from within expressions to transform and combine values.
Note: The Terraform language does not support user-defined functions, and so only the functions built into the language are available for use.
The most commonly used Terraform functions
Terraform list functions
formatlist()
: formatlist produces a list of strings by formatting several other values according to a specification string.
It’s particularly useful when you want to create a formatted string by combining elements from a list, and you want to apply a consistent formatting pattern to those.
elements.locals {
fruits = ["apple", "banana", "orange"]
formatted_list = formatlist("I like %s.", local.fruits...)
}
output "formatted_list_output" {
value = local.formatted_list
}
-> formatted_list_output = [ "I like apple.", "I like banana.", "I like orange.",]
flatten()
: flatten function transforms a nested list or nested tuple into a flat list.
This can be useful when you have a list of lists or a list of tuples, and you want to combine all the elements into a single flat list.
locals {
nested_list = [
["item1", "item2"],
["item3", "item4", "item5"],
["item6"],
]
flat_list = flatten(local.nested_list)
}
output "flat_list_output" {
value = local.flat_list
}
concat()
: The concat function concatenates two or more lists or strings together.
It’s commonly used when you want to combine multiple lists or strings into a single list or string.variable "list1" {
default = ["a", "b", "c"]
}
variable "list2" {
default = ["d", "e", "f"]
}
variable "combined_list" {
value = concat(var.list1, var.list2)
}
element():
It retrieves an element from a list at a specified index.
locals {
weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
third_weekday = element(local.weekdays, 2)
}
slice():
It extracts a sublist from a list, specifying a starting index and an optional ending index.
locals {
numbers = [1, 2, 3, 4, 5]
selected_numbers = slice(local.numbers, 1, 3)
}
compact
takes a list of strings and returns a new list with any null or empty string elements removed.
> compact(["apple", "", "mango", null, "grape"])
[
"apple",
"mango",
"grape",
]
distinct
removes duplicates from a list, retaining the first occurrence of each value while preserving their relative ordering.
> distinct(["a", "b", "a", "c", "d", "b"])
[
"a",
"b",
"c",
"d",
]
Terraform map functions
merge()
: The merge function in Terraform is used to combine multiple maps into a single map.
merge(map1, map2, map3, …) -> merged_map
This function is useful when you want to aggregate configurations from multiple sources. It is mostly used to create flexible and modular configurations.
variable "base_config" {
type = map(string)
default = { key1 = "value1", key2 = "value2" }
}
variable "additional_config" {
type = map(string)
default = { key2 = "new_value2", key3 = "value3" }
}
output "extended_config" {
value = merge(var.base_config, var.additional_config)
}
lookup()
: The lookup function in Terraform is used to safely access the value of a given key in a map. It returns the value associated with the specified key if it exists or a default value if the key does not exist.
This function is particularly useful in scenarios where you want to dynamically fetch information from a map, especially when dealing with variable configurations and data structures.
variable "environment_vars" {
type = map(string)
default = {
dev = "dev-database-url"
prod = "prod-database-url"
}
}
variable "current_environment" {
type = string
default = "prod"
}
output "database_url" {
value = lookup(var.environment_vars, var.current_environment, "default-database-url")
}
In this example, the lookup function fetches the database URL based on the current environment. If the environment is not found in the map, it defaults to a predefined value.
zipmap()
: zipmap function in Terraform is used to create a map by pairing elements from two separate lists
This function is particularly useful in scenarios where you have two lists that correspond to each other, and you want to construct a map associating elements from one list with elements from the other
variable "resource_names" {
type = list(string)
default = ["web", "db", "app"]
}
resource "aws_instance" "example" {
count = length(var.resource_names)
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = zipmap(["Name"], [var.resource_names[count.index]])
}
In this scenario, the zipmap
is used to dynamically assign names to AWS instances based on the elements in the resource_names list.
variable "config_keys" {
type = list(string)
default = ["max_connections", "timeout", "retry_limit"]
}
variable "config_values" {
type = list(number)
default = [100, 30, 5]
}
output "application_config" {
value = zipmap(var.config_keys, var.config_values)
}
Here zipmap
is used for configuring application parameters.
length()
: Returns the length of a list, string, or map.keys()
: Returns a list of keys from a map.values()
: Returns a list of values from a map.
variable "tags" {
type = map(string)
default = {
"Name" = "my-instance"
"Env" = "production"
"App" = "web"
}
}
locals {
tag_count = length(var.tags)
tag_keys = keys(var.tags)
tag_values = values(var.tags)
}
map():
The map function transforms the values of a map using expressions.
locals {
prices = {
apple = 0.5
banana = 0.3
orange = 0.7
}
discounted_prices = map(
for fruit, price in local.prices : fruit => price * 0.9
)
}
Terraform string Functions
format():
format function allows you to create formatted strings by substituting values into placeholders.
In the below example, the format function is used to generate unique names for AWS instances dynamically.
variable "environment" {
type = string
default = "dev"
}
resource "aws_instance" "example" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = format("instance-%s-%02d", var.environment, count.index + 1)
}
}
replace():
Replaces occurrences of a specified substring in a given string with another substring.
Syntax replace(string, search, replace)
variable "file_paths" {
type = list(string)
default = ["C:\\Users\\user1\\documents\\file1.txt", "D:\\Data\\file2.txt", "E:\\Projects\\file3.txt"]
}
output "standardized_file_paths" {
value = [for path in var.file_paths : replace(path, "\\", "/")]
}
# We used replace to standardize the file path
split()
: It splits a string into a list of substrings based on a specified delimiter.
This function is useful in scenarios where you need to extract or manipulate parts of a string within your Terraform configurations.
variable "user_emails" {
type = list(string)
default = ["user1@example.com", "user2@example.com", "user3@example.com"]
}
resource "aws_instance" "user_instances" {
count = length(var.user_emails)
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = replace(split("@", var.user_emails[count.index])[0], ".", "-")
}
}
In the above scenario, we have three user email addresses as input, and we’ll use the name part of each email address to create AWS EC2 instances with dynamically generated names.
We used the replace function to replace dots with hyphens in the names.
join():
It concatenates a list of strings into a single string, separated by a delimiter.
locals {
join_string = join(",", ["a", "b", "c"])
}
output "join_string" {
value = local.join_string
}
# output will be -> “a, b, c”.

title
Converts the first letter of each word in the given string to uppercase.upper
converts all letters in the given string to uppercase.lower
converts all letters in the given string to lowercase.
> title("hello world")
Hello World
> upper("Hello World")
HELLO WORLD
> lower("HELLO WORLD")
hello world
chomp
trims newline characters at the end of a string.
It is very useful when you are dealing with multiline strings or reading content from a file using file
function, and want to remove unwanted newline (‘\n’) characters
# reading multiine string
> chomp("This is a multiline string\nwith unnecessary line breaks\n")
This is a multiline string with unnecessary line breaks# reading file
variable "file_path" {
type = string
default = "path/to/file.txt"
}
output "file_content" {
value = chomp(file(var.file_path))
}
can
andtry
functions are used to handle situations where a variable may not exist or may be set tonull
These functions help make your configurations more robust by providing a way to handle potential errors or missing values.
can
function checks whether a variable or attribute exists and is non-null.
Returns true
if the variable exists and is non-null.
Returns false
if the variable does not exist or is explicitly set to null
variable "example_variable" {
type = string
}locals {
variable_exists = can(var.example_variable)
}
try
function is used to handle situations where a value might be null. It provides a default value if the specified key or attribute is null.
Returns the value of the variable if it’s non-null.
Returns the default value if the variable is explicitly set to null
.
variable "example_variable" {
type = string
default = null
}locals {
safe_variable = try(var.example_variable, "default_value")
}
The Terraform file functions
abspath
function converts a relative file path to an absolute path.
locals {
relative_path = "main.tf"
absolute_path = abspath(local.relative_path)
}
output "absolute_path_output" {
value = local.absolute_path
}
# absolute_path_output = "/Users/akhileshmishra3/terraform-blogs/main.tf"
basename
function extracts the filename (the last component) from a given path.
locals {
file_path = "/Users/akhileshmishra3/terraform-blogs/main.tf"
file_name = basename(local.file_path)
}
output "file_name_output" {
value = local.file_name
}
# file_name_output = "main.tf"
fileexists
function checks whether a file exists at the specified path.
variable "file_to_check" {
type = string
default = "files/config.txt"
}
locals {
file_exists = fileexists(var.file_to_check)
}
output "file_exists_output" {
value = local.file_exists
}
# file_exists_output = false
file
Read the contents of a file at the given path, and return them as a string.
locals {
file_content = file("README.MD")
}
output "file_content_output" {
value = local.file_content
}
# file_content_output = <<-EOT
This file is used for
Terraform functions examples
EOT
This function only works with files that exist before Terraform runs, and It can’t be used with files created dynamically during a Terraform operation.
file
is often used to load configuration or data from files into your Terraform configurations.
Suppose we have a fileconfig.txt
with the following content
# config.txt
region = "us-west-2"
instance_type = "t2.micro"
ami = "ami-0123456789abcdef0"
You can use the file
function to read the contents of this file in your Terraform configuration.
# main.tf
# Read the contents of the 'config.txt' file
variable "config_content" {
type = string
default = file("${path.module}/config.txt")
}
# Parse the configuration content as HCL
locals {
parsed_config = yamldecode(var.config_content)
}
# Use the parsed configuration in your resources
resource "aws_instance" "example" {
ami = local.parsed_config.ami
instance_type = local.parsed_config.instance_type
region = local.parsed_config.region
}
path.module
is a built-in variable that represents the directory path where the current Terraform configuration file is located.
Encoding/Decoding Terraform functions
yamlencode
encode a given value to a string using YAML 1.2 block syntax.
The above example shows how you can use yamlencode
Terraform function.
base64encode
encode a string as a base64-encoded string.
This function is useful when dealing with sensitive information like passwords or access keys.
variable "db_password" {
type = string
sensitive = true
}
resource "kubernetes_secret" "example" {
metadata {
name = "db-credentials"
}
data = {
password = base64encode(var.db_password)
}}
It is often used to encode the custom startup scripts when configuring instances in cloud providers.
variable "user_data" {
type = string
default = "#!/bin/bash\napt-get update\napt-get install -y nginx"
}
resource "aws_instance" "example" {
ami = "ami-12345678"
instance_type = "t2.micro"
user_data = base64encode(var.user_data)
}