# terraform / locals / expressions

Don't Repeat Yourself: Local Values

A local value assigns a name to an expression so you can use it multiple times within a module without repeating code. Unlike input variables, locals cannot be overridden from outside — they are computed internally to enforce naming rules, merge common tags, and simplify complex expressions.

locals { ... }DRY
name_prefix"${var.project}-${var.env}"
common_tagsmerge(tags, { ... })
is_productionvar.env == "prod"
1
Definition
N
Reuses in Module
0
External Drift
# concept

What is a Local Value?

Think of Input Variables as function parameters that someone else passes in, and Local Values as internal local variables created inside the function body to store intermediate calculations.

When to Use Local Values

  • Standardized Naming: Concatenating "${var.env}-${var.app_name}-${var.region}" once instead of typing it on 20 resources.
  • Common Tag Sets: Enforcing mandatory compliance tags (Owner, Environment, CostCenter, ManagedBy) across all cloud assets.
  • Complex Calculations: Storing ternary conditions or formatting CIDR blocks cleanly.
internal only

Scoped to Current Module

Locals are strictly internal. A parent module or CLI argument cannot override or read a child module's locals block. This encapsulates business logic safely.

dynamic

Reference Any Variable or Resource

Locals can reference variables (var.foo), other locals (local.bar), and exported resource attributes (aws_vpc.main.id).

# syntax

Declaring & Referencing Locals

locals.tfDECLARATION
1 2 3 4 5 6 7 8 9
locals {
  service_name = "billing-api"
  owner        = "shan-devops"
  prefix       = "${var.environment}-${local.service_name}"
  
  is_prod      = var.environment == "production" ? true : false
  instance_type= local.is_prod ? "m5.large" : "t3.micro"
}
main.tfUSAGE (local.NAME)
1 2 3 4 5 6 7 8
resource "aws_instance" "web" {
  ami           = "ami-0123456789abcdef0"
  instance_type = local.instance_type

  tags = {
    Name = "${local.prefix}-web-server"
  }
}

Notice the reference syntax: You declare with locals { ... } (plural), but when referencing you use local.name (singular).

# production recipe

Enterprise Tagging with merge()

Every enterprise cloud governance policy requires tags for cost tracking, auditing, and ownership. Defining a central common_tags map inside a local block and combining it with resource-specific tags via the merge() function is the gold standard.

maintenance

Single Place to Update

If finance requests a new CostCenter = "CC-904" tag across 50 resources, you add one line in locals.tf and Terraform updates every resource cleanly on next apply.

main.tfTAG MERGING PATTERN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
locals {
  common_tags = {
    Project     = "ECommerce-Portal"
    Environment = var.environment
    ManagedBy   = "Terraform"
    Owner       = "Shan DevOps"
  }
}

resource "aws_s3_bucket" "app_data" {
  bucket = "shan-app-storage-${var.environment}"

  tags = merge(local.common_tags, {
    Name        = "app-storage-bucket"
    DataPrivacy = "Confidential"
  })
}
# expressions

Complex Expressions & String Manipulation

locals.tfADVANCED EXPRESSIONS
1 2 3 4 5 6 7 8 9 10 11 12
locals {
  # List slicing and joining
  azs             = ["ap-south-1a", "ap-south-1b", "ap-south-1c"]
  active_azs      = slice(local.azs, 0, var.az_count)

  # Dynamic S3 bucket naming (lower-cased and sanitized)
  bucket_name     = lower(replace("${var.company}-${var.project}-${var.environment}", "_", "-"))

  # Conditional count
  enable_bastion  = var.environment == "prod" ? 1 : 0
}
readability

Clean Resource Blocks

Without locals, resource blocks become bloated with nested functions like lower(replace(...)) or complex ternary statements. Shifting computation into locals.tf keeps main.tf clean and declarative.

maintenance

Debug with terraform console

You can test and evaluate local expressions interactively in the CLI using terraform console before applying changes.

# comparison

Variables vs Locals vs Outputs vs Data

TypeSyntax PrefixWho Sets Value?Overridable from CLI?Primary Purpose
Input Variablevar.nameUser / CI/CD pipelineYesExternal parameters & environment tuning
Local Valuelocal.nameInternal module logicNoDRY expressions, naming rules, tag maps
Output Valueoutput.nameExported by resourceNoReturn values for CLI or parent modules
Data Sourcedata.type.nameCloud Provider APINoRead existing infrastructure external to code
# guidelines

Best Practices & Pitfalls to Avoid

✓ DO

  • Use locals for values referenced multiple times in the same module.
  • Use locals for common tag maps and standard naming prefixes.
  • Store locals in a dedicated locals.tf file when projects grow.

✗ DON'T

  • Don't create a local for every single hardcoded literal used only once.
  • Don't use locals when users need to customize the value per environment (use variables).
  • Avoid creating circular local dependencies (e.g. A references B, B references A).