การติดตั้ง Terraform และโครงสร้างโปรเจกต์
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”ติดตั้ง Terraform ผ่าน version manager แทนที่จะ pin binary เดียวไว้ตายตัว เขียน GCP resource จริงตัวแรก แล้วแยกออกเป็นไฟล์หลายไฟล์ตามชื่อที่เป็นธรรมเนียม — ซึ่งจริง ๆ แล้ว Terraform ไม่ได้บังคับสักไฟล์เดียว
ติดตั้ง Terraform (และทำไม version manager ดีกว่า binary เดียว)
หัวข้อที่มีชื่อว่า “ติดตั้ง Terraform (และทำไม version manager ดีกว่า binary เดียว)”คุณดาวน์โหลด Terraform binary เดียวจาก HashiCorp มาวางไว้ใน PATH ได้ และก็ใช้งานได้ดีถ้าทดลองอยู่คนเดียวโปรเจกต์เดียว แต่พอต้องทำงานมากกว่าหนึ่งโปรเจกต์ วิธีนี้จะเริ่มมีปัญหา repository หนึ่งเขียนไว้กับ Terraform 1.5 อีกอันต้องการ feature ของ 1.9 ส่วนอีกอันย้ายไป OpenTofu แล้ว การคอยสลับ binary กลาง (global) ตัวเดียวให้ตรงกับโปรเจกต์ที่กำลังทำอยู่ตลอดเวลาเป็นงานที่น่าเบื่อและพลาดง่าย
version manager แก้ปัญหานี้ด้วยการติดตั้งหลายเวอร์ชันเก็บไว้คู่กัน แล้วสลับใช้ทีละโปรเจกต์ โดยมักอิงจาก version file ที่ commit ไว้ใน repository tenv เป็นตัวเลือกที่ดีเพราะไม่ได้ผูกกับเครื่องมือตัวเดียว จัดการ Terraform, OpenTofu, Terragrunt และ Atmos ได้จาก CLI ตัวเดียว ซึ่งสำคัญมากเมื่อทีมของคุณใช้ Terragrunt คู่กับ Terraform (จะพูดถึงในคอร์สนี้ตอนหลัง)
# install tenv (macOS, via Homebrew)brew install tenv
# install and pin a specific Terraform version for the current directorytenv tf install 1.9.0tenv tf use 1.9.0
# tenv reads a .terraform-version file (or the terraform block's# required_version constraint) to pick the right version automaticallyterraform versionไม่ว่าจะติดตั้งด้วยวิธีไหน คำสั่งที่ใช้งานกับ terraform binary ในแต่ละวันก็เหมือนกันทุกประการ — version manager เปลี่ยนแค่วิธีที่ binary นั้นมาอยู่บนเครื่องคุณเท่านั้น
โปรเจกต์ GCP จริงขนาดเล็กที่สุด
หัวข้อที่มีชื่อว่า “โปรเจกต์ GCP จริงขนาดเล็กที่สุด”โปรเจกต์ Terraform ก็แค่ directory ที่มีไฟล์ .tf หนึ่งไฟล์หรือมากกว่า ตัวที่เล็กที่สุดแต่ใช้งานได้จริงประกาศ provider กับ resource เดียว
terraform { required_version = ">= 1.7.0"
required_providers { google = { source = "hashicorp/google" version = "~> 6.0" } }}
provider "google" { project = "acme-app" region = "us-central1"}
resource "google_storage_bucket" "reports" { name = "acme-monthly-reports" location = "US"}นี่คือ configuration ที่สมบูรณ์และ apply ได้ทันที ไม่ต้องมี VPC ไม่ต้องมี networking ไม่มีข้อกำหนดล่วงหน้าอะไรเลยนอกจาก GCP credentials ในเครื่อง (Application Default Credentials ตั้งค่าด้วย gcloud auth application-default login) google_storage_bucket เป็น resource แรกที่ดีด้วยเหตุผลนี้เป๊ะ ๆ เพราะไม่ต้องพึ่งอะไรที่ต้องมีอยู่ก่อนเลย
การจัดไฟล์ตามธรรมเนียม
หัวข้อที่มีชื่อว่า “การจัดไฟล์ตามธรรมเนียม”โปรเจกต์จริงจะแยกไฟล์เดียวนั้นออกเป็นหลายไฟล์ ตามธรรมเนียมที่ใช้กัน
terraform { required_version = ">= 1.7.0"
required_providers { google = { source = "hashicorp/google" version = "~> 6.0" } }}
provider "google" { project = "acme-app" region = "us-central1"}variable "bucket_name" { description = "Name of the storage bucket to create" type = string default = "acme-monthly-reports"}resource "google_storage_bucket" "reports" { name = var.bucket_name location = "US"}output "bucket_self_link" { description = "Self link of the created storage bucket" value = google_storage_bucket.reports.self_link}ชื่อ main.tf, variables.tf, outputs.tf, providers.tf และ versions.tf เป็น ธรรมเนียม ไม่ใช่ข้อบังคับของ Terraform Terraform ไม่ได้สนใจชื่อไฟล์เลยแม้แต่น้อย แต่โหลดทุกไฟล์ .tf ใน directory มารวมกันเป็น configuration เดียว และไม่สนว่าคุณจะตั้งชื่อไฟล์ว่า main.tf, zzz.tf หรือ whatever.tf resource ที่อยู่ใน outputs.tf กับ variable ที่อ้างอิงถึงใน main.tf ทำงานเหมือนกันเป๊ะกับตอนที่ทุกอย่างอยู่ในไฟล์เดียว Terraform สร้าง dependency graph แบบเดียวกันเสมอไม่ว่าจะแยกไฟล์แบบไหน การแยกตามหน้าที่ (providers, variables, resources, outputs) มีไว้เพื่อให้คนที่เปิด directory ขึ้นมารู้ว่าต้องไปดูตรงไหนเท่านั้น ไม่ใช่เพราะ Terraform ต้องการเส้นแบ่งนั้น
flowchart TB
subgraph dir["Project directory"]
main["main.tf"]
vars["variables.tf"]
outs["outputs.tf"]
prov["providers.tf"]
ver["versions.tf"]
end
main --> combined["One combined Terraform configuration"]
vars --> combined
outs --> combined
prov --> combined
ver --> combined สุขอนามัยวันแรก ๆ ได้แก่ fmt กับ validate
หัวข้อที่มีชื่อว่า “สุขอนามัยวันแรก ๆ ได้แก่ fmt กับ validate”มีสองคำสั่งที่ควรรันก่อน commit ทุกครั้ง และรันบ่อยแค่ไหนก็ไม่มีต้นทุนอะไรเลย
terraform fmt เขียนไฟล์ทับในที่เดิมให้เป็น formatting มาตรฐานของ Terraform เช่น indentation ที่สม่ำเสมอ เครื่องหมาย = ที่จัดตำแหน่งตรงกัน spacing ที่สม่ำเสมอ แก้แค่ style ไม่ได้แก้ logic
terraform fmtterraform validate ตรวจว่า configuration สอดคล้องกันเองภายใน เช่น HCL syntax ที่ถูกต้อง argument ที่ type ตรง และ variable ที่ถูกอ้างอิงถึงมีอยู่จริง จับ typo และความผิดพลาดเชิงโครงสร้างได้ตั้งแต่ก่อนที่จะติดต่อ GCP ด้วยซ้ำ
terraform validateSuccess! The configuration is valid.สิ่งที่ validate ไม่ได้ทำ คือตรวจสอบอะไรกับ GCP API จริง validate ไม่รู้เลยว่าโปรเจกต์ของคุณชน service quota หรือยัง ชื่อ bucket ที่คุณเลือกถูกใช้ไปแล้วทั่วโลกหรือเปล่า หรือ service account ของคุณมีสิทธิ์สร้าง resource นั้นจริงไหม ทั้งหมดนี้จะโผล่มาให้เห็นตอนรัน terraform plan เท่านั้น เพราะเป็นคำสั่งแรกที่ติดต่อกับ GCP จริง ๆ configuration ที่ผ่าน validate ได้อย่างสมบูรณ์ก็ยังพังตอน plan หรือ apply ได้อยู่ดี