Variable: Input และ Output ใน Terraform

Variable เป็นกลไกที่ทำให้ Terraform configuration สามารถนำกลับมาใช้ซ้ำได้ในหลายสภาพแวดล้อม เพราะค่าต่าง ๆ เช่น region, size, domain name ไม่ได้ถูก hardcode ลงในโค้ด แต่ส่งผ่านจากภายนอก ทำให้โปรเจกต์เดียวสามารถสร้าง dev ขนาดเล็กและ prod ขนาดใหญ่ได้โดยไม่ต้องแก้ไฟล์ .tf เลย การออกแบบ variable ที่ดีตั้งแต่แรกคือกุญแจของการทำ module ที่ยืดหยุ่น

บทความนี้จะอธิบายประเภทของ variable ใน Terraform การประกาศพร้อม type constraint และ validation การส่งค่าหลายวิธี และความแตกต่างของ input variable กับ output value ที่ผู้เริ่มต้นมักสับสน

ประกาศ Input Variable

Input variable ประกาศด้วย block variable ภายในไฟล์ (นิยมใส่ใน variables.tf) แต่ละตัวควรมี description, type และ default ถ้าเป็นไปได้ เพื่อให้ผู้ใช้งานเข้าใจวัตถุประสงค์และลดโอกาสผิดพลาดขณะส่งค่า

variable "region" {
  description = "AWS region to deploy resources"
  type        = string
  default     = "ap-southeast-1"
}

variable "instance_count" {
  description = "Number of worker instances"
  type        = number
  default     = 2
}

variable "enable_monitoring" {
  description = "Enable detailed monitoring"
  type        = bool
  default     = false
}

variable "tags" {
  description = "Common tags for all resources"
  type        = map(string)
  default     = {}
}

Type Constraint ที่ซับซ้อนขึ้น

นอกจาก primitive type, Terraform รองรับ structural type เช่น object และ tuple ที่ระบุรูปแบบข้อมูลได้ละเอียด เหมาะกับ variable ที่ต้องการกำหนด schema ให้ผู้ใช้ส่งค่าตามโครงสร้างที่วางไว้

variable "database" {
  description = "Database configuration"
  type = object({
    engine  = string
    version = string
    size    = string
    multi_az = bool
  })
  default = {
    engine   = "postgres"
    version  = "15.4"
    size     = "db.t3.small"
    multi_az = false
  }
}

variable "subnets" {
  description = "List of subnet CIDRs"
  type        = list(string)
  default     = ["10.0.1.0/24", "10.0.2.0/24"]
}

Validation

Block validation ภายใน variable ช่วยตรวจสอบค่าที่ถูกส่งเข้ามา หากไม่ผ่านเงื่อนไข Terraform จะ fail ตั้งแต่ขั้น plan ก่อนสร้าง resource จริง ลดโอกาส apply ค่าที่ผิด

variable "environment" {
  description = "Deployment environment"
  type        = string

  validation {
    condition     = contains(["dev", "stage", "prod"], var.environment)
    error_message = "environment ต้องเป็น dev, stage หรือ prod เท่านั้น"
  }
}

variable "port" {
  type = number
  validation {
    condition     = var.port > 0 && var.port < 65536
    error_message = "port ต้องอยู่ในช่วง 1-65535"
  }
}

Sensitive Variable

หาก variable เก็บข้อมูลลับ เช่น password, token, secret key ให้ใส่ sensitive = true Terraform จะไม่แสดงค่านี้ใน plan/apply output และ output ที่ใช้ค่านี้ต่อก็จะถูกซ่อนโดยอัตโนมัติ

variable "db_password" {
  description = "Database master password"
  type        = string
  sensitive   = true
}

วิธีส่งค่า Variable

Terraform รับค่า variable จากหลายแหล่งและมีลำดับความสำคัญชัดเจน แหล่งที่มาทีหลังจะทับค่าแหล่งก่อนหน้า

  • -var="name=value" ตอนรัน CLI
  • -var-file="custom.tfvars" ชี้ไฟล์ .tfvars แบบกำหนดเอง
  • ไฟล์ *.auto.tfvars และ *.auto.tfvars.json ที่โหลดอัตโนมัติ
  • ไฟล์ terraform.tfvars หรือ terraform.tfvars.json
  • Environment variable ชื่อ TF_VAR_ชื่อvar
  • Default value ใน block variable
# terraform.tfvars
region        = "us-east-1"
instance_count = 5
tags = {
  Owner       = "platform-team"
  CostCenter  = "R&D"
}

# รัน
terraform plan
terraform apply -var="region=ap-southeast-1"
terraform apply -var-file="prod.tfvars"
export TF_VAR_db_password="secret123"
terraform apply

Output Value

Output ใช้ export ค่าที่คำนวณได้หลัง apply ออกไปนอก module หรือไปแสดงที่ CLI เช่น IP address ของ EC2 ที่สร้างใหม่ endpoint ของ RDS หรือ URL ของ Load Balancer Output มีบทบาทสำคัญเมื่อ module ถูก consume โดย module อื่น เพราะเป็น interface ที่ caller อ่านได้

output "instance_public_ip" {
  description = "Public IP of the web instance"
  value       = aws_instance.web.public_ip
}

output "db_connection_string" {
  description = "Database connection string"
  value       = "postgres://${aws_db_instance.main.endpoint}/${var.db_name}"
  sensitive   = true
}

output "subnet_ids" {
  value = aws_subnet.private[*].id
}

รัน terraform output หรือ terraform output subnet_ids เพื่อดูค่า ส่วนใน CI/CD pipeline สามารถ terraform output -json แล้ว parse ต่อได้สะดวก

Input vs Output ต่างกันอย่างไร

  • Input variable = ค่าที่ ส่งเข้า configuration (ประกาศด้วย variable)
  • Output value = ค่าที่ ส่งออก จาก configuration (ประกาศด้วย output)
  • Input รู้ล่วงหน้า ก่อน apply / Output คำนวณได้หลัง apply
  • ทั้งคู่รองรับ description, sensitive, และ type
  • ใน module: input คือพารามิเตอร์ที่ caller ส่ง / output คือค่าที่ caller อ่านได้

สรุป

Variable และ Output ทำให้ configuration ของ Terraform เป็นสินค้าที่ขายต่อทีมอื่นได้ โดย input ช่วยให้ยืดหยุ่นต่อสภาพแวดล้อม และ output ช่วยให้ consumer ดึงค่าไปใช้ได้โดยไม่ต้องรู้ internal ของ module การประกาศ type constraint, validation, และ sensitive จะลดโอกาสผิดพลาดอย่างมาก เมื่อใช้คู่กับไฟล์ .tfvars ต่อ environment และ TF_VAR_ ใน CI/CD จะทำให้ pipeline จัดการ config ทั้งหมดอย่างเป็นระบบ ขั้นตอนถัดไปคือการรวบค่าที่คำนวณซ้ำ ๆ ไว้ใน local value เพื่อเขียน configuration ให้อ่านง่ายยิ่งขึ้น