terraform plan
terraform plan= preview การเปลี่ยนแปลงก่อน apply จริง — สำคัญที่สุดของ workflow
Plan ทำอะไร?
- อ่าน config (
.tffiles) - อ่าน state (
.tfstate) - เรียก provider API → ดูสถานะ reality
- คำนวณ diff: config vs state vs reality
- แสดง list ของ create/update/replace/destroy
Usage
terraform plan
ต้อง init ก่อน:
terraform init
terraform plan
Plan Output
Terraform will perform the following actions:
# aws_s3_bucket.data will be created
+ resource "aws_s3_bucket" "data" {
+ bucket = "my-data-bucket"
+ bucket_domain_name = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ tags_all = (known after apply)
}
# aws_instance.web will be updated in-place
~ resource "aws_instance" "web" {
id = "i-1234567890abcdef0"
~ instance_type = "t2.micro" -> "t2.small"
# (other unchanged attributes)
}
# aws_security_group.old will be destroyed
- resource "aws_security_group" "old" {
- id = "sg-12345" -> null
- name = "old-sg" -> null
}
Plan: 1 to add, 1 to change, 1 to destroy.
Action Symbols
| Symbol | Action |
|---|---|
+ | Create (new) |
~ | Update in-place |
-/+ | Replace (destroy + recreate) |
- | Destroy |
<= | Read (data source) |
Save Plan to File
# Save plan
terraform plan -out=tfplan
# Apply เฉพาะ plan ที่ save
terraform apply tfplan
Production Pattern
terraform plan -out=tfplan(ใน CI)- Save plan file as artifact
- Reviewer อ่าน plan ใน PR
- Merge →
terraform apply tfplan(ใช้ plan เดิม)
→ ไม่มี surprise ระหว่าง plan และ apply
Useful Flags
-var / -var-file
terraform plan -var="environment=prod"
terraform plan -var-file="prod.tfvars"
-target (ระวังใช้)
# Plan เฉพาะ resource เดียว
terraform plan -target="aws_s3_bucket.data"
# หลายตัว
terraform plan -target="aws_vpc.main" -target="aws_subnet.public"
-target = last resort
ใช้ -target ตอน emergency เท่านั้น — ทำให้ state ออกจากสภาพปกติได้
ดีกว่า: split state files หรือ refactor code
-refresh-only
# ดูว่า reality เปลี่ยนไปจาก state มั้ย (ไม่ดู config diff)
terraform plan -refresh-only
ใช้ตรวจ drift — มีคนแก้ใน console
-destroy
# Plan การลบทุก resource
terraform plan -destroy
→ ใช้ก่อน terraform destroy
-detailed-exitcode
terraform plan -detailed-exitcode
# Exit 0 = no changes
# Exit 1 = error
# Exit 2 = changes detected
ใช้ใน CI:
- name: Plan
id: plan
run: terraform plan -detailed-exitcode -out=tfplan
continue-on-error: true
- name: Has changes?
if: steps.plan.outputs.exitcode == 2
run: echo "Changes detected — review needed"
-parallelism
# Default = 10 concurrent operations
terraform plan -parallelism=20
-no-color
terraform plan -no-color # ปิด ANSI color (เหมาะ log)
Plan Output Formats
JSON
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
plan.json
{
"format_version": "1.2",
"resource_changes": [
{
"address": "aws_s3_bucket.data",
"type": "aws_s3_bucket",
"name": "data",
"change": {
"actions": ["create"],
"before": null,
"after": {...}
}
}
]
}
ใช้ใน CI parse:
terraform show -json tfplan | jq '.resource_changes | length'
Plan Review: สิ่งที่ต้องสังเกต
1. Total Counts
Plan: 5 to add, 3 to change, 1 to destroy.
ตัวเลข match กับที่คาดไว้ไหม?
2. Replace (-/+)
~ instance_type = "t2.micro" -> "t2.large" # forces replacement
Replace = downtime! — เช็คว่า expected ไหม
3. Destroy
- resource "aws_db_instance" "prod"
ถ้าเห็นนี่ = ALARM — ลบ database โดยไม่ตั้งใจ?
4. Sensitive Changes
~ password = (sensitive value)
ดูว่ามี secret rotated ไหม
Drift Detection
# เช็คว่า reality ตรงกับ state ไหม (ไม่อ่าน config diff)
terraform plan -refresh-only
# Output:
~ resource "aws_instance" "web" {
~ tags = {
~ "Manual" = "I added this in console!" -> null
}
}
→ มีคนเพิ่ม tag ใน console manually
ถ้าอยาก accept drift → terraform apply -refresh-only
ถ้าอยาก revert drift → terraform apply (re-apply config)
ตัวอย่าง: PR Workflow
.github/workflows/terraform-plan.yml
name: Terraform Plan
on:
pull_request:
paths: ["**.tf", "**.tfvars"]
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Init
run: terraform init
- name: Validate
run: terraform validate
- name: Plan
id: plan
run: terraform plan -out=tfplan
- name: Comment PR
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const output = require('child_process').execSync('terraform show -no-color tfplan').toString();
const body = `### Terraform Plan\n\`\`\`\n${output}\n\`\`\``;
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
Common Errors
Stale State
Error: Resource not found
→ มีคน destroy resource ใน console แล้ว state ยังมี
แก้:
terraform refresh
# หรือ
terraform state rm <RESOURCE>
Lock Error
Error: Error acquiring the state lock
→ มีคนกำลังรัน apply อยู่ หรือ lock ค้าง
แก้:
# รอให้คนนั้นเสร็จ — หรือถ้าแน่ใจว่า lock ค้าง:
terraform force-unlock <LOCK_ID>
สรุป
terraform plan= preview ก่อน apply- 4 actions: create (
+), update (~), replace (-/+), destroy (-) - ใช้
-out=tfplansave → apply ทีหลัง = no-surprise -refresh-onlyตรวจ drift-detailed-exitcodeใน CI ดูว่ามี change ไหม- เช็ค Replace + Destroy เสมอก่อน apply
ต่อไป → terraform apply