The State File
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”state ของ Terraform คือไฟล์ JSON ที่แมป resource แต่ละตัวใน configuration ของคุณเข้ากับ object จริงที่สร้างขึ้น และเป็นสิ่งเดียวที่ทำให้ Terraform รู้ว่าตัวเองจัดการอะไรอยู่
ทำไม Terraform ถึงต้องมี state
หัวข้อที่มีชื่อว่า “ทำไม Terraform ถึงต้องมี state”ตอนคุณรัน terraform apply Terraform สร้าง resource ผ่าน Azure Resource Manager API — virtual machine, storage account, resource group สักตัว Azure API ไม่มีแนวคิดเรื่อง “Terraform” เลย ถ้า subscription ของคุณมี virtual machine 500 ตัว Azure API ก็บอกไม่ได้ว่า VM ตัวไหนคือตัวที่ Terraform สร้างให้กับ resource "azurerm_linux_virtual_machine" "web" block นี้ ตัวไหนที่เพื่อนร่วมทีมสร้างมือใน Azure Portal และตัวไหนเป็นของ Terraform configuration อื่นไปเลย
Terraform แก้ปัญหานี้ด้วยการเก็บ record ของตัวเอง ทุกครั้งที่ apply Terraform จะเขียน mapping ระหว่าง resource address ใน configuration กับ ID จริงของ infrastructure ลงไฟล์ state ที่ชื่อ default คือ terraform.tfstate
resource "azurerm_linux_virtual_machine" "web" { name = "web-vm" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location size = "Standard_B2s" admin_username = "azureuser"
network_interface_ids = [ azurerm_network_interface.web.id, ]}หลัง apply ไฟล์ state จะบันทึกประมาณว่า azurerm_linux_virtual_machine.web -> /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-resources/providers/Microsoft.Compute/virtualMachines/web-vm path ยาว ๆ นั้นแหละคือหน้าตาจริงของ “real-world object” บน Azure — ทุก resource ถูกระบุด้วย resource ID แบบเต็มที่เข้ารหัส subscription, resource group, provider namespace และชื่อ resource ไว้ในสตริงเดียว นอกจาก ID แล้ว state ยัง cache ทุก attribute ที่ Azure ส่งกลับมาสำหรับ VM ตัวนั้นด้วย — private IP, OS disk ID, network interface ที่ผูกอยู่ และอื่น ๆ ข้อมูลที่ cache ไว้นี้ทำให้ Terraform ไม่ต้องเรียก Azure API แล้ว describe resource ทุกตัวใหม่แค่เพื่อคำนวณ plan เพราะเทียบ configuration กับ state ล่าสุดก่อนได้เลย นี่คือทั้งวิธีตรวจจับว่าอะไรเปลี่ยน และเป็นข้อดีด้าน performance จริง ๆ กับ configuration ขนาดใหญ่ state ยังเป็นที่เก็บ dependency metadata ด้วย ทำให้ Terraform รู้ลำดับที่ต้องสร้าง แก้ไข หรือลบ resource
ความเสี่ยงของ plaintext ที่ซ่อนอยู่ใน state file
หัวข้อที่มีชื่อว่า “ความเสี่ยงของ plaintext ที่ซ่อนอยู่ใน state file”state file ไม่ได้ผ่านการ sanitize attribute value ใด ๆ ที่ Terraform ได้รับจาก provider — หรือที่คุณตั้งตรงใน configuration — จะไปอยู่ใน terraform.tfstate แบบ plaintext รวมถึงค่าที่เป็น secret ด้วย ตัวอย่างที่เจอบ่อย
resource "azurerm_postgresql_flexible_server" "main" { name = "app-db" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location version = "15" sku_name = "GP_Standard_D2s_v3" administrator_login = "appadmin" administrator_password = "S3cretPassword!"}คุณไม่เคยรัน terraform output กับ password ตัวนั้น และไม่เคย print ใน log แต่ค่านั้นก็ยังไปอยู่ใน terraform.tfstate แบบ plaintext อยู่ดี เพราะ Terraform ต้องจำ attribute ทุกตัวที่ตั้งไว้ เพื่อตรวจ drift ตอน plan ครั้งถัดไป ใครก็ตามที่อ่านไฟล์นั้นได้ก็อ่าน password ได้ด้วย
นี่คือเหตุผลที่ terraform.tfstate ห้าม commit เข้า version control เด็ดขาด ใส่ลง .gitignore ตั้งแต่ commit แรกของโปรเจกต์ใหม่
terraform.tfstateterraform.tfstate.backup*.tfstate*.tfstate.*บทถัดไปจะพูดถึงการย้าย state ออกจาก laptop ไปไว้บน remote backend ที่ควบคุม access ได้จริง นั่นคือวิธีแก้ความเสี่ยงนี้ที่แท้จริง — .gitignore แค่กันอุบัติเหตุที่เห็นชัดที่สุดเท่านั้น
การดู state
หัวข้อที่มีชื่อว่า “การดู state”Terraform มีคำสั่งสามตัวสำหรับดูข้างใน state โดยไม่ต้องเปิดไฟล์ JSON เอง
# Human-readable dump of the entire current stateterraform show
# List every resource address currently tracked in stateterraform state list
# Show full attribute detail for one specific resourceterraform state show azurerm_linux_virtual_machine.webterraform state list คือวิธีเร็วที่สุดในการตอบคำถามว่า “configuration นี้จัดการอะไรอยู่ตอนนี้” คำสั่งนี้ print address อย่าง azurerm_linux_virtual_machine.web หรือ module.network.azurerm_virtual_network.main โดยไม่มีรายละเอียดอื่น terraform state show <address> จะลงลึกไป address ใดตัวหนึ่ง แล้ว print attribute ทุกตัวที่ Terraform บันทึกไว้ ซึ่งบ่อยครั้งเร็วกว่าการไปเปิด Azure Portal เวลาต้องเช็คค่าที่ Terraform track อยู่
flowchart LR cfg["Terraform config (.tf files)"] --> apply["terraform apply"] apply --> azure["Real Azure resources"] azure -->|attributes written back| state["terraform.tfstate"] cfg -->|next run| plan["terraform plan"] state -->|read for comparison| plan