What Is a Terraform Local Value?
A local value is scratch paper. You write a calculation once — like "${var.project}-${var.environment}" — give it a name (name_prefix), and reference that name everywhere you need the result instead of repeating the calculation.
Locals are the DRY (Don't Repeat Yourself) principle for Terraform expressions. If you find yourself writing the same expression in three resource blocks, that expression belongs in a local.
At Swiggy, the infrastructure team uses a single common_tags local that applies consistently to every resource. When the cost accounting team needs a new tag, one line changes in the locals block — not sixty lines across sixty resources.
+------------------------------------------+| Without locals — expression repeated: || bucket = "swiggy-${var.env}-data" | <- in S3 resource| name = "swiggy-${var.env}-app" | <- in EC2 resource| id = "swiggy-${var.env}-rds" | <- in RDS resource| 3 places to update when naming changes |+------------------------------------------+ | v+------------------------------------------+| With locals — computed once: || locals { || prefix = "swiggy-${var.environment}" || } || bucket = "${local.prefix}-data" || name = "${local.prefix}-app" || id = "${local.prefix}-rds" || 1 place to update |+------------------------------------------+Locals Block Syntax
# locals.tf — or add locals block anywhere in .tf files# All locals in one block are evaluated together locals { # ── String construction ──────────────────────────────────────────────── name_prefix = "${var.project}-${var.environment}" # swiggy-payments-prod # ── Tagging ─────────────────────────────────────────────────────────── # Define once, use on every resource common_tags = { Project = var.project Environment = var.environment ManagedBy = "terraform" Repository = "github.com/swiggy/infrastructure" CostCenter = "platform-${var.environment}" } # ── Conditional logic ────────────────────────────────────────────────── is_production = var.environment == "prod" instance_type = local.is_production ? "t3.large" : "t3.micro" backup_retention = local.is_production ? 30 : 3 multi_az = local.is_production # ── Map lookups ──────────────────────────────────────────────────────── # Look up instance type for the current environment ec2_size = lookup(var.instance_types_map, var.environment, "t3.micro") # ── Computed names ────────────────────────────────────────────────────── # Locals CAN reference other locals in the same block s3_bucket_name = "${local.name_prefix}-data-${data.aws_caller_identity.current.account_id}" rds_id = "${local.name_prefix}-postgres" sg_name = "${local.name_prefix}-app-sg" log_group = "/aws/${data.aws_region.current.name}/${local.name_prefix}/app" # ── List manipulation ────────────────────────────────────────────────── az_count = length(data.aws_availability_zones.available.names) azs = slice(data.aws_availability_zones.available.names, 0, 2) # Flatten list of lists into one list all_private_cidrs = flatten([var.private_subnet_cidrs, var.db_subnet_cidrs]) # ── Map operations ────────────────────────────────────────────────────── # Merge common tags with resource-specific tags rds_tags = merge(local.common_tags, { Name = local.rds_id Component = "database" Engine = "postgres" })}Using Locals in Resources
# main.tf — locals keep resource blocks clean and DRY resource "aws_s3_bucket" "data" { bucket = local.s3_bucket_name # computed once in locals tags = local.common_tags # map from locals — not repeated here} resource "aws_instance" "app" { ami = data.aws_ami.amazon_linux.id instance_type = local.instance_type # conditional — large in prod, micro in dev tags = merge(local.common_tags, { Name = "${local.name_prefix}-app" })} resource "aws_db_instance" "main" { identifier = local.rds_id multi_az = local.multi_az # true in prod, false in dev backup_retention_period = local.backup_retention tags = local.rds_tags}Locals vs Variables — When to Use Which
+------------------------------------------+| Use VARIABLES when: || - The caller needs to set the value || - The value differs per environment || - Example: instance_type, environment, || db_password, project_name |+------------------------------------------+ +------------------------------------------+| Use LOCALS when: || - Value is computed from other values || - Not settable by the caller || - Used in more than one place || - Example: name_prefix, common_tags, || is_production, s3_bucket_name |+------------------------------------------+Advanced Locals Patterns
locals { # For expressions inside locals upper_envs = [for env in ["dev", "staging", "prod"] : upper(env)] # Convert list to set for for_each environment_set = toset(["dev", "staging", "prod"]) # Complex conditional log_level = ( var.environment == "prod" ? "warn" : var.environment == "staging" ? "info" : "debug" # dev ) # Build IAM policy ARNs dynamically ssm_policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" # CIDR math public_subnets = [ for i in range(2) : cidrsubnet(var.vpc_cidr, 8, i + 1) ] # Result: ["10.0.1.0/24", "10.0.2.0/24"] when vpc_cidr = "10.0.0.0/16"}Troubleshooting Locals
| Error | Root Cause | Fix |
|---|---|---|
Error: Cycle: local.x -> local.y -> local.x |
Two locals referencing each other in a loop | Restructure — one must not depend on the other |
Error: local.x is not defined |
Typo in local name or missing declaration | Check spelling in locals block |
local.x returns wrong type |
Expression result type mismatch | Use tostring(), tolist(), or toset() for type coercion |
| Large locals block is hard to read | All locals in one block | Split into multiple locals blocks — they can appear anywhere in .tf files |
PLACEMENT PRO TIP**Tip:** Add a comment above each local explaining why it exists — not what it computes. `# common tags applied to every resource` is more useful than `# tags map` as a comment on the `common_tags` local.
REMEMBER THIS**Remember:** Locals cannot be set by callers. If a value needs to be configurable by the person running Terraform (or a CI/CD pipeline), it must be a variable. If it is computed internally from other values, it is a local.