Resource และ Data Source เป็นสองสิ่งที่ผู้ใช้ Terraform ต้องเข้าใจให้ดีที่สุด เพราะเป็นส่วนที่สร้างและอ่านข้อมูลจาก cloud provider แบบที่ทุก configuration ต้องใช้ Resource คือของที่ Terraform สร้าง ลบ และแก้ไขให้ ส่วน Data Source คือของที่มีอยู่แล้วและต้องการดึงข้อมูลมาใช้ ความเข้าใจผิดระหว่างสองสิ่งนี้มักนำไปสู่ปัญหา state drift และความพยายามสร้างของซ้ำที่มีอยู่แล้ว
บทความนี้จะอธิบาย lifecycle ของ resource, วิธีเขียน data source, การส่งต่อค่า attribute ระหว่างกัน และตัวอย่างใช้งานจริงที่ผสมทั้งสองสิ่งเข้าด้วยกัน
Resource คืออะไร
Resource block ระบุ infrastructure ที่ Terraform จะจัดการตลอด lifecycle ทั้งการสร้าง (create), อัพเดต (update), และลบ (destroy) ทุก resource ต้องระบุสองส่วน: resource type (เช่น aws_instance) และ local name ที่ไม่ซ้ำกันในโปรเจกต์
resource "aws_s3_bucket" "assets" {
bucket = "myapp-assets-prod"
tags = {
Environment = "production"
}
}
เมื่อรัน terraform apply Terraform จะเรียก API ของ provider เพื่อสร้าง resource ให้ตรงกับที่ระบุ และบันทึก state ลงไฟล์ tfstate เพื่อใช้เทียบ diff ครั้งถัดไป
Lifecycle ของ Resource
- Create — resource ไม่มีใน state → เรียก API สร้างใหม่
- Update in-place — resource มีอยู่ + argument ที่แก้ไม่กระทบ immutable field → PATCH
- Replace (destroy + create) — argument ที่แก้ต้อง recreate เช่น region ของ EC2
- Destroy — ลบ resource ออกจาก configuration หรือรัน
terraform destroy
Data Source คืออะไร
Data source block ดึงข้อมูลจาก provider โดยไม่สร้างหรือเปลี่ยนแปลงอะไร เหมาะกับกรณีที่ resource มีอยู่แล้ว เช่น ข้อมูลของ VPC ที่สร้างผ่าน Console, AMI ล่าสุดของ Ubuntu หรือ account ID ของ AWS ที่กำลังใช้
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
data "aws_vpc" "default" {
default = true
}
data "aws_caller_identity" "current" {}
ค่าที่ได้จาก data source จะถูก refresh ทุกครั้งที่รัน plan/apply ทำให้ configuration ตอบสนองต่อการเปลี่ยนแปลงภายนอกได้ เช่น เมื่อ Ubuntu ออก image ใหม่ resource ที่ใช้ data.aws_ami.ubuntu.id จะชี้ไป image ใหม่ทันที (พร้อม recreate instance ถ้าจำเป็น)
การส่งต่อค่า Attribute
ค่า attribute ที่ resource/data source ให้ สามารถอ้างอิงไปยัง resource อื่นได้ในรูปแบบ type.name.attribute วิธีนี้ทำให้ Terraform สร้าง dependency graph อัตโนมัติและรู้ว่าต้องสร้างตัวไหนก่อน-หลัง
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
}
ในตัวอย่าง AWS instance อ้างอิงทั้ง data source และ resource อื่น Terraform จะรู้เองว่าต้องสร้าง security group ก่อน จากนั้นค่อยสร้าง EC2
Meta-argument ที่ใช้บ่อย
# สร้างหลาย resource ด้วย count
resource "aws_instance" "worker" {
count = 3
ami = data.aws_ami.ubuntu.id
instance_type = "t3.small"
tags = {
Name = "worker-${count.index + 1}"
}
}
# สร้าง resource ตาม map ด้วย for_each
resource "aws_s3_bucket" "by_env" {
for_each = toset(["dev", "stage", "prod"])
bucket = "logs-${each.key}"
}
Lifecycle Block
lifecycle block ภายใน resource ใช้ควบคุมพฤติกรรมพิเศษ เช่น ห้ามลบโดยเด็ดขาดหรือสร้างของใหม่ก่อนลบของเก่าเพื่อ zero-downtime
resource "aws_db_instance" "main" {
# ... config ...
lifecycle {
prevent_destroy = true
create_before_destroy = false
ignore_changes = [password]
}
}
ข้อแตกต่างสำคัญ
- Resource = Terraform manage (สร้าง/ลบ/อัพเดต) / Data source = อ่านอย่างเดียว
- Resource ขึ้นต่อ provider credentials + write permission / Data source ต้องการเพียง read permission
- Destroy ลบ resource ออกจาก cloud แต่ data source จะไม่ลบอะไรเลย
- Import ใช้ได้เฉพาะ resource — data source ไม่ต้อง import
- หากต้องการ reference ของที่ทีมอื่นสร้าง → ใช้ data source หรือ
terraform_remote_state
สรุป
Resource คือหัวใจของ Terraform ใช้สร้างและจัดการ infrastructure ตลอด lifecycle ส่วน Data Source ใช้อ่านข้อมูลที่มีอยู่แล้วโดยไม่แตะต้อง สองสิ่งนี้ทำงานร่วมกันผ่านการส่งต่อ attribute ซึ่ง Terraform จะคำนวณ dependency graph ให้โดยอัตโนมัติ การใช้ meta-argument count, for_each, และ lifecycle จะช่วยให้ configuration ยืดหยุ่นและปลอดภัยขึ้น ต่อไปควรเรียนรู้เรื่อง variable และ output เพื่อทำให้ module สามารถนำกลับมาใช้ในหลาย environment ได้

