TerraformPilot

DevOps

Terraform Locals Explained: Simplify Your Configuration

Learn Terraform locals to reduce duplication, compute values, and organize complex configurations. Practical examples with naming conventions, tag maps

LLuca Berton1 min read
Terraform Locals Explained: Simplify Your Configuration

Locals are computed values within your Terraform configuration — think of them as named constants or intermediate variables. They reduce duplication, make complex expressions readable, and keep your code DRY.

Basic Syntax

#
locals {
  project     = "myapp"
  environment = terraform.workspace
  region      = "us-east-1"
 
  # Computed from other locals
  name_prefix = "${local.project}-${local.environment}"
}
 
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
 
  tags = {
    Name = "${local.name_prefix}-vpc"  # "myapp-prod-vpc"
  }
}

Locals vs Variables

#
Featurevariablelocals
Set byUser (tfvars, CLI, env)Terraform author (in code)
PurposeConfigurable inputsComputed/derived values
Overridable✅ Yes❌ No (fixed in code)
Validationvalidation block❌ No
Referencevar.namelocal.name
# Variable: user provides the value
variable "environment" {
  type    = string
  default = "dev"
}
 
# Local: you compute the value
locals {
  is_production = var.environment == "prod"
  instance_type = local.is_production ? "t3.xlarge" : "t3.micro"
}

Rule: If the user should be able to change it → variable. If it's derived from other values → locals.

Common Patterns

#

1. Standard Tags

#
locals {
  common_tags = {
    Project     = var.project
    Environment = var.environment
    ManagedBy   = "terraform"
    Team        = var.team
    CostCenter  = var.cost_center
  }
}
 
resource "aws_instance" "web" {
  ami           = data.aws_ami.al2023.id
  instance_type = "t3.micro"
 
  tags = merge(local.common_tags, {
    Name = "${local.name_prefix}-web"
    Role = "web-server"
  })
}
 
resource "aws_s3_bucket" "data" {
  bucket = "${local.name_prefix}-data"
  tags   = local.common_tags
}
 
resource "aws_rds_instance" "main" {
  identifier = "${local.name_prefix}-db"
  # ...
  tags = merge(local.common_tags, {
    Name     = "${local.name_prefix}-db"
    Backup   = "daily"
    Snapshot = "true"
  })
}

Every resource gets consistent tags without repeating the map.

2. Naming Conventions

#
locals {
  name_prefix = "${var.project}-${var.environment}"
 
  # Resource-specific names
  vpc_name     = "${local.name_prefix}-vpc"
  cluster_name = "${local.name_prefix}-ecs"
  db_name      = "${local.name_prefix}-db"
  bucket_name  = "${local.name_prefix}-data-${data.aws_caller_identity.current.account_id}"
}

3. Conditional Logic

#
locals {
  is_production = var.environment == "prod"
 
  # Different configs per environment
  instance_type    = local.is_production ? "t3.xlarge"  : "t3.micro"
  multi_az         = local.is_production ? true         : false
  min_capacity     = local.is_production ? 3            : 1
  max_capacity     = local.is_production ? 10           : 2
  enable_monitoring = local.is_production
  backup_retention = local.is_production ? 30           : 7
}
 
resource "aws_db_instance" "main" {
  instance_class       = local.instance_type
  multi_az             = local.multi_az
  backup_retention_period = local.backup_retention
  monitoring_interval  = local.enable_monitoring ? 60 : 0
}

4. Data Transformation

#
variable "subnet_config" {
  default = {
    public = {
      cidrs = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
      public = true
    }
    private = {
      cidrs = ["10.0.11.0/24", "10.0.12.0/24", "10.0.13.0/24"]
      public = false
    }
  }
}
 
locals {
  azs = data.aws_availability_zones.available.names
 
  # Flatten into a map for for_each
  all_subnets = merge([
    for tier, config in var.subnet_config : {
      for i, cidr in config.cidrs :
      "${tier}-${local.azs[i]}" => {
        cidr   = cidr
        az     = local.azs[i]
        tier   = tier
        public = config.public
      }
    }
  ]...)
}
 
resource "aws_subnet" "all" {
  for_each = local.all_subnets
 
  vpc_id                  = aws_vpc.main.id
  cidr_block              = each.value.cidr
  availability_zone       = each.value.az
  map_public_ip_on_launch = each.value.public
 
  tags = {
    Name = "${local.name_prefix}-${each.key}"
    Tier = each.value.tier
  }
}

5. Computed CIDR Blocks

#
locals {
  vpc_cidr = "10.0.0.0/16"
 
  public_subnets = {
    for i, az in local.azs :
    az => cidrsubnet(local.vpc_cidr, 8, i)
  }
 
  private_subnets = {
    for i, az in local.azs :
    az => cidrsubnet(local.vpc_cidr, 8, i + 100)
  }
 
  database_subnets = {
    for i, az in local.azs :
    az => cidrsubnet(local.vpc_cidr, 8, i + 200)
  }
}

6. Merging Configuration

#
variable "additional_tags" {
  type    = map(string)
  default = {}
}
 
locals {
  # Base tags + user-provided tags + auto-generated tags
  all_tags = merge(
    local.common_tags,
    var.additional_tags,
    {
      LastUpdated = timestamp()
    }
  )
}

Multiple locals Blocks

#

You can have multiple locals blocks — Terraform merges them:

# locals-naming.tf
locals {
  name_prefix = "${var.project}-${var.environment}"
}
 
# locals-tags.tf
locals {
  common_tags = {
    Project     = var.project
    Environment = var.environment
  }
}
 
# locals-network.tf
locals {
  vpc_cidr = "10.0.0.0/16"
  azs      = ["us-east-1a", "us-east-1b", "us-east-1c"]
}

Anti-Patterns

#

Don't Over-Abstract

#
# ❌ Too many levels of indirection
locals {
  a = var.x
  b = local.a
  c = local.b
  d = local.c  # What is this even?
}
 
# ✅ Direct and readable
locals {
  instance_type = var.environment == "prod" ? "t3.xlarge" : "t3.micro"
}

Don't Use Locals for Simple Values

#
# ❌ Unnecessary — just use the variable directly
locals {
  region = var.region
}
 
# ✅ Use local only when you're computing something
locals {
  region_short = replace(var.region, "-", "")  # "useast1"
}

Hands-On Courses

#

Conclusion

#

Locals keep your Terraform code DRY and readable. Use them for standard tags, naming conventions, conditional logic, and data transformation. Don't use them as variable aliases — only create a local when you're computing or deriving a value. The most common pattern is common_tags + name_prefix — these two locals alone eliminate hundreds of lines of duplication in a typical project.

#Terraform#DevOps#IaC#HCL

Share this article