Skip to main content

Command Palette

Search for a command to run...

Day 11 — Terraform Functions Part 1

Updated
12 min read
Day 11 — Terraform Functions Part 1

Terraform Functions

Welcome to Day 11! Today marks an exciting milestone in our Terraform journey. We're diving into Terraform functions—powerful built-in tools that make your infrastructure code cleaner, smarter, and more maintainable.

This topic is extensive, so I've split it into two parts. Today, we'll cover the most commonly used functions that you'll work with daily. Tomorrow, we'll explore the remaining functions to complete your toolkit.

If you've been following this series from Day 1, you already have a solid foundation. If you're just joining, I recommend starting from Day 0, which explains how to participate in this challenge and be eligible for the end-of-series rewards.

Let's make our Terraform code work smarter, not harder! 💪


What Exactly is a Function?

Before we jump into Terraform functions, let's understand what a function is in simple terms—especially if you're not from a programming background.

The Simple Explanation

A function makes your life easier by letting you reuse code instead of writing it repeatedly.

Imagine you need to add two numbers and print the result. You write:

a = 2
b = 3
c = a + b
print(c)

Simple enough for once. But what if you need to do this 10 times with different numbers? You could copy-paste this code 10 times, but that's:

  • Repetitive and boring

  • Prone to errors

  • Hard to maintain

  • Inefficient

Instead, you wrap this logic inside a function:

function sum(a, b) {
  c = a + b
  return c
}

Now you can call sum(2, 3), sum(5, 7), or sum(100, 200) anytime you need it. Write once, use everywhere!

That's the power of functions.


Functions in Terraform: A Key Difference

Terraform uses HCL (HashiCorp Configuration Language), which is a configuration language, not a full programming language like Python or JavaScript.

This means:

What Terraform CAN do:

  • Use powerful built-in functions

  • Manipulate data (strings, numbers, collections)

  • Perform calculations and transformations

  • Infrastructure provisioning

What Terraform CANNOT do:

  • Create custom functions (unlike programming languages)

  • Use classes or objects

  • Full OOP (Object-Oriented Programming) support

Key Takeaway: In Terraform, you cannot create your own functions. You can only use the built-in functions provided by Terraform.

But don't worry—Terraform provides a rich library of built-in functions that cover almost everything you need!


Categories of Terraform Functions

Terraform functions are organized into these categories:

  1. String Functions - Manipulating text

  2. Numeric Functions - Working with numbers

  3. Collection Functions - Lists, maps, sets operations

  4. Type Conversion Functions - Converting between types

  5. Date and Time Functions - Working with timestamps

  6. Validation Functions - Checking conditions

  7. Lookup Functions - Finding values in maps

  8. File Functions - Reading files

Today, we'll cover the first five categories with practical AWS examples.


Using Terraform Console

Before we dive into examples, let's learn about terraform console—an interactive shell for testing functions.

Open Terraform Console:

terraform console

This opens an interactive prompt where you can test functions directly without writing Terraform files.

To exit:

exit

or press Ctrl+D

Let's start exploring functions!


1. String Functions 📝

String functions help you manipulate text—essential for naming resources, formatting outputs, and data processing.

upper() - Convert to Uppercase

> upper("hello 30 days of aws terraform")
"HELLO 30 DAYS OF AWS TERRAFORM"

Real-world use: Standardizing environment names or tags.


lower() - Convert to Lowercase

> lower("HELLO 30 DAYS OF AWS TERRAFORM")
"hello 30 days of aws terraform"

Real-world use: S3 bucket names (which must be lowercase).


trim() - Remove Characters

The trim() function removes specified characters from the beginning and end of a string.

Syntax:

trim(string, character_set)

Example - Remove spaces:

> trim("  AWSTF  ", " ")
"AWSTF"

Example - Remove specific characters:

> trim("AWSTF", "F")
"AWST"

What it does: Removes all occurrences of "F" from the beginning and end of the string.


