ข้ามไปยังเนื้อหา

Drift Detection and Import

Configuration drift คือสิ่งที่เกิดขึ้นเมื่อของจริงเบี่ยงเบนไปจาก Terraform config โดยที่ Terraform ไม่รู้ตัว และ terraform import (หรือ declarative import block) คือวิธีที่คุณเอา resource ที่มีอยู่แล้วเข้ามาอยู่ภายใต้การจัดการของ Terraform ตั้งแต่แรก

Drift เกิดขึ้นทุกครั้งที่มีใครหรืออะไรก็ตามเปลี่ยน resource นอกเหนือจาก Terraform เคสที่เจอบ่อยบน Google Cloud วิศวกรเปิด Console ตอนรีบ ๆ แล้วแก้ label ของ Compute Engine instance ตรง ๆ หรือ resize machine type โดยไม่แตะ Terraform configuration ที่ควรจะจัดการ instance นั้นอยู่ state ที่ Terraform บันทึกไว้ตอนนี้ไม่ตรงกับ object จริงแล้ว

ไม่มีอะไรแจ้งเตือนคุณเรื่องนี้อัตโนมัติ ครั้งถัดไปที่มีคนรัน

Terminal window
terraform plan

Terraform จะ refresh มุมมองของ resource จริง เทียบกับทั้ง configuration และ state ล่าสุดที่บันทึกไว้ แล้วโชว์ความต่างออกมาเป็น plan — ปกติจะเป็น “in-place update” ที่จะ revert การเปลี่ยนแปลงที่ทำมือกลับไปเป็นแบบที่ configuration บอกไว้ เพราะ Terraform ถือว่า configuration คือ source of truth

บางครั้งคุณอยากได้ผลตรงข้าม คือยอมรับการเปลี่ยนแปลงที่ทำมือ แล้วแค่ทำให้ state ของ Terraform sync กลับมาตรงกับความเป็นจริง โดยไม่แตะ infrastructure จริงเลย นั่นคือหน้าที่ของ -refresh-only mode

Terminal window
terraform apply -refresh-only

คำสั่งนี้ update แค่ attribute ที่บันทึกไว้ใน state file ให้ตรงกับสิ่งที่รันอยู่จริงบน GCP ตอนนี้ ไม่สร้าง update หรือ destroy resource จริงตัวไหนเลย — เป็นการ reconcile ความจำของ Terraform กับโลกจริงล้วน ๆ

เอา resource ที่มีอยู่แล้วเข้ามาอยู่ภายใต้การจัดการด้วย terraform import

หัวข้อที่มีชื่อว่า “เอา resource ที่มีอยู่แล้วเข้ามาอยู่ภายใต้การจัดการด้วย terraform import”

GCP resource จำนวนมากถูกสร้างขึ้นก่อนที่ Terraform จะมีตัวตนใน project ด้วยซ้ำ — Compute Engine instance ที่ใครสักคนคลิกสร้างใน Console, Cloud Storage bucket ที่สร้างจาก script ยุคแรก คำสั่ง CLI terraform import เชื่อม resource ที่มีอยู่แล้วจริงตัวหนึ่งเข้ากับ resource address ที่เขียนไว้ใน configuration ของคุณแล้ว

Terminal window
terraform import google_compute_instance.web projects/my-project-id/zones/us-central1-a/instances/web-vm

จุดที่พลาดง่ายคือเรื่องนี้ terraform import เติมแค่ state entry เท่านั้น ไม่เขียน HCL ให้คุณเลย คุณยังต้องเขียน resource "google_compute_instance" "web" { ... } block เองด้วยมือ ให้ attribute ใกล้เคียงกับ object จริงพอที่ plan ครั้งถัดไปจะไม่ขึ้น diff ใหญ่ ๆ ถ้า config ผิด apply ครั้งถัดไปของคุณอาจพยายาม “fix” resource ให้ตรงกับ config ที่ไม่เคยอธิบาย resource นั้นจริง ๆ

Terraform 1.5 เพิ่มวิธี import แบบที่สอง ซึ่งตอนนี้หลายทีมชอบมากกว่าสำหรับอะไรก็ตามที่ควรค่าแก่การ review นั่นคือ import block ที่เขียนตรงในไฟล์ .tf คู่กับ configuration ตัวอื่น ๆ

import {
to = google_compute_instance.web
id = "projects/my-project-id/zones/us-central1-a/instances/web-vm"
}

ข้อดีเทียบกับคำสั่ง CLI อยู่ที่เรื่อง review ล้วน ๆ การรัน terraform import ผ่าน CLI คือ side effect ทันที — เพราะเขียนลง state ทันทีที่คุณรัน โดยไม่มีขั้นตอน preview เลย ในทางกลับกัน import block ก็แค่ configuration อีกชิ้นหนึ่ง ที่ขึ้นในผลลัพธ์ของ terraform plan เป็น planned import เหมือนกับ planned create หรือ update ทำให้เพื่อนร่วมทีม review ได้ใน pull request ก่อนที่ใครจะรัน apply Terraform ยังสร้าง resource block เริ่มต้นให้คุณได้จาก attribute ของ object จริง โดยใช้ terraform plan -generate-config-out=generated.tf แล้วคุณค่อย review และจัดให้เรียบร้อยแทนที่จะเขียนจากศูนย์

terraform state subcommand อีกสองตัวปิดท้ายบทนี้ และทั้งคู่เป็นภาพสะท้อนกลับกัน

Terminal window
# Rename or move a resource's address in state, e.g. after refactoring into a module
terraform state mv google_compute_instance.web module.app.google_compute_instance.web
# Stop tracking a resource in state, without destroying the real object
terraform state rm google_compute_instance.web

terraform state mv เปลี่ยนแค่ address ที่ Terraform ใช้อ้างอิง resource ใน state — มีประโยชน์ตอน restructure configuration เข้า module แล้วไม่อยากให้ Terraform คิดว่าต้อง destroy address เก่าแล้วสร้างใหม่ terraform state rm ตรงข้ามกับ import เลย โดยลบ entry ของ resource ออกจาก state ทำให้ Terraform เลิกจัดการ แต่ไม่มีผลอะไรกับ GCP object จริงเลย object นั้นยังรันต่อไปเหมือนเดิมทุกอย่าง แค่ตอนนี้ไม่มีใครจัดการแล้ว

flowchart LR
  manual["Instance label edited in Console"] --> plan["terraform plan"]
  plan -->|"diff shown"| drift["Drift detected"]
  existing["Existing untracked GCP resource"] --> importBlock["import block or terraform import"]
  importBlock -->|"state entry created"| state["terraform.tfstate"]
A manually changed resource surfaces as drift on plan, while an existing untracked resource is brought in with import
เพื่อนร่วมทีมแก้ Compute Engine instance ด้วยมือใน GCP Console Terraform โชว์ configuration drift นี้ออกมายังไง
ความต่างสำคัญระหว่างคำสั่ง CLI terraform import กับ declarative import block คืออะไร
terraform state rm ทำอะไรกับ GCP resource จริงที่ชี้ไปหา
terraform apply -refresh-only ทำอะไร