Welcome to Day 11 of our 30 Days of AWS Terraform series! After structuring our projects and mastering Meta Arguments, we are now diving into a core concept that enables highly efficient and reusable infrastructure code: Terraform Functions. If you’re new to programming concepts, a function is simply a tool that makes life easier by allowing you to reuse code repeatedly. Instead of writing the same four lines of code 10 times, you wrap them once inside a function (like sum) and call that function whenever needed, passing different inputs each time.
Crucially, Terraform is not a full-fledged programming language; it is the HashiCorp Configuration Language (HCL). Because of this, you cannot create your own custom functions in Terraform; you can only use the rich library of inbuilt …
Welcome to Day 11 of our 30 Days of AWS Terraform series! After structuring our projects and mastering Meta Arguments, we are now diving into a core concept that enables highly efficient and reusable infrastructure code: Terraform Functions. If you’re new to programming concepts, a function is simply a tool that makes life easier by allowing you to reuse code repeatedly. Instead of writing the same four lines of code 10 times, you wrap them once inside a function (like sum) and call that function whenever needed, passing different inputs each time.
Crucially, Terraform is not a full-fledged programming language; it is the HashiCorp Configuration Language (HCL). Because of this, you cannot create your own custom functions in Terraform; you can only use the rich library of inbuilt functions provided by Terraform. These functions are categorized into types such as string, numeric, collection, type conversion, and more. Today, we explore some of the most practical and frequently used functions.
1. String Manipulation Functions
String functions are used to clean, format, and modify text inputs, ensuring they meet specific infrastructure requirements (like S3 bucket naming conventions).
Lower, Upper, Trim, and Replace
• lower() and upper(): These convert a string to all lowercase or all uppercase characters, respectively. ◦ Example: lower("HELLO WORLD") returns "hello world". • trim(): Removes specified characters from a string. ◦ Example: trim(" AWSTF ", "f") removes the character ‘f’ from the string, but leaves the spaces if space is not specified as the character to trim. • replace(): Substitutes an occurrence of a character or substring with a new character. ◦ Example: replace("hello world", " ", "-") returns "hello-world". A powerful technique is nesting functions to perform multiple operations in one line. For instance, to ensure a project name is lowercase and uses hyphens instead of spaces:
Code Example (Nesting lower() and replace()):
locals {
# First, replace spaces with hyphens; then, convert the result to lowercase.
formatted_project_name = lower(
replace(var.project_name, " ", "-")
)
}
output "formatted_name" {
value = local.formatted_project_name
}
If var.project_name is "Project Alpha Resource", the output becomes "project-alpha-resource". This is crucial for handling naming constraints (like those for S3 buckets, which must be lowercase and cannot contain spaces).
2. Collection Functions: Managing Groups
Collection functions help manage groups of data (like lists and maps) for tasks such as calculating length or combining inputs. Length, Concat, and Merge • length(): Calculates the number of elements in a list. •** concat(): Combines two or more lists or tuples into a single list. • **merge(): Combines two or more maps (key-value pairs) into a single map. If duplicate keys exist, the last provided map takes precedence. The merge() function is excellent for combining default tags and environment-specific tags:
Code Example (Merging Maps):
# variables.tf contains var.default_tags and var.environment_tags (both are maps)
locals {
new_tag = merge(
var.default_tags,
var.environment_tags
)
}
resource "aws_s3_bucket" "first_s3" {
# ... bucket configuration ...
tags = local.new_tag # This applies all merged tags
}
This single tag block now contains all key-value pairs from both input maps.
3. Lookup Function: Dynamic Environment Selection
The lookup() function is vital for selecting configuration values based on an input key, often used to determine environment-specific settings. The Structure: The lookup function requires three arguments: lookup(map, key, default). 1. Map: The entire collection of data (e.g., instance sizes for Dev, Staging, Prod). 2. Key: The value you are looking up (e.g., the current environment name). 3. Default: A fallback value if the key is not found (e.g., t2.micro). Code Example (Instance Sizing):
variable "instance_sizes" {
type = map(string)
default = {
dev = "t2.micro"
staging = "t3.small"
prod = "t3.large"
}
}
variable "environment" {
default = "prod"
}
locals {
instance_size = lookup(
var.instance_sizes,
var.environment,
"t2.micro" # Default value if key is missing
)
}
If var.environment is set to "prod", the lookup function returns "t3.large". If it is set to an undefined environment like "production", it returns the default value, "t2.micro".
4. Iteration and Splitting Data
split() and for Expressions When inputs are provided as a single comma-separated string, you must convert them into a list for iteration. The split() function takes a separator (e.g., comma) and a string, returning a list of substrings. The for expression (different from the for_each meta argument) then allows you to iterate through that list to create a structured collection of output values, such as security group rules. Code Example (Splitting String into List and Iterating):
variable "allowed_ports" {
default = "80,443,8080" # This is a single string
}
locals {
port_list = split(",", var.allowed_ports) # Output: ["80", "443", "8080"]
sg_rules = {
for port in local.port_list : port => {
name = "port-${port}"
description = "Allow traffic on port ${port}"
}
}
}
# The 'sg_rules' local is now a map of rules, structured for resource definition.
This process converts a simple string into a complex map of security group rules, demonstrating how functions and expressions combine for powerful data manipulation.
Mastering these functions is the key to writing concise, repeatable, and maintainable Terraform configurations. Thanks @piyushsachdeva