Skip to main content

Local Values

Locals = ตัวแปรท้องถิ่นใน Terraform ที่ใช้ลด duplication และทำให้ code อ่านง่ายขึ้น — ไม่ใช่ input จากภายนอก

Locals Block

locals {
name_prefix = "${var.environment}-${var.project}"

common_tags = {
Environment = var.environment
Project = var.project
ManagedBy = "terraform"
}
}

ใช้ผ่าน local.<NAME>:

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

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

Locals vs Variables

Aspectvariablelocals
Sourceexternal (CLI, .tfvars, env)internal (computed)
Typetypedinferred
Overrideได้ไม่ได้
เปลี่ยนตอน applyผ่าน inputrecompute เอง
ใช้ทำอะไรparameterizeDRY + readable
Mental Model
  • variable = "input parameter" ของ module/project
  • locals = "private helper" คำนวณค่าใน module

Use Cases

1. Lessen Duplication

❌ ไม่ใช้ locals:

resource "aws_s3_bucket" "data" {
bucket = "${var.env}-${var.project}-data"
tags = {
Environment = var.env
Project = var.project
ManagedBy = "terraform"
}
}

resource "aws_s3_bucket" "logs" {
bucket = "${var.env}-${var.project}-logs"
tags = {
Environment = var.env
Project = var.project
ManagedBy = "terraform"
}
}

✅ ใช้ locals:

locals {
name_prefix = "${var.env}-${var.project}"
common_tags = {
Environment = var.env
Project = var.project
ManagedBy = "terraform"
}
}

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

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

2. Computed Values

variable "vpc_cidr" {
type = string
default = "10.0.0.0/16"
}

locals {
# คำนวณ subnet CIDR จาก VPC CIDR
public_subnets = [for i in range(2) : cidrsubnet(var.vpc_cidr, 8, i)]
private_subnets = [for i in range(2) : cidrsubnet(var.vpc_cidr, 8, i + 100)]
}

resource "aws_subnet" "public" {
count = length(local.public_subnets)
vpc_id = aws_vpc.main.id
cidr_block = local.public_subnets[count.index]
}

3. Conditional Logic

locals {
is_prod = var.environment == "prod"

instance_type = local.is_prod ? "t3.large" : "t3.micro"
backup_count = local.is_prod ? 30 : 7
multi_az = local.is_prod
}

resource "aws_db_instance" "main" {
instance_class = local.instance_type
backup_retention_period = local.backup_count
multi_az = local.multi_az
}

4. Transform Data

variable "users" {
type = list(object({
name = string
email = string
admin = bool
}))
}

locals {
# Convert list → map
user_map = { for u in var.users : u.name => u }

# Filter
admins = [for u in var.users : u if u.admin]

# Map (transform)
admin_emails = [for u in local.admins : u.email]
}

resource "aws_iam_user" "users" {
for_each = local.user_map
name = each.value.name
}

Multiple Locals Blocks

มีหลาย locals block ในไฟล์เดียวก็ได้ — Terraform merge ให้:

locals {
region = "ap-southeast-1"
}

locals {
full_name = "${local.region}-app"
}

→ access local.region และ local.full_name

ใช้แบ่งกลุ่ม locals ตาม theme เช่น:

locals {
# Naming
name_prefix = "${var.env}-${var.project}"
bucket_name = "${local.name_prefix}-data"
}

locals {
# Networking
vpc_cidr = "10.0.0.0/16"
public_subnets = [for i in range(2) : cidrsubnet(local.vpc_cidr, 8, i)]
}

locals {
# Tags
common_tags = {
Environment = var.env
ManagedBy = "terraform"
}
}

ตัวอย่าง Real-World

locals.tf
locals {
# Project metadata
project_name = "my-app"
environment = var.environment
region = data.aws_region.current.name
account_id = data.aws_caller_identity.current.account_id

# Naming
name_prefix = "${local.project_name}-${local.environment}"
bucket_name = "${local.name_prefix}-${local.account_id}-${local.region}"

# Tags (merge default with user tags)
common_tags = merge(
{
Project = local.project_name
Environment = local.environment
ManagedBy = "terraform"
Region = local.region
},
var.extra_tags
)

# Network (computed from VPC CIDR)
vpc_cidr = var.vpc_cidr
public_subnets = [for i in range(var.az_count) : cidrsubnet(local.vpc_cidr, 8, i)]
private_subnets = [for i in range(var.az_count) : cidrsubnet(local.vpc_cidr, 8, i + 100)]

# Conditional
is_prod = local.environment == "prod"
enable_deletion_protection = local.is_prod
backup_retention = local.is_prod ? 30 : 7
}

data "aws_region" "current" {}
data "aws_caller_identity" "current" {}

Anti-Patterns

# ❌ Locals ที่อ้างอิง resource attribute (slow, fragile)
locals {
vpc_id = aws_vpc.main.id # อันที่อ้าง .id ตรงๆ ดีกว่า
}

# ❌ Locals ที่คัดลอกค่ามาจาก variable
locals {
region = var.region # ใช้ var.region ตรงๆ ดีกว่า
}

# ❌ Locals ใหญ่เกินไป (อ่านยาก)
locals {
config = {
# 50+ keys ที่ลึกซ้อน
}
}
# ดีกว่าแยกเป็น object variable

สรุป

  • locals = private helper variable
  • ใช้ลด duplication + ทำ computed values + conditional logic
  • ไม่รับ input จากภายนอก (ต่างจาก variable)
  • มีหลาย locals block ในไฟล์เดียวได้
  • หลีกเลี่ยง locals ที่ "ก็คือ variable เหมือนกัน" — ใช้ var.<x> ตรงๆ ดีกว่า

ต่อไป → Section 6: Outputs