
Terraform functions follow a consistent pattern:
function_name(arg1, arg2, ...). In real modules you typically pass variables, resource attributes, or data lookups into these functions — not hard-coded literals. Using functions centralizes logic and reduces duplication.Basic function pattern
Terraform functions accept zero or more arguments and return a computed value. The most common usage patterns include string formatting for resource names, numeric decisions for sizing, and collection transformations to supportfor_each or indexing.

var.env, aws_vpc.main.cidr_block) when using in modules.
Quick reference: common function categories
| Category | Purpose | Example |
|---|---|---|
| Numeric | Sizing, scaling, rounding | max(10, var.min_cpu) |
| String | Naming, tagging, payloads | join("-", [var.env, var.service]) |
| Network | CIDR math, host addressing | cidrsubnet(aws_vpc.main.cidr_block, 3, 1) |
| Type conversions | Stable iteration and lookups | toset(var.azs) |
Numeric functions
Numeric helpers let Terraform compute sizing decisions and defaults instead of hard-coding values. Common functions:max(...)— largest numeric valuemin(...)— smallest numeric valuefloor(...)— round downceil(...)— round up
String functions
String manipulation enforces naming conventions, builds unique resource names, and prepares user-data payloads. Common functions:join(separator, list)— concatenate a list of stringsupper(s)/lower(s)— case conversionsreplace(s, old, new)— substring substitutionbase64encode(s)— encode for user-data or API payloads
format() to build predictable names, e.g. format("%s-%s-%s", var.env, var.role, var.region).

Network functions: CIDR math without the pain
Network functions likecidrsubnet and cidrhost let you calculate subnets and host addresses programmatically.
Example: create a VPC and generate multiple subnets using cidrsubnet
cidrsubnet(base_cidr, newbits, netnum) works:
newbitsextends the prefix length by that many bits, so the original block splits into2^newbitssubnets.netnumselects which subnet index to use (0-based).- In the example,
newbits = 3turns/16into/19subnets.
When using
cidrsubnet with dynamic indexes, ensure your netnum never exceeds 2^newbits - 1. Off-by-one or unstable indexing can produce overlapping CIDR ranges. Convert sets back to lists for stable indexing when required.Type conversion and collection helpers
Convert collections to the proper type forfor_each, maps, or index-based logic.
Key functions:
toset(x)— convert to set (unique values; good forfor_each)tolist(x)— convert to list (stable index order required)tomap(x)— convert to map for key lookupstostring(x)— make values suitable for tags or logs
for_each, then compute a deterministic subnet index
tosetremoves duplicates sofor_eachiterates only unique values.- If you need deterministic indices, convert the set back to a list with
tolist(...)and useindex(...).
Compact cheat sheet
| Function | Returns | Typical use |
|---|---|---|
max(...), min(...) | number | Compute capacity or constraints |
floor(x), ceil(x) | number | Rounding for counts or sizes |
join(sep, list) | string | Build names or tags |
upper(s), lower(s) | string | Enforce naming conventions |
replace(s, old, new) | string | Templating strings |
base64encode(s) | string | User-data or API payloads |
cidrsubnet(base, newbits, netnum) | string (CIDR) | Split VPC CIDR into subnets |
toset(x), tolist(x), tomap(x) | collection | Prepare for for_each, index access, or lookups |