skip to content
Astro Cactus

Essential Terraform Functions and Expressions

/ 4 min read

Series Navigation

Introduction to Terraform Functions

Terraform functions help you transform and combine values within your configurations. This post covers essential built-in functions and how to use them effectively.

String Functions

1. String Manipulation

locals {
# Format strings
instance_name = format("web-server-%s-%s", var.environment, var.region)
# Convert case
uppercase_name = upper(local.instance_name)
lowercase_name = lower(local.instance_name)
# Trim whitespace
cleaned_string = trimspace(" hello ")
# Split and join
tags_list = split(",", "tag1,tag2,tag3")
tags_string = join(", ", local.tags_list)
# Regular expressions
matches = regex("^web-(.+)-(.+)$", local.instance_name)
}

2. String Templates

locals {
# Basic template
welcome_message = "Hello, ${var.user_name}!"
# Multiline template
user_data = <<-EOF
#!/bin/bash
echo "Installing dependencies..."
apt-get update
apt-get install -y ${join(" ", var.packages)}
EOF
}

Numeric Functions

1. Mathematical Operations

locals {
# Basic math
max_instances = max(2, 3, 1)
min_instances = min(2, 3, 1)
# Rounding
ceil_value = ceil(3.1) # Returns 4
floor_value = floor(3.9) # Returns 3
# Absolute value
abs_value = abs(-42)
}

2. Number Conversions

locals {
# Parse numbers
parsed_int = parseint("42", 10) # Base 10
parsed_hex = parseint("2A", 16) # Base 16
# Format numbers
formatted_float = format("%.2f", 3.14159)
}

Collection Functions

1. List Operations

locals {
# List manipulation
first_item = element(var.availability_zones, 0)
reversed_list = reverse(var.subnet_cidrs)
# List comprehension
instance_ids = [for instance in aws_instance.web : instance.id]
# Filtering
prod_instances = [
for instance in var.instances
if instance.environment == "prod"
]
# Flatten nested lists
flattened = flatten([
["a", "b"],
["c", "d"]
])
}

2. Map Operations

locals {
# Merge maps
combined_tags = merge(
var.common_tags,
{
Environment = var.environment
Project = var.project_name
}
)
# Keys and values
tag_keys = keys(local.combined_tags)
tag_values = values(local.combined_tags)
# Map transformation
upper_tags = {
for key, value in var.tags :
upper(key) => upper(value)
}
}

Type Conversion Functions

locals {
# Convert to string
port_string = tostring(80)
# Convert to number
count_number = tonumber("42")
# Convert to boolean
feature_enabled = tobool("true")
# Convert to list
az_list = tolist(toset(var.availability_zones))
# Convert to map
tags_map = tomap({
Name = "example"
Environment = "prod"
})
}

Date and Time Functions

locals {
# Current timestamp
current_time = timestamp()
# Format timestamp
formatted_time = formatdate("YYYY-MM-DD hh:mm:ss", timestamp())
# Time calculations
expiry_time = timeadd(timestamp(), "24h")
time_diff = timecmp(timestamp(), local.expiry_time)
}

Encoding and Hashing Functions

locals {
# Base64 encoding/decoding
encoded = base64encode("Hello, World!")
decoded = base64decode(local.encoded)
# File content
cert_content = filebase64("${path.module}/cert.pem")
# Hashing
md5_hash = md5("Hello, World!")
sha256_hash = sha256("Hello, World!")
}

Filesystem Functions

locals {
# File operations
file_content = file("${path.module}/template.txt")
template_rendered = templatefile("${path.module}/script.tpl", {
packages = var.packages
environment = var.environment
})
# Directory operations
files = fileset(path.module, "scripts/*.sh")
}

IP Network Functions

locals {
# CIDR calculations
subnet_cidrs = cidrsubnets("10.0.0.0/16", 4, 4, 4)
# IP manipulation
host_ip = cidrhost("10.0.0.0/24", 5)
# Network calculations
network_prefix = cidrnetmask("10.0.0.0/16")
}

Conditional Functions

locals {
# Conditional selection
instance_type = coalesce(var.instance_type, "t3.micro")
# Ternary operator
environment_tag = var.environment != "" ? var.environment : "default"
# Can (null safety)
instance_ip = can(aws_instance.example.private_ip) ? aws_instance.example.private_ip : null
}

Best Practices for Using Functions

  1. Readability First

    # Good: Clear and readable
    locals {
    instance_name = format("web-%s-%s", var.env, var.region)
    }
    # Avoid: Too complex
    locals {
    instance_name = join("-", compact(concat(["web"], split(",", var.env), split(",", var.region))))
    }
  2. Use Type Constraints

    variable "port_number" {
    type = number
    validation {
    condition = can(tonumber(var.port_number))
    error_message = "Port must be a valid number."
    }
    }
  3. Error Handling

    locals {
    safe_division = can(1/var.denominator) ? 1/var.denominator : null
    }

Common Use Cases and Examples

1. Dynamic Tag Generation

locals {
common_tags = merge(
var.default_tags,
{
Name = format("%s-%s", var.project_name, var.environment)
Environment = var.environment
ManagedBy = "terraform"
Timestamp = formatdate("YYYY-MM-DD", timestamp())
}
)
}

2. Conditional Resource Creation

resource "aws_instance" "example" {
count = var.environment == "prod" ? 2 : 1
instance_type = contains(["prod", "staging"], var.environment) ? "t3.medium" : "t3.micro"
tags = merge(
local.common_tags,
{
Role = coalesce(var.instance_role, "default")
}
)
}

3. Dynamic Block Generation

dynamic "ingress" {
for_each = {
for port in var.allowed_ports :
port => port
}
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
cidr_blocks = [
formatlist("%s/32", var.allowed_ips)
]
}
}

Next Steps

In Part 4: Variables, Outputs, and Dependencies, we’ll explore how to use these functions with variables and outputs to create more dynamic and flexible infrastructure configurations.

Additional Resources