Terraform Local Values and Expressions

Repeating the same value in ten different places is a maintenance problem. Change that value once and you must update it ten times — and risk missing one. Local values (locals) solve this by letting you define a value once and reuse it everywhere. Expressions let you compute and transform values dynamically. This topic covers both.

What Are Local Values

A local value assigns a name to an expression. Once defined, you reference that name anywhere in the configuration. Unlike input variables (which accept external values), locals are computed internally and cannot be set from outside.

Analogy: A Sticky Note on Your Desk

You have a project code — "PRJ-2024-ALPHA" — that appears on server names, bucket names, tags, and log groups. Instead of typing it 15 times, you write it on one sticky note and put it where you can see it. Every time you need it, you look at the note. Locals are that sticky note.

Declaring Locals

All local values go inside a single locals block. You can have multiple locals blocks across different files — Terraform merges them.

locals {
  environment   = "production"
  project_name  = "ecommerce-app"
  region        = "us-east-1"

  # Computed from other locals
  name_prefix   = "${local.project_name}-${local.environment}"

  common_tags = {
    Project     = local.project_name
    Environment = local.environment
    ManagedBy   = "Terraform"
  }
}

Reference locals with the local. prefix (singular — not locals.):

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"
  tags          = local.common_tags
}

resource "aws_s3_bucket" "app_data" {
  bucket = "${local.name_prefix}-data"
  tags   = local.common_tags
}

Diagram: One Local, Many Uses

locals {
  name_prefix = "ecommerce-app-production"
}
        |
        |-----> aws_instance.web     tags.Name = "ecommerce-app-production-web"
        |-----> aws_s3_bucket.data   bucket    = "ecommerce-app-production-data"
        |-----> aws_db_instance.db   identifier= "ecommerce-app-production-db"
        |-----> aws_lb.frontend      name      = "ecommerce-app-production-lb"

Update the local once and all four resources automatically use the new value.

Expressions in Terraform

An expression is any value that Terraform evaluates — a literal, a reference, or a computation. Expressions appear as argument values anywhere in your configuration.

String Interpolation

Embed expressions inside strings using ${...} syntax.

locals {
  app_name = "myapp"
  env      = "prod"
  full_name = "${local.app_name}-${local.env}"
  # Result: "myapp-prod"
}

Conditional Expressions

Use a ternary expression to choose between two values based on a condition.

# Syntax: condition ? value_if_true : value_if_false

locals {
  instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
}

If the environment is production, use the larger instance. Otherwise use the smaller one. One line replaces a whole block of if-else logic.

Arithmetic and Comparison Expressions

locals {
  replica_count    = var.environment == "prod" ? 3 : 1
  storage_size_gb  = var.base_storage * 2
  is_large_env     = var.replica_count > 2
}

Heredoc Strings for Multi-line Values

When a value spans multiple lines — such as a policy document or a startup script — use heredoc syntax.

locals {
  user_data_script = <<-EOT
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
  EOT
}

Practical Example: Centralising Tags

One of the most common uses of locals in real projects is centralising resource tags. Every cloud resource gets the same base tags, plus any resource-specific tags added on top.

locals {
  base_tags = {
    Project     = var.project_name
    Environment = var.environment
    Owner       = "platform-team"
    ManagedBy   = "Terraform"
  }
}

resource "aws_instance" "api_server" {
  ami           = data.aws_ami.linux.id
  instance_type = "t3.medium"

  tags = merge(local.base_tags, {
    Name = "api-server"
    Role = "backend"
  })
}

The merge() function combines two maps. The base tags apply to every resource; the resource-specific tags layer on top.

Key Points

  • Local values define computed names inside a locals block and are referenced with the local. prefix.
  • Locals reduce repetition and make your configuration easier to update and read.
  • Use string interpolation ("${...}") to embed expressions inside strings.
  • Conditional expressions (condition ? true_val : false_val) choose between values based on logic.
  • Centralising tags in a local.common_tags map is a widely used best practice in real Terraform projects.

Leave a Comment