replace() - Replace Characters

Replace occurrences of a substring with another substring.

Syntax:

replace(string, search_string, replacement_string)

Example:

> replace("hello world", " ", "-")
"hello-world"

Real-world use: Converting spaces to hyphens for resource names.


substr() - Extract Substring

Extract a portion of a string based on position and length.

Syntax:

substr(string, start_index, length)

Example:

> substr("hello world", 0, 5)
"hello"

What it does: Starts at index 0, extracts 5 characters.

Real-world use: Truncating long strings to meet naming constraints.


2. Numeric Functions 🔢

Functions for mathematical operations on numbers.

max() - Find Maximum

> max(5, 12, 1)
12

Real-world use: Determining the maximum instance count across environments.


min() - Find Minimum

> min(5, 12, 1)
1

Real-world use: Setting minimum resource limits.


abs() - Absolute Value

> abs(-42)
42

Real-world use: Calculating differences or distances.


3. Collection Functions 📦

Functions for working with lists, maps, and sets.

length() - Count Elements

> length([1, 2, 3, 4])
4

Real-world use: Counting availability zones or subnet IDs.


concat() - Concatenate Lists

> concat([1, 2], [3, 4])
[1, 2, 3, 4]

Real-world use: Combining multiple lists of CIDR blocks or security group rules.


merge() - Merge Maps

> merge({a = 1, b = 2}, {c = 3, d = 4})
{
  "a" = 1
  "b" = 2
  "c" = 3
  "d" = 4
}

Real-world use: Combining default tags with environment-specific tags.


4. Type Conversion Functions 🔄

Convert values from one type to another.

toset() - Convert List to Set

> toset(["a", "b", "a"])
toset([
  "a",
  "b",
])

What happened? The duplicate "a" was removed because sets don't allow duplicates.


tonumber() - Convert to Number

> tonumber("23")
23

What changed? "23" (string) became 23 (number).


tostring() - Convert to String

> tostring(23)
"23"

What changed? 23 (number) became "23" (string).


5. Date and Time Functions ⏰

timestamp() - Current Time

> timestamp()
"2024-12-05T10:30:45Z"

Real-world use: Adding creation timestamps to resources.


formatdate() - Format Timestamp

> formatdate("DD-MM-YYYY", timestamp())
"05-12-2024"

Custom formats:

  • "YYYY-MM-DD" → "2024-12-05"

  • "DD/MM/YY" → "05/12/24"

  • "MMM DD, YYYY" → "Dec 05, 2024"


Real-World Terraform Examples

Now let's use these functions in actual Terraform configurations with AWS resources.


Example 1: Formatting Project Names

Scenario: You have a project name with uppercase letters and spaces. You need to convert it to lowercase and replace spaces with hyphens for S3 bucket naming.

variables.tf

variable "project_name" {
  description = "Name of the project"
  type        = string
  default     = "Project Alpha Resource"
}

main.tf

locals {
  # Convert to lowercase and replace spaces with hyphens
  formatted_project_name = lower(replace(var.project_name, " ", "-"))
}

outputs.tf

output "formatted_project_name" {
  description = "Formatted project name"
  value       = local.formatted_project_name
}

Run Terraform:

terraform init
terraform plan

Output:

formatted_project_name = "project-alpha-resource"

What happened:

  1. replace(var.project_name, " ", "-") → "Project-Alpha-Resource"

  2. lower(...) → "project-alpha-resource"


Example 2: Merging Tags

Scenario: You have default tags for all resources and environment-specific tags. You need to merge them.

variables.tf

variable "default_tags" {
  description = "Default tags for all resources"
  type        = map(string)
  default = {
    Company   = "TechCorp"
    ManagedBy = "Terraform"
  }
}

variable "environment_tags" {
  description = "Environment-specific tags"
  type        = map(string)
  default = {
    Environment = "Development"
    CostCenter  = "Engineering"
  }
}

main.tf

locals {
  # Merge both tag maps
  merged_tags = merge(var.default_tags, var.environment_tags)
}

resource "aws_s3_bucket" "example" {
  bucket = "my-terraform-bucket"
  tags   = local.merged_tags
}

Result:

tags = {
  Company     = "TechCorp"
  ManagedBy   = "Terraform"
  Environment = "Development"
  CostCenter  = "Engineering"
}

Example 3: Validating S3 Bucket Names

Scenario: S3 bucket names have strict requirements:

  • Maximum 63 characters

  • Lowercase only

  • No spaces or special characters

Let's auto-format any bucket name to meet these requirements.

variables.tf

variable "bucket_name" {
  description = "S3 bucket name"
  type        = string
  default     = "Project Alpha Storage!"
}

main.tf

locals {
  # Format bucket name to meet S3 requirements
  formatted_bucket_name = lower(
    replace(
      substr(var.bucket_name, 0, 63),  # Max 63 chars
      " ", 
      "-"
    )
  )

  # Remove special characters
  clean_bucket_name = replace(local.formatted_bucket_name, "!", "")
}

resource "aws_s3_bucket" "data" {
  bucket = local.clean_bucket_name

  tags = {
    Name = "Data Bucket"
  }
}

Input: "Project Alpha Storage!"

Steps:

  1. substr(..., 0, 63) → Limit to 63 characters

  2. replace(..., " ", "-") → "Project-Alpha-Storage!"

  3. lower(...) → "project-alpha-storage!"

  4. replace(..., "!", "") → "project-alpha-storage"

Final bucket name: project-alpha-storage


Example 4: Using split() and For Expression

Scenario: You have a comma-separated string of ports that you need to convert into a list and create security group rules.

variables.tf

variable "allowed_ports" {
  description = "Comma-separated list of allowed ports"
  type        = string
  default     = "80,443,8080,3306"
}

main.tf

locals {
  # Split the string into a list
  port_list = split(",", var.allowed_ports)

  # Create security group rule objects using for expression
  sg_rules = [
    for port in local.port_list : {
      name        = "port-${port}"
      port        = port
      description = "Allow traffic on port ${port}"
    }
  ]
}

outputs.tf

output "port_list" {
  description = "List of ports"
  value       = local.port_list
}

output "sg_rules" {
  description = "Security group rules"
  value       = local.sg_rules
}

Run Terraform:

terraform refresh
terraform output

Output:

port_list = [
  "80",
  "443",
  "8080",
  "3306",
]

sg_rules = [
  {
    "name"        = "port-80"
    "port"        = "80"
    "description" = "Allow traffic on port 80"
  },
  {
    "name"        = "port-443"
    "port"        = "443"
    "description" = "Allow traffic on port 443"
  },
  # ... and so on
]

What happened:

  1. split(",", var.allowed_ports)["80", "443", "8080", "3306"]

  2. for port in local.port_list → Iterates through each port

  3. Creates a map for each port with name, port, and description


Understanding String Interpolation

When you need to combine variables with strings, use string interpolation.

Syntax:

"${variable_name}"

Example:

variable "environment" {
  default = "production"
}

resource "aws_instance" "web" {
  ami           = "ami-12345"
  instance_type = "t3.micro"

  tags = {
    Name = "${var.environment}-web-server"
  }
}

Result: Tag Name = "production-web-server"

Why use ${}?

When you're inside a string and want to insert a variable's value, you must use ${}. This tells Terraform: "Evaluate this variable and insert its value here."


Example 5: lookup() Function

Scenario: You want different instance types for different environments (dev, staging, prod).

variables.tf

variable "environment" {
  description = "Deployment environment"
  type        = string
  default     = "dev"
}

variable "instance_sizes" {
  description = "Instance sizes per environment"
  type        = map(string)
  default = {
    dev     = "t2.micro"
    staging = "t3.small"
    prod    = "t3.large"
  }
}

main.tf

locals {
  # Look up instance size based on environment
  # If environment not found, use t2.micro as default
  instance_size = lookup(
    var.instance_sizes,
    var.environment,
    "t2.micro"  # Default fallback
  )
}

outputs.tf

output "instance_size" {
  description = "Selected instance size"
  value       = local.instance_size
}

Test different environments:

Environment: dev

terraform plan

Output: instance_size = "t2.micro"

Environment: prod (in terraform.tfvars)

environment = "prod"
terraform plan

Output: instance_size = "t3.large"

Environment: unknown

environment = "production"  # Not in map
terraform plan

Output: instance_size = "t2.micro" (fallback default)


How Functions Work Together

You can chain multiple functions to create powerful transformations:

locals {
  formatted_name = lower(replace(substr(var.name, 0, 20), " ", "-"))
}

Execution order (inside to outside):

  1. substr(var.name, 0, 20) → Limit to 20 characters

  2. replace(..., " ", "-") → Replace spaces with hyphens

  3. lower(...) → Convert to lowercase

Example flow:

Input: "Project Alpha Storage Bucket"
    ↓
substr(..., 0, 20) → "Project Alpha Storag"
    ↓
replace(..., " ", "-") → "Project-Alpha-Storag"
    ↓
lower(...) → "project-alpha-storag"

Quick Reference Table 📋

FunctionCategoryPurposeExample
upper()StringConvert to uppercaseupper("hello") → "HELLO"
lower()StringConvert to lowercaselower("HELLO") → "hello"
trim()StringRemove characterstrim(" hi ", " ") → "hi"
replace()StringReplace substringreplace("a-b", "-", "_") → "a_b"
substr()StringExtract substringsubstr("hello", 0, 3) → "hel"
split()StringSplit into listsplit(",", "a,b") → ["a", "b"]
max()NumericMaximum valuemax(1, 5, 3) → 5
min()NumericMinimum valuemin(1, 5, 3) → 1
abs()NumericAbsolute valueabs(-5) → 5
length()CollectionCount elementslength([1, 2, 3]) → 3
concat()CollectionMerge listsconcat([1], [2]) → [1, 2]
merge()CollectionMerge mapsmerge({a=1}, {b=2})
toset()Type ConvTo settoset(["a", "a"]) → ["a"]
tonumber()Type ConvTo numbertonumber("5") → 5
tostring()Type ConvTo stringtostring(5) → "5"
timestamp()Date/TimeCurrent timeReturns current timestamp
formatdate()Date/TimeFormat dateformatdate("DD-MM-YYYY", ...)
lookup()LookupFind in maplookup(map, key, default)

Wrapping Up

Today we covered the essential Terraform functions you'll use daily:

String Functions:

  • upper(), lower() - Case conversion

  • trim(), replace() - String manipulation

  • substr(), split() - String extraction

Numeric Functions:

  • max(), min(), abs() - Calculations

Collection Functions:

  • length(), concat(), merge() - Working with lists/maps

Type Conversion:

  • toset(), tonumber(), tostring() - Type changes

Date/Time:

  • timestamp(), formatdate() - Time handling

Lookup:

  • lookup() - Dynamic value selection

Key Takeaways:

  • Functions make code reusable and maintainable

  • Terraform has built-in functions only (no custom functions)

  • Chain functions for powerful transformations

  • Use terraform console to test functions

  • Always provide fallback values

Remember: The best way to learn is by doing. Open terraform console and experiment with these functions yourself!


Reference Video :

What's Next?

Tomorrow on Day 12, we'll continue with:

  • File Functions - Reading and processing files

  • Encoding Functions - Base64, JSON, YAML

  • IP Network Functions - CIDR calculations

  • Crypto Functions - Hashing and encryption

  • And more advanced function patterns!

Keep practicing, keep building, and see you tomorrow! 🚀


Practice makes perfect! Don't just read—try these functions in your own Terraform code.

Happy Terraforming! 🚀

More from this blog

B

BejadiRajeshReddy

19 posts