100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
HCL

Expressions and Functions

HCL expressions let you reference values, perform operations, and transform data using Terraform's extensive built-in function library. This topic covers references, operators, conditionals, and common functions.

HCL Language BasicsIntermediate10 min readJul 9, 2026
Analogies

Expressions and Functions

Every argument value in a Terraform configuration is an expression — even a plain string literal like "us-east-1" is technically the simplest possible expression. Expressions range from direct references (var.region, aws_vpc.main.id, local.name_prefix) to arithmetic and comparison operators, conditional (ternary) expressions, for expressions that transform collections, and calls into Terraform's large standard library of built-in functions. Because Terraform has no user-defined functions, the built-in library is what gives configurations real computational flexibility.

🏏

Cricket analogy: Even calling a shot 'defensive block' is technically a decision expression — from that simplest call, batting choices range from a straight bat block to a reverse-sweep, all evaluated in the moment the ball arrives, just like Terraform expressions from literals to functions.

References and Operators

References follow the pattern <TYPE>.<NAME>.<ATTRIBUTE> for resources, var.<name> for variables, local.<name> for locals, and module.<name>.<output> for child module outputs. Terraform supports the usual arithmetic (+, -, *, /, %), comparison (==, !=, <, >, <=, >=), and logical (&&, ||, !) operators, plus the conditional operator condition ? true_val : false_val for inline branching — commonly used to toggle a value based on an environment or feature flag.

🏏

Cricket analogy: A scorecard entry like team.batsman.runs mirrors Terraform's <TYPE>.<NAME>.<ATTRIBUTE> reference, and a captain's ternary call — rain forecast ? bat_first : bowl_first — is exactly the inline branching Terraform's conditional operator provides.

hcl
locals {
  is_prod        = var.environment == "production"
  instance_type  = local.is_prod ? "m5.large" : "t3.micro"
  desired_count  = local.is_prod ? 3 : 1

  # for expression: transform a list into a map
  subnet_by_az = {
    for s in data.aws_subnets.selected.ids :
    data.aws_subnet.each[s].availability_zone => s
  }

  # function calls
  bucket_name = lower(replace("${var.project} Reports", " ", "-"))
  cidr_block  = cidrsubnet(var.vpc_cidr, 8, 2)
  all_tags    = merge(var.default_tags, { Name = local.bucket_name })
}

for Expressions and splat

A for expression transforms one collection into another: [for x in list : upper(x)] produces a new list, and {for k, v in map : k => v} (or with a colon-separated key => value form) produces a new map. Optionally appending an if clause filters elements during the transformation. The splat operator, resource.*.attribute or more idiomatically resource[*].attribute, is a shorthand for extracting a given attribute from every instance of a resource created with count or for_each.

🏏

Cricket analogy: Converting a list of raw scores into strike rates — [for run in scores : run/balls*100] — is a for expression transforming a collection, and pulling every batsman's average with team.*.average is the splat operator's shorthand extraction.

Terraform's function library is organized by category — string functions (upper, lower, replace, format), collection functions (merge, concat, distinct, flatten), numeric functions (min, max, ceil), encoding functions (jsonencode, base64encode), and network functions (cidrsubnet, cidrhost) among others. The official documentation's function index is the fastest way to discover the right one rather than reinventing logic manually.

Functions like tostring(), tonumber(), tolist(), and tomap() perform explicit type conversion, useful when a value's inferred type doesn't match what a downstream argument expects. The coalesce() function returns the first non-null argument from a list, a common pattern for providing layered defaults, while try() attempts a sequence of expressions and returns the first one that doesn't produce an error — useful for gracefully handling optional or possibly-absent attributes.

🏏

Cricket analogy: Converting a scoreboard's string '287' to a number for run-rate math is exactly tonumber(), and coalesce() picking the first available score — live feed, else radio commentary, else scorecard — mirrors layered fallback defaults; try() gracefully handles a missing player stat.

for expressions and functions are pure and cannot have side effects or depend on execution order beyond normal dependency resolution — you cannot write imperative loops that mutate state across iterations. If you find yourself wanting genuinely procedural logic, that's usually a sign the work belongs in a provisioner, external script, or a purpose-built provider instead.

  • Every argument value in HCL is an expression, from literals to function calls.
  • References follow TYPE.NAME.ATTRIBUTE, var.NAME, local.NAME, and module.NAME.OUTPUT patterns.
  • The conditional operator condition ? a : b enables inline branching.
  • for expressions transform lists into lists or maps, optionally filtering with an if clause.
  • The splat operator resource[*].attribute extracts an attribute across all instances of a multi-instance resource.
  • Terraform provides no user-defined functions; complex logic composes the extensive built-in function library instead.

Practice what you learned

Was this page helpful?

Topics covered

#HCL#TerraformInfrastructureAsCodeStudyNotes#DevOps#ExpressionsAndFunctions#Expressions#Functions#References#Operators#StudyNotes#SkillVeris