Mục lục
- Core Terraform Workflow
- 📦 Code Examples
- The Terraform Lifecycle
- Step 1: Write Configuration
- Step 2: Initialize the Project
- Step 3: Format Your Code
- Step 4: Validate Configuration
- Step 5: Preview Changes (Plan)
- Step 6: Apply Changes
- Step 7: Inspect Your Infrastructure
- Step 8: Destroy Infrastructure
- Debugging Terraform
- Importing Existing Infrastructure
- State Operations
- Advanced CLI Operations
- The Complete Workflow Script
- Checkpoint Questions
- What's Next: Part 7 - Modules for Organization
- Series Navigation
- References
Core Terraform Workflow
Bạn đã học HCL syntax, variables, và state management rồi đó. Giờ mình sẽ nói về workflow thực tế - chu trình hàng ngày bạn sẽ dùng mỗi khi làm việc với infrastructure.
Đây là Part 6 of 12 trong series Terraform tutorial. Đến cuối bài, bạn sẽ biết những commands cần thiết, khi nào dùng chúng, và cách debug khi có vấn đề xảy ra.
📦 Code Examples
Repository: terraform-hcl-tutorial-series This Part: Part 6 - Workflow Examples
Get working example:
git clone https://github.com/khuongdo/terraform-hcl-tutorial-series.git
cd terraform-hcl-tutorial-series
git checkout part-06
cd examples/part-06-workflow/
# Practice the workflow
terraform init
terraform fmt
terraform validate
terraform plan
The Terraform Lifecycle
Mỗi infrastructure change đều follow pattern này:
Write → Init → Format → Validate → Plan → Apply → Destroy
↑______________________________________________|
(Repeat for changes)
Mỗi step có một nhiệm vụ riêng. Skip một cái là bạn sẽ biết tại sao nó tồn tại theo cách khó nhằn đó.
| Step | What It Does | What Breaks If You Skip It |
|---|---|---|
| Write | Define what you want | Nothing to deploy |
| Init | Download providers, set up backend | Commands fail với "not initialized" |
| Format | Clean up spacing và indentation | Messy diffs, annoying code reviews |
| Validate | Check syntax trước khi run | Errors during plan/apply |
| Plan | Preview changes | Surprises, accidental deletions |
| Apply | Actually create stuff | No infrastructure |
| Destroy | Tear it all down | Orphaned resources, surprise bills |
Giờ mình sẽ đi qua từng bước nhé.
Step 1: Write Configuration
Bắt đầu bằng cách viết những gì bạn muốn. Đây là một basic EC2 instance setup:
# main.tf
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = "${var.project_name}-web-server"
Environment = var.environment
}
}
# variables.tf
variable "aws_region" {
description = "AWS region for resources"
type = string
default = "us-east-1"
}
variable "ami_id" {
description = "AMI ID for EC2 instance"
type = string
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
variable "project_name" {
description = "Project name for resource tagging"
type = string
}
variable "environment" {
description = "Environment (dev/staging/prod)"
type = string
}
# outputs.tf
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id
}
output "public_ip" {
description = "Public IP address of the instance"
value = aws_instance.web.public_ip
}
Step 2: Initialize the Project
Command: terraform init
Đây luôn là command đầu tiên bạn chạy. Nó sẽ:
- Download provider plugins (AWS, GCP, whatever bạn đang dùng)
- Set up backend (nơi state được stored)
- Tạo
.terraformdirectory
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.31.0...
- Installed hashicorp/aws v5.31.0 (signed by HashiCorp)
Terraform has been successfully initialized!
Khi nào chạy init lại:
- Added provider mới
- Changed backend configuration
- Fresh Git clone
- Deleted
.terraform/directory
terraform init an toàn để run nhiều lần. Nếu bạn không chắc có cần hay không, cứ chạy thôi.
Step 3: Format Your Code
Command: terraform fmt
Cái này auto-format .tf files của bạn theo Terraform standard style:
$ terraform fmt
main.tf
variables.tf
What it fixes:
- Indentation (2 spaces)
- Spacing around
= - Alignment
- Trailing whitespace
Before formatting:
resource "aws_instance" "web"{
ami="ami-12345"
instance_type = "t3.micro"
tags={
Name="web-server"
}
}
After terraform fmt:
resource "aws_instance" "web" {
ami = "ami-12345"
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
}
Bạn có thể add cái này vào Git pre-commit hook để không ai push messy code:
#!/bin/bash
# .git/hooks/pre-commit
terraform fmt -check -recursive
if [ $? -ne 0 ]; then
echo "Error: Terraform files not formatted. Run 'terraform fmt'"
exit 1
fi
Step 4: Validate Configuration
Command: terraform validate
Cái này check syntax errors và missing required fields mà không đụng đến cloud APIs:
$ terraform validate
Success! The configuration is valid.
What it catches:
- Bad HCL syntax
- Missing required arguments
- Invalid resource references
- Type mismatches
Example validation failure:
resource "aws_instance" "web" {
ami = var.ami_id
# Oops, quên instance_type
}
$ terraform validate
Error: Missing required argument
on main.tf line 10, in resource "aws_instance" "web":
10: resource "aws_instance" "web" {
The argument "instance_type" is required, but no definition was found.
terraform validate chạy hoàn toàn locally - nó không bao giờ gọi API calls. Dùng nó sớm và thường xuyên nhé.
Step 5: Preview Changes (Plan)
Command: terraform plan
Đây là step quan trọng nhất. Nó show exactly những gì sẽ xảy ra trước khi bạn thay đổi bất cứ thứ gì:
$ terraform plan -var-file="dev.tfvars" -out=tfplan
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.web will be created
+ resource "aws_instance" "web" {
+ ami = "ami-0c55b159cbfafe1f0"
+ instance_type = "t3.micro"
+ id = (known after apply)
+ public_ip = (known after apply)
+ tags = {
+ "Environment" = "dev"
+ "Name" = "myapp-web-server"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ instance_id = (known after apply)
+ public_ip = (known after apply)
────────────────────────────────────────────────────────────────────
Saved the plan to: tfplan
Understanding plan symbols:
| Symbol | Meaning | Risk Level |
|---|---|---|
+ | Resource sẽ được created | Low |
~ | Resource sẽ modified in-place | Medium |
-/+ | Resource sẽ destroyed và recreated | High |
- | Resource sẽ destroyed | Critical |
Save plans cho safer applies:
# Generate plan file
terraform plan -out=tfplan
# Review the plan
terraform show tfplan
# Apply the exact plan
terraform apply tfplan
Tại sao phải save plans?
Không có -out, chạy terraform apply sẽ generate plan mới tại apply time. Nếu có gì đó thay đổi giữa plan và apply (drift, thay đổi của người khác), bạn có thể apply cái gì đó khác với cái bạn đã review.
Luôn dùng terraform plan -out=tfplan rồi terraform apply tfplan trong production. Đừng bao giờ chạy terraform apply mà không review saved plan trước.
Step 6: Apply Changes
Command: terraform apply
Cái này executes plan và creates/modifies/destroys infrastructure:
# Interactive apply (hỏi confirmation)
$ terraform apply -var-file="dev.tfvars"
# Auto-approve (chỉ dùng trong CI/CD thôi)
$ terraform apply -var-file="dev.tfvars" -auto-approve
# Apply saved plan (recommended)
$ terraform apply tfplan
Output:
aws_instance.web: Creating...
aws_instance.web: Still creating... [10s elapsed]
aws_instance.web: Still creating... [20s elapsed]
aws_instance.web: Creation complete after 25s [id=i-0abcd1234efgh5678]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
instance_id = "i-0abcd1234efgh5678"
public_ip = "54.123.45.67"
What happens during apply:
- Terraform locks state file (prevents other runs)
- Gọi API calls đến cloud provider của bạn
- Updates
terraform.tfstatevới new resource IDs - Unlocks state file
- Shows outputs
Khi apply fails mid-execution:
Nếu nó crashes (network error, API limit, timeout), state file reflects những gì actually được created. Fix issue đó và re-run terraform apply - Terraform sẽ picks up từ nơi nó dừng lại.
Step 7: Inspect Your Infrastructure
Sau khi apply, check những gì bạn đã tạo:
# List tất cả resources trong state
$ terraform state list
aws_instance.web
# Show detailed resource attributes
$ terraform state show aws_instance.web
# aws_instance.web:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
id = "i-0abcd1234efgh5678"
public_ip = "54.123.45.67"
tags = {
"Environment" = "dev"
"Name" = "myapp-web-server"
}
}
# View outputs lại
$ terraform output
instance_id = "i-0abcd1234efgh5678"
public_ip = "54.123.45.67"
# Get specific output value (cho scripts)
$ terraform output -raw public_ip
54.123.45.67
Step 8: Destroy Infrastructure
Command: terraform destroy
Khi bạn xong (dev environment, testing, etc.), tear it down:
$ terraform destroy -var-file="dev.tfvars"
Terraform will perform the following actions:
# aws_instance.web will be destroyed
- resource "aws_instance" "web" {
- ami = "ami-0c55b159cbfafe1f0" -> null
- instance_type = "t3.micro" -> null
- id = "i-0abcd1234efgh5678" -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_instance.web: Destroying... [id=i-0abcd1234efgh5678]
aws_instance.web: Destruction complete after 35s
Destroy complete! Resources: 1 destroyed.
Targeted destruction (specific resource):
# Destroy chỉ web instance thôi
terraform destroy -target=aws_instance.web
terraform destroy là irreversible. Luôn chạy terraform plan -destroy trước để preview những gì sẽ deleted. Trong production, protect critical resources với lifecycle { prevent_destroy = true }.
Debugging Terraform
Khi có lỗi, Terraform cung cấp detailed logging:
Enable Debug Logging
# Set log level (TRACE, DEBUG, INFO, WARN, ERROR)
export TF_LOG=DEBUG
# Save logs to file
export TF_LOG_PATH=terraform-debug.log
# Run Terraform command
terraform apply
# Disable logging
unset TF_LOG TF_LOG_PATH
Log levels:
| Level | Use Case |
|---|---|
TRACE | Maximum verbosity - shows mọi API call |
DEBUG | Detailed debugging info |
INFO | Standard operational messages |
WARN | Warning messages only |
ERROR | Errors only |
Common Debugging Scenarios
Problem: "Error: Provider configuration not present"
# Solution: Run init
terraform init
Problem: "Error: Inconsistent dependency lock file"
# Solution: Regenerate lock file
terraform init -upgrade
Problem: "Error: Backend initialization required"
# Solution: Migrate backend
terraform init -migrate-state
Problem: "Error: Resource already exists"
# Solution: Import existing resource
terraform import aws_instance.web i-1234567890abcdef0
Importing Existing Infrastructure
Có manually created resources? Import chúng vào Terraform:
Step 1: Write the resource configuration
# main.tf
resource "aws_instance" "legacy" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "legacy-server"
}
}
Step 2: Import the resource
# Syntax: terraform import <resource_type>.<name> <cloud_resource_id>
$ terraform import aws_instance.legacy i-1234567890abcdef0
aws_instance.legacy: Importing from ID "i-1234567890abcdef0"...
aws_instance.legacy: Import prepared!
Prepared aws_instance for import
aws_instance.legacy: Refreshing state... [id=i-1234567890abcdef0]
Import successful!
Step 3: Verify the import
$ terraform plan
No changes. Your infrastructure matches the configuration.
Nếu plan shows changes, update configuration của bạn để match với actual resource.
Dùng terraform show sau import để see full resource configuration, rồi copy nó vào .tf files của bạn.
State Operations
Terraform state commands cho phép bạn inspect và manipulate state mà không đụng đến cloud resources:
List Resources
$ terraform state list
aws_instance.web
aws_security_group.web_sg
aws_vpc.main
Show Resource Details
$ terraform state show aws_instance.web
# aws_instance.web:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
id = "i-0abcd1234efgh5678"
instance_type = "t3.micro"
}
Move Resources (Refactoring)
Khi bạn rename resources trong code, update state để match:
# Renamed trong code: aws_instance.web → aws_instance.app_server
$ terraform state mv aws_instance.web aws_instance.app_server
Move "aws_instance.web" to "aws_instance.app_server"
Successfully moved 1 object(s).
Remove Resources from State
Remove resource khỏi Terraform management mà không destroying nó:
$ terraform state rm aws_instance.web
Removed aws_instance.web
Successfully removed 1 resource instance(s).
Instance vẫn còn tồn tại trong AWS, nhưng Terraform không còn manage nó nữa.
Advanced CLI Operations
Refresh State
Sync Terraform state với actual cloud resources:
$ terraform refresh
Khi nào dùng:
- Có người made manual changes trong cloud console
- Checking drift giữa state và reality
Note: Terraform tự động refreshes trong plan và apply, nên explicit refresh hiếm khi cần thiết.
Replace Resources
Force replacement của resource (destroy rồi recreate):
# Replace specific resource
$ terraform apply -replace=aws_instance.web
# Hữu ích khi:
# - Resource bị corrupted
# - Cần fresh instance ID
# - Testing disaster recovery
Targeted Apply
Apply changes chỉ cho specific resources thôi:
# Chỉ create/update web instance
$ terraform apply -target=aws_instance.web
# Multiple targets
$ terraform apply -target=aws_instance.web -target=aws_security_group.web_sg
Dùng -target có thể tạo inconsistent state. Tránh trong production trừ khi recovering từ partial failures.
The Complete Workflow Script
Đây là production-ready workflow script:
#!/bin/bash
# deploy.sh - Safe Terraform deployment script
set -e # Exit on error
ENVIRONMENT=${1:-dev}
VAR_FILE="${ENVIRONMENT}.tfvars"
echo "========================================="
echo "Deploying to: $ENVIRONMENT"
echo "========================================="
# Step 1: Initialize
echo "→ Initializing Terraform..."
terraform init
# Step 2: Format check
echo "→ Checking code formatting..."
terraform fmt -check -recursive
if [ $? -ne 0 ]; then
echo "❌ Code not formatted. Run: terraform fmt -recursive"
exit 1
fi
# Step 3: Validate
echo "→ Validating configuration..."
terraform validate
# Step 4: Generate plan
echo "→ Generating plan..."
terraform plan -var-file="$VAR_FILE" -out=tfplan
# Step 5: Show plan
echo "→ Plan generated. Review changes above."
read -p "Apply this plan? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "❌ Deployment cancelled."
rm tfplan
exit 0
fi
# Step 6: Apply plan
echo "→ Applying changes..."
terraform apply tfplan
# Cleanup
rm tfplan
echo "✅ Deployment complete!"
terraform output
Usage:
chmod +x deploy.sh
./deploy.sh dev # Deploy to dev
./deploy.sh staging # Deploy to staging
./deploy.sh prod # Deploy to production
Checkpoint Questions
Test understanding của bạn:
terraform initlàm gì? Tại sao phải chạy nó trướcplanhoặcapply?Khác biệt giữa
terraform planvàterraform apply? Bạn có thể apply mà không planning không?Điều gì xảy ra nếu bạn chạy
terraform applyhai lần mà không có code changes?Làm sao destroy chỉ một specific resource mà không tear down mọi thứ?
Tại sao phải save plans với
-out=tfplan? Nó giải quyết vấn đề gì?Khi nào bạn sẽ dùng
terraform import? Bạn có thể import resources mà Terraform đã created không?Khác biệt giữa
terraform state rmvàterraform destroylà gì?
Click để xem answers
terraform initdownloads provider plugins và initializes backend. Required trước mọi Terraform operations.planpreviews changes mà không making them;applyexecutes changes. Bạn có thể apply mà không có explicit plan, nhưng nó risky (plan happens inline).Không có gì thay đổi. Terraform detects infrastructure matches desired state và performs no operations.
terraform destroy -target=resource_type.nameSaved plans đảm bảo changes bạn reviewed là exactly những gì được applied (không có drift giữa plan time và apply time).
Dùng
importđể bring manually-created resources vào Terraform management. Yes, bạn có thể import Terraform-created resources nếu bạn lost state.state rmremoves khỏi Terraform tracking nhưng leaves resource intact.destroydeletes actual cloud resource.
What's Next: Part 7 - Modules for Organization
Bạn đã master Terraform workflow rồi đó, nhưng điều gì xảy ra khi codebase của bạn grows đến hàng trăm resources? Copy cùng một VPC configuration across 10 projects? Manage dependencies giữa related resources?
Trong Part 7, mình sẽ giải quyết những problems này với Terraform modules - reusable components mà:
- Eliminate code duplication
- Enforce organizational standards
- Enable team collaboration
- Simplify complex infrastructure
Bạn sẽ học:
- Tạo custom modules từ scratch
- Dùng public modules từ Terraform Registry
- Pass variables và outputs giữa modules
- Version và publish internal modules
- Structure large Terraform projects
Preview snippet (Part 7):
# Instead of repeating VPC code...
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
name = "${var.project}-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = false
}
# Reference module outputs
resource "aws_instance" "web" {
subnet_id = module.vpc.public_subnets[0]
}
Hẹn gặp bạn ở Part 7 nhé.
Series Navigation
- Part 1: Why Infrastructure as Code?
- Part 2: Setting Up Terraform (Coming soon)
- Part 3: Your First Cloud Resource (Coming soon)
- Part 4: HCL Fundamentals (Coming soon)
- Part 5: Variables, Outputs & State (Coming soon)
- Part 6: Core Terraform Workflow (You are here)
- Part 7: Modules for Organization (Coming soon)
- Part 8: Multi-Cloud Patterns (Coming soon)
- Part 9: State Management & Team Workflows (Coming soon)
- Part 10: Testing & Validation (Coming soon)
- Part 11: Security & Secrets Management (Coming soon)
- Part 12: Production Patterns & DevSecOps (Coming soon)
References
- Terraform CLI Documentation
- Terraform State Command Reference
- Debugging Terraform
- Import Command Documentation
Điều hướng series:
- Phần 1: Tại sao cần Infrastructure as Code?
- Phần 2: Cài đặt Terraform
- Phần 3: Tài nguyên Cloud đầu tiên
- Phần 4: Cơ bản về HCL
- Phần 5: Variables, Outputs & State
- Phần 6: Quy trình làm việc với Terraform (Bạn đang ở đây)
- Phần 7: Modules để tổ chức code (Sắp ra mắt)
- Phần 8: Mô hình Multi-Cloud (Sắp ra mắt)
- Phần 9: Quản lý State & Team Workflows (Sắp ra mắt)
- Phần 10: Testing & Validation (Sắp ra mắt)
- Phần 11: Bảo mật & Quản lý Secrets (Sắp ra mắt)
- Phần 12: Production Patterns & DevSecOps (Sắp ra mắt)
Questions hoặc feedback? Drop a comment below hoặc kết nối trên LinkedIn.