Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

Terraform Expressions and Functions

Terraform’s expression language (HCL) includes dozens of built-in functions for transforming values, computing network addresses, encoding data, manipulating strings, and working with collections. Expressions are evaluated during terraform plan — no runtime execution.


String Functions

locals {
# format — printf-style formatting
bucket_name = format("mycompany-%s-%s", var.environment, var.region)
# "mycompany-production-us-east-1"
# join / split
az_string = join(", ", ["us-east-1a", "us-east-1b", "us-east-1c"])
# "us-east-1a, us-east-1b, us-east-1c"
parts = split(",", "api,auth,worker")
# ["api", "auth", "worker"]
# upper / lower / title
env_upper = upper(var.environment) # "PRODUCTION"
env_lower = lower(var.environment) # "production"
# replace
clean_name = replace(var.service_name, "_", "-") # "my_service" → "my-service"
# trimspace / trim / trimprefix / trimsuffix
clean = trimspace(" hello world ") # "hello world"
# startswith / endswith
is_api = startswith(var.service_name, "api-")
# substr
short_name = substr(var.service_name, 0, 20) # First 20 chars
# length (for strings)
name_length = length(var.service_name)
# contains (substring check)
has_prod = strcontains(var.environment, "prod")
}

String Templates

# Interpolation
resource "aws_cloudwatch_log_group" "app" {
name = "/ecs/${var.environment}/${var.service_name}"
}
# Heredoc for multi-line
locals {
user_data = <<-EOT
#!/bin/bash
echo "Environment: ${var.environment}"
echo "Service: ${var.service_name}"
systemctl start myapp
EOT
}
# templatefile — render a template file with variables
resource "aws_instance" "web" {
user_data = templatefile("${path.module}/templates/user-data.sh.tpl", {
environment = var.environment
app_version = var.app_version
db_endpoint = aws_db_instance.main.endpoint
})
}

Numeric Functions

locals {
max_instances = max(var.min_capacity, 1)
min_replicas = min(var.desired_count, var.max_count)
abs_offset = abs(-5) # 5
# floor / ceil
instances = ceil(var.vcpus / 4.0) # Round up to whole instances
# pow
storage = pow(2, 10) # 1024
}

Collection Functions

locals {
subnet_ids = ["subnet-01", "subnet-02", "subnet-03"]
regions = ["us-east-1", "eu-west-1", "us-east-1"] # duplicate
# length
count = length(local.subnet_ids) # 3
# distinct — remove duplicates (preserves order)
unique_regions = distinct(local.regions) # ["us-east-1", "eu-west-1"]
# flatten — collapse nested lists
all_ids = flatten([
["subnet-01", "subnet-02"],
["subnet-03", "subnet-04"]
]) # ["subnet-01", "subnet-02", "subnet-03", "subnet-04"]
# concat — join lists
all_subnets = concat(var.public_subnet_ids, var.private_subnet_ids)
# element — get element at index (wraps around)
az = element(var.availability_zones, count.index % length(var.availability_zones))
# slice — sublist
first_two_azs = slice(var.availability_zones, 0, 2)
# contains — membership check
is_allowed = contains(["t3.micro", "t3.small"], var.instance_type)
# keys / values (for maps)
env_names = keys(var.environments)
env_values = values(var.environments)
# lookup — safe map access with default
instance_type = lookup(var.instance_types_by_env, var.environment, "t3.micro")
# zipmap — build a map from lists of keys and values
tag_map = zipmap(["Environment", "Team"], [var.environment, var.team])
# merge — combine maps (right-hand takes precedence)
all_tags = merge(
{ ManagedBy = "terraform", Environment = var.environment },
var.additional_tags
)
# toset / tolist / tomap — type conversions
subnet_set = toset(var.subnet_ids) # For for_each
}

For Expressions

locals {
# Transform a list
upper_envs = [for e in var.environments : upper(e)]
# Filter a list
prod_buckets = [for b in var.buckets : b if b.environment == "production"]
# Transform a list to a map
bucket_by_name = { for b in var.buckets : b.name => b.arn }
# Transform a map
tags_upper = { for k, v in var.tags : upper(k) => v }
# Filter a map
enabled_services = { for k, v in var.services : k => v if v.enabled }
# Flatten nested structures
all_users = flatten([
for team in var.teams : [
for user in team.members : {
name = user
team = team.name
}
]
])
}

Network / CIDR Functions

locals {
vpc_cidr = "10.0.0.0/16"
# cidrsubnet — compute subnets
# cidrsubnet(prefix, newbits, netnum)
subnet_0 = cidrsubnet(local.vpc_cidr, 8, 0) # "10.0.0.0/24"
subnet_1 = cidrsubnet(local.vpc_cidr, 8, 1) # "10.0.1.0/24"
subnet_2 = cidrsubnet(local.vpc_cidr, 8, 2) # "10.0.2.0/24"
# cidrhost — specific IP from CIDR
gateway_ip = cidrhost(local.vpc_cidr, 1) # "10.0.0.1"
broadcast = cidrhost(local.vpc_cidr, -1) # "10.0.255.255"
# cidrnetmask — get subnet mask
netmask = cidrnetmask("10.0.0.0/16") # "255.255.0.0"
# cidrsubnets — compute multiple subnets at once
all_subnets = cidrsubnets(local.vpc_cidr, 8, 8, 8, 8)
# ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
# Auto-compute subnets for each AZ
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index)
availability_zone = var.availability_zones[count.index]
}

Encoding Functions

locals {
# base64 encode/decode
encoded = base64encode("Hello, World!") # "SGVsbG8sIFdvcmxkIQ=="
decoded = base64decode("SGVsbG8sIFdvcmxkIQ==")
# JSON encode/decode
config_json = jsonencode({
environment = var.environment
services = var.service_names
tags = var.tags
})
config_map = jsondecode(data.aws_secretsmanager_secret_version.config.secret_string)
# MD5 / SHA (for change detection)
script_hash = filemd5("${path.module}/scripts/bootstrap.sh")
config_hash = sha256(jsonencode(var.app_config))
}

Filesystem Functions

locals {
# file — read a file's contents
ca_cert = file("${path.module}/certs/ca.pem")
# filebase64 — read and base64 encode
cert_b64 = filebase64("${path.module}/certs/server.pem")
# filemd5 / filesha256 — hash a file
script_hash = filemd5("${path.module}/scripts/init.sh")
# path.module — directory of the current module
# path.root — root module directory
# path.cwd — current working directory
}

Conditional Expression

locals {
# condition ? true_value : false_value
instance_type = var.environment == "production" ? "t3.large" : "t3.micro"
replica_count = var.high_availability ? 3 : 1
log_retention = var.environment == "production" ? 90 : 7
# Nested conditionals (keep shallow for readability)
size = (
var.environment == "production" ? "xlarge" :
var.environment == "staging" ? "medium" :
"small"
)
}