Providers & resources
สิ่งที่จะสร้าง
หัวข้อที่มีชื่อว่า “สิ่งที่จะสร้าง”resource เล็ก ๆ หนึ่งตัวบนแต่ละ cloud ด้วย Terraform ล้วน ๆ — ยังไม่มี Terragrunt S3 bucket บน AWS, Cloud Storage bucket บน GCP, และ resource group บน Azure แต่ละตัวเกือบฟรี, destroy ได้เร็ว, และเพียงพอจะซ้อมทั้ง loop: ประกาศ provider, ประกาศ resource, แล้ว init, plan, และ apply
เราใช้ Terraform เปล่า ๆ โดยตั้งใจ การได้เห็น provider block เกือบเหมือนกันสามชุดด้วยมือคือสิ่งที่ทำให้ Terragrunt ใน Module 3 รู้สึกเหมือนโล่งอกมากกว่าจะเป็นเรื่องลึกลับ
provider คือ plugin ที่แปล HCL ให้เป็น API ของ cloud — aws, google, azurerm คุณ pin version ไว้ใน block required_providers เพื่อให้เพื่อนร่วมงาน (หรือ CI run อีกหกเดือนข้างหน้า) ได้พฤติกรรมเดียวกับคุณ แล้วตั้งค่า (region, project, subscription) ใน block provider resource คือสิ่งหนึ่งที่ provider นั้นจัดการ: bucket, network, database
loop init/plan/apply คือหัวใจที่เต้นของ Terraform init download provider ที่ pin ไว้; plan diff config ของคุณกับความจริงแล้วแสดงว่าอะไรจะเปลี่ยนบ้าง; apply ลงมือทำให้เกิดขึ้นจริง คุณจะรัน loop นั้นเป็นพัน ๆ ครั้ง — หัดเชื่อ plan และอย่า apply plan ที่คุณยังไม่ได้อ่าน
ข้อดีข้อเสีย
หัวข้อที่มีชื่อว่า “ข้อดีข้อเสีย”pin provider ด้วย ~> เทียบกับเอาอะไรก็ตามที่ใหม่สุด:
- Pros: reproducible ได้
~> 6.0รับ bug fix 6.x แต่ไม่รับ breaking 7.0applyจึงทำสิ่งเดียวกันบนทุกเครื่องและใน CI - Cons: คุณเป็นเจ้าของการ upgrade เอง ดัน major หมายถึงต้องอ่าน upgrade guide แล้ว test ใหม่ — แต่ provider ที่ไม่ pin แล้วกระโดดข้าม major เงียบ ๆ แย่กว่ามาก
Terraform เปล่าต่อ cloud (บทนี้) เทียบกับกระโดดไป Terragrunt เลย:
- Pros: คุณเรียน engine ตรง ๆ — provider, state, plan — โดยไม่มีอะไรคั่นระหว่างคุณกับ API เมื่อมีอะไรพังทีหลัง คุณรู้ว่าต้องโทษชั้นไหน
- Cons: ซ้ำซาก — provider block สามชุด, backend สามชุด, setting เดียวกันสามสำเนา ความซ้ำนั้นคือปัญหาที่ Terragrunt มีอยู่เพื่อแก้ และคุณจะรู้สึกได้ตอนจบ module นี้
ติดตั้ง
หัวข้อที่มีชื่อว่า “ติดตั้ง”พวกนี้เป็นการทดลองใช้แล้วทิ้ง เก็บไว้นอก tree จริง — directory scratch/ ที่คุณจะลบก่อน Module 4 สร้าง module จริง
1. scratch/aws/main.tf — the AWS provider + an S3 bucket
หัวข้อที่มีชื่อว่า “1. scratch/aws/main.tf — the AWS provider + an S3 bucket”ชื่อ bucket unique ทั้งโลก เราจึงต่อท้ายด้วย random string:
terraform { required_providers { aws = { source = "hashicorp/aws", version = "~> 6.0" } random = { source = "hashicorp/random", version = "~> 3.6" } }}
provider "aws" { region = "us-east-1"}
resource "random_id" "suffix" { byte_length = 4}
resource "aws_s3_bucket" "first" { bucket = "clouddeploy-first-${random_id.suffix.hex}"}2. scratch/gcp/main.tf — the Google provider + a Cloud Storage bucket
หัวข้อที่มีชื่อว่า “2. scratch/gcp/main.tf — the Google provider + a Cloud Storage bucket”terraform { required_providers { google = { source = "hashicorp/google", version = "~> 6.0" } random = { source = "hashicorp/random", version = "~> 3.6" } }}
provider "google" { project = "YOUR_GCP_PROJECT_ID" region = "us-central1"}
resource "random_id" "suffix" { byte_length = 4}
resource "google_storage_bucket" "first" { name = "clouddeploy-first-${random_id.suffix.hex}" location = "US-CENTRAL1" uniform_bucket_level_access = true force_destroy = true}3. scratch/azure/main.tf — the azurerm provider + a resource group
หัวข้อที่มีชื่อว่า “3. scratch/azure/main.tf — the azurerm provider + a resource group”azurerm provider v4 ต้องมี block features {} และ subscription provider อ่าน ARM_SUBSCRIPTION_ID จาก environment คุณจึงไม่ต้อง hardcode ไว้:
terraform { required_providers { azurerm = { source = "hashicorp/azurerm", version = "~> 4.0" } }}
provider "azurerm" { features {} # subscription_id comes from the ARM_SUBSCRIPTION_ID env var, # or set it here explicitly.}
resource "azurerm_resource_group" "first" { name = "clouddeploy-first" location = "eastus"}export ARM_SUBSCRIPTION_ID="$(az account show --query id -o tsv)"ทุกตัวในนี้ authenticate ด้วย CLI credential ที่คุณตั้งไว้ใน Module 1 — ไม่มี key ในไฟล์พวกนี้เลย
ตรวจสอบผล
หัวข้อที่มีชื่อว่า “ตรวจสอบผล”รัน loop ในแต่ละ directory แสดง AWS ไว้; GCP และ Azure ใช้คำสั่งเหมือนกัน:
cd scratch/awsterraform init # Terraform has been successfully initialized!terraform plan # Plan: 2 to add, 0 to change, 0 to destroy.terraform apply # type 'yes' when promptedapply ที่สำเร็จจบด้วย:
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.ตอนนี้ยืนยันว่า resource มีอยู่จริง ตรงจาก cloud CLIs:
aws s3 ls | grep clouddeploy-firstgcloud storage buckets list --filter="name:clouddeploy-first"az group show --name clouddeploy-first --query name -o tsvแล้วรื้อทิ้งเพื่อไม่ให้อะไรค้างบนบิล:
terraform destroy # Destroy complete! Resources: 2 destroyed.คุณเสร็จเมื่อทั้งสาม cloud ไปจาก plan → apply → resource ที่ CLI ยืนยันแล้ว → destroy สะอาด
ตรวจสอบความเข้าใจ:
- block
required_providersกับ blockproviderต่างกันอย่างไร? - ทำไม
~> 6.0ถึงปกป้องคุณในจุดที่ “ใช้ตัวล่าสุด” เปล่า ๆ จะกัดคุณในที่สุด? terraform planแสดงอะไรให้คุณ และทำไมคุณไม่ควรapplyplan ที่คุณยังไม่ได้อ่าน?- ไม่มีไฟล์ไหนในนี้เก็บ credential ของ cloud เลย — แล้วแต่ละ provider authenticate อย่างไร?
คุณตั้งค่า provider ทั้งสามแล้ว, สร้างและ destroy resource ตัวแรกบนแต่ละ cloud, และรัน loop init/plan/apply คุณยังได้รู้สึกถึงความซ้ำ — provider block สามชุดที่ต่างกันแค่รายละเอียด ก่อนเราจะแก้ด้วย Terragrunt เราต้องเข้าใจสิ่งที่ Terraform จำไว้ระหว่าง apply แต่ละครั้งก่อน: state ต่อไปคือ state, variable, และ output