ถ้าเรามี resource หรือ service อยู่บน cloud อยู่แล้ว เราจะเอามันมาใส่ใน tf script ยังไงดีนะ?


Import resource ที่มี

ในชีวิตจริง เราคงมีเคสที่ว่า เรามี resource อะไรๆ อยู่บน cloud อยู่ก่อนแล้ว สร้างไว้นานแล้ว และเราต้องการจะใช้ Terraform เข้ามาจัดการ เราสามารถดึงมันเข้ามาใน Terraform ได้ฮะ เรียกวิธีการนี้ว่า Terraform import

Terraform import มีความสามารถ import state ของ resource เข้าไปใน state file ได้เท่านั้นนะฮะ หมายความว่าเราต้องแก้ไข tf script ให้มี resource นั้นเองตามหลังด้วยนะฮะ ไม่อย่างนั้น เวลา apply ก็จะเท่ากับเราลบ resource นั้นทิ้งไปอยู่ดี

เอาว่า เรามี bucket สองตัวที่เราสร้างเองแบบ manual แบบนี้

ทีนี้ การ import จะแตกต่างกันนิดหน่อย ขึ้นอยู่กับว่าเราออกแบบ tf script ไว้แบบไหนฮะ

ถ้า resource อยู่ใน main scripts

If we go easy by writting resources in a single main folder, now we can do the following steps.

ถ้าเราเขียนให้ resource อยู่ใน main script ก็จะ import โดยอ้างอิง main script ทีนี้ เรามีขั้นตอนตามนี้ฮะ

  1. สร้าง resource เปล่าๆ
  2. Import state ให้อ้างอิง resource เปล่าๆ
  3. แสดง state แล้วปรับค่าใน tf script
  4. ตรวจสอบผล tf script

1. สร้าง resource เปล่าๆ

2. Import state ให้อ้างอิง resource เปล่าๆ

ใช้ command นี้

terraform import -var-file="<var-file>" \
    '<resource_type>.<resource_name>' <resource_address>

var-file สำหรับกรณีที่เราใช้ตัวแปรใน script ฮะ ถ้าไม่ระบุแบบนี้ ตอน run คำสั่งจริง Terraform ก็จะเด้งมาถามค่าอยู่ดีฮะ

Resource type และ name คือ resource ที่เรากำหนดใน tf script

Resource address อันนี้ให้อ้างอิง format ใน Terraform registry เอาฮะ อย่างกรณีนี้ GCS bucket จะเป็นไปตามรูปแบบที่กำหนดในเอกสารตามลิงก์นี้ ซึ่งก็คือ <project_name>/<bucket_name> หรือเอาแค่ bucket_name ก็ได้เหมือนกันฮะ

3. แสดง state แล้วปรับค่าใน tf script

พอ import เสร็จ ก็ลอง list ดูและเปิด configuration

terraform state list
terraform state show <state_name>

จากนั้นให้ copy config มาใส่ที่ resource block แบบนี้

4. ตรวจสอบผล tf script

สุดท้าย เราจะลอง plan ว่า state และ script ตอนนี้ตรงกันแล้ว

อ้าว! ทำผิดตรงไหนสักแห่งล่ะ พออ่านดีๆ พบว่า มันมี unconfigurable attributes หรือค่าที่ไม่ให้กำหนด ก็คือ self_link กับ url ส่วน id นั้นเป็น invalid key

ทั้งสามตัวไม่ใช่ค่าที่เราจะกำหนดใน resource block ได้ ดังนั้นให้เอามันออกไปฮะ

ลอง plan อีกทีนึง ก็ผ่านแล้ว

ถ้า resource อยู่ใน module

1. สร้าง resource เปล่าๆ

สร้าง module resource เปล่าๆ แบบนี้

แล้วอ้างอิง module ไว้ที่ main script แบบนี้

2. Import state ให้อ้างอิง resource เปล่าๆ

พอเป็น module เราจะเขียนต่างไปจากเดิมนิดนึง แบบนี้ฮะ

terraform import -var-file="<var-file>" \
    'module.<module_name>.<resource_type>.<resource_name>' <resource_address>

ตรงนี้เนี่ย อธิบายได้ว่า

  • module_name ใช้ "bucket" ตามที่กำหนดใน main script
  • resource_type ใช้ "google_cloud_storage_bucket" เพราะเราเขียนไว้ใน module
  • resource_name ใช้ "bucket_in_module" เพราะเราตั้งชื่อไว้ใน module

ถ้าไม่มีอะไรผิดพลาด เราจะเห็น output ว่า import จาก module ชื่อนี้ สำเร็จแล้วนะ

แล้วก็ทำเหมือนเดิมฮะ list state แล้วดู config ไปใส่ใน block ของ module

3. แสดง state แล้วปรับค่าใน tf script

ดู config ด้วยคำสั่ง

terraform state list
terraform state show <state_name>

แล้วเอาไปวางใน resource block ใน module

4. ตรวจสอบผล tf script

แล้วสุดท้ายก็ plan ให้แน่ใจว่า "no change" ก็เป็นอันเสร็จพิธีฮะ


Update state

ที่เล่ามาข้างต้นคือ วิธี import resource ที่ไม่เคยอยู่ใน state มาก่อน แต่ถ้ามันอยู่ใน state อยู่แล้ว และมี change อะไรสักอย่างกับ resource นั้น แต่ไม่ได้ทำผ่าน Terraform ล่ะ

สมมติว่า เราเปลี่ยน bucket class จาก "STANDARD" ให้เป็น "NEARLINE" แบบนี้

เปลี่ยนแค่ bucket "bluebirz-manual-create-bucket-for_main" อันเดียว

เพราะว่าเรามี state ของ resource นี้อยู่แล้ว ชีวิตง่ายเลยแค่สั่ง update แบบนี้

terraform apply -var-file="<var-file>" -refresh-only -auto-approve

Terraform ก็จะไปดึงมาเทียบแล้วปรับ config ให้เท่ากันระหว่าง state file และบน cloud

ง่ายๆ แค่นี้แหละฮะ เราก็ได้ state ที่ใหม่ล่าสุดแล้ว และอย่าลืมไปแก้ที่ script ด้วยล่ะ ให้มันตรงกันกับ state ฮะ