Terraform Cho Developer: IaC Không Khó Như Bạn Nghĩ
ClickOps — Tội Ác Của Developer Trên AWS Console
Tuần trước em cần tạo lại một S3 bucket + CloudFront distribution cho project side project. Lần trước em đã tạo nó — 3 tháng trước — bằng cách click-click trên AWS Console. Kết quả? Em nhớ mang máng đã tạo, nhưng quên hoàn toàn:
- Bucket name là gì?
- CloudFront config thế nào?
- CORS policy ra sao?
- Lifecycle rule có không?
Đó là ClickOps — thuật ngữ chỉ việc tạo infrastructure bằng cách click chuột trên UI. Nhanh lúc đầu, đau về sau. Và developer nào cũng từng mắc.
Terraform giải quyết chính xác vấn đề này: infrastructure dưới dạng code. Viết một lần, chạy bao nhiêu lần cũng ra kết quả giống nhau. Muốn biết config gì? Đọc file .tf. Muốn thay đổi? Sửa file, review diff, apply.
Bài viết này hướng dẫn từ zero cho developer — không phải sysadmin lecture — cách bắt đầu với Terraform.
Terraform Là Gì?
TL;DR: Terraform là tool của HashiCorp cho phép bạn định nghĩa infrastructure bằng code (HCL), rồi tự động tạo/thay đổi/xóa resources trên cloud.
Nếu bạn là developer, hãy nghĩ đơn giản:
- Code → bạn viết
.tffiles mô tả infrastructure muốn có - Plan → Terraform so sánh code với reality, cho bạn biết sẽ thay đổi gì
- Apply → Terraform thực thi, tạo/thay đổi/xóa resources
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Viết .tf │────▶│ terraform │────▶│ terraform │
│ files │ │ plan │ │ apply │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
Mô tả infra "Sẽ thêm 2 resource, Resources được
muốn có sửa 1 resource" tạo trên AWS
Điểm mạnh lớn nhất so với CloudFormation: multi-cloud. Cùng một syntax, bạn quản lý AWS, GCP, Azure, Cloudflare, thậm chí PostgreSQL users. Và HCL (HashiCorp Configuration Language) đọc dễ hơn JSON/YAML rất nhiều khi viết infrastructure.
Tại Sao Developer Nên Học Terraform?
TL;DR: Nó không chỉ là skill DevOps — nó là skill giúp bạn làm việc hiệu quả hơn và tăng giá trị trên thị trường.
1. Reproducibility — tạo lại môi trường bất cứ lúc nào
Bạn có staging environment chạy ổn. Cần tạo lại production y hệt? Copy-paste click? Hay chạy terraform apply? Terraform đảm bảo hai môi trường giống nhau đến từng config detail.
2. Version control — infrastructure cũng có git history
Thay đổi infrastructure giờ nằm trong PR. Muốn biết ai đã mở port 22 trên security group? git log. Muốn revert? git revert. Không khác gì code.
3. Collaboration — không còn "ai đó đã thay đổi gì?"
ClickOps thì mỗi người một kiểu. Terraform enforce một cách làm việc duy nhất. Review PR infrastructure cũng như review PR code.
4. Career — DevOps/SRE đang hot
Theo khảo sát 2025, developer biết Terraform có mức lương cao hơn 15-25% so với developer chỉ biết code. Không phải vì Terraform khó — mà vì cầu vượt cung.
Core Concepts — 5 Khái Niệm Cần Nắm
TL;DR: Provider kết nối cloud, Resource là thứ bạn tạo, State là "database" của Terraform, Plan là preview, Apply là thực thi.
1. Provider — "Plugin" kết nối cloud
Provider là cách Terraform nói chuyện với cloud provider. AWS, GCP, Azure, Cloudflare — mỗi cái có một provider.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-southeast-1"
}
2. Resource — thứ bạn muốn tạo
Resource là đơn vị cơ bản. Một S3 bucket, một EC2 instance, một security group rule.
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-awesome-bucket-2026"
tags = {
Environment = "production"
}
}
"aws_s3_bucket" là resource type, "my_bucket" là local name (dùng tham chiếu trong code), còn bucket = "..." là argument.
3. State — "database" của Terraform
Đây là concept quan trọng nhất mà developer hay bỏ qua. Terraform lưu trạng thái hiện tại của infrastructure vào file terraform.tfstate. File này là "nguồn sự thật" — Terraform dùng nó để biết resource nào đang tồn tại, config gì.
Lưu ý: State file chứa cả resource ID, có thể chứa sensitive data. Không bao giờ commit vào git. Lưu remote (S3 + DynamoDB lock) khi làm việc team.
4. Plan — preview trước khi apply
terraform plan
Output cho bạn biết chính xác sẽ thêm/sửa/xóa gì. Đây là diff của infrastructure — giống git diff nhưng cho cloud resources.
# aws_s3_bucket.my_bucket will be created
+ resource "aws_s3_bucket" "my_bucket" {
+ bucket = "my-awesome-bucket-2026"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
5. Apply — thực thi
terraform apply
Sau khi confirm "yes", Terraform thực hiện đúng những gì plan đã show. Không hơn, không kém.
Hands-on: Deploy S3 + CloudFront
TL;DR: Tạo một S3 bucket hosting static website + CloudFront distribution. Khoảng 40 dòng HCL.
Bước 1: Install Terraform
# macOS
brew install terraform
# Linux (amd64)
wget https://releases.hashicorp.com/terraform/1.12.0/terraform_1.12.0_linux_amd64.zip
unzip terraform_1.12.0_linux_amd64.zip
sudo mv terraform /usr/local/bin/
# Verify
terraform version
Bước 2: Viết Terraform code
Tạo thư mục project, tạo file main.tf:
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# Remote state trên S3 (xem phần dưới)
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "s3-cloudfront/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
provider "aws" {
region = "ap-southeast-1"
}
# S3 Bucket cho static hosting
resource "aws_s3_bucket" "website" {
bucket = "my-static-site-2026-unique"
}
resource "aws_s3_bucket_website_configuration" "website" {
bucket = aws_s3_bucket.website.id
index_document {
suffix = "index.html"
}
error_document {
key = "error.html"
}
}
resource "aws_s3_bucket_public_access_block" "website" {
bucket = aws_s3_bucket.website.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# CloudFront Distribution
resource "aws_cloudfront_distribution" "cdn" {
origin {
domain_name = aws_s3_bucket.website.bucket_regional_domain_name
origin_id = "s3-website"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path
}
}
enabled = true
default_root_object = "index.html"
price_class = "PriceClass_200"
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "s3-website"
viewer_protocol_policy = "redirect-to-https"
compress = true
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
resource "aws_cloudfront_origin_access_identity" "oai" {
comment = "OAI for static site"
}
# S3 bucket policy cho CloudFront
resource "aws_s3_bucket_policy" "website" {
bucket = aws_s3_bucket.website.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowCloudFrontAccess"
Effect = "Allow"
Principal = {
AWS = aws_cloudfront_origin_access_identity.oai.iam_arn
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.website.arn}/*"
}
]
})
}
# Outputs
output "cloudfront_domain" {
value = aws_cloudfront_distribution.cdn.domain_name
}
output "s3_bucket_name" {
value = aws_s3_bucket.website.id
}
HCL vs JSON/YAML: HCL được thiết kế riêng cho infrastructure. Nó hỗ trợ variables, expressions, functions — linh hoạt hơn YAML, dễ đọc hơn JSON. Nếu bạn đã viết Ansible (YAML) hoặc CloudFormation (JSON), HCL sẽ thấy thoải mái hơn nhiều.
Bước 3: Init → Plan → Apply
# Khởi tạo — download providers
terraform init
# Preview — xem sẽ tạo gì
terraform plan
# Apply — tạo resources
terraform apply
# Nhập "yes" khi được hỏi
# Output sẽ hiển thị:
# cloudfront_domain = "d1234abcd.cloudfront.net"
# s3_bucket_name = "my-static-site-2026-unique"
Ba lệnh. Đó là workflow lặp lại mỗi khi thay đổi infrastructure.
Bước 4: Quản lý State — Remote State với S3
Tại sao cần remote state?
- Local state = một người làm được. Team = conflict.
- State chứa sensitive data (resource ID, connection strings).
- Remote state trên S3 + DynamoDB lock = an toàn, nhiều người cùng làm.
Thiết lập một lần:
# Tạo S3 bucket cho state
aws s3api create-bucket \
--bucket my-terraform-state-bucket \
--region ap-southeast-1 \
--create-bucket-configuration LocationConstraint=ap-southeast-1
# Enable versioning (rollback state nếu cần)
aws s3api put-bucket-versioning \
--bucket my-terraform-state-bucket \
--versioning-configuration Status=Enabled
# Tạo DynamoDB table cho locking
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
Sau đó thêm backend "s3" block vào terraform như trong ví dụ ở trên. Chạy terraform init lại, Terraform sẽ hỏi có muốn di chuyển state từ local lên S3 không — chọn yes.
Terraform vs CloudFormation vs Pulumi
TL;DR: Terraform là lựa chọn an toàn nhất cho developer. CloudFormation lock-in AWS. Pulumi mạnh nhưng quá mức cho team nhỏ.
- Terraform (HCL): Multi-cloud, community lớn nhất, ecosystem module phong phú. Syntax riêng nhưng dễ học. Lựa chọn mặc định cho hầu hết team.
- CloudFormation (JSON/YAML): Native AWS, không cần cài thêm tool. Nhưng chỉ dùng được AWS, syntax dài dòng, debug khó. Phù hợp nếu team chỉ dùng AWS và đã quen.
- Pulumi (TypeScript/Python/Go): Viết infrastructure bằng ngôn ngữ lập trình quen thuộc. Mạnh cho logic phức tạp (loops, conditionals). Nhưng thêm complexity không cần thiết cho 90% use case.
Góc nhìn cá nhân: Nếu bạn là developer, bắt đầu với Terraform. Lý do đơn giản: khi đổi cloud provider (hoặc dùng multi-cloud), kiến thức Terraform transfer được. CloudFormation knowledge thì không.
Tips Cho Developer Mới Bắt Đầu
TL;DR: Bắt đầu nhỏ, dùng module, đừng sợ destroy.
1. Bắt đầu với resource đơn giản
Đừng nhảy vào deploy cả VPC + ECS cluster ngay. Bắt đầu với S3 bucket, rồi EC2 instance, rồi dần phức tạp hơn. Mỗi resource là một bài học.
2. Dùng Terraform modules
Cộng đồng đã viết sẵn module cho hầu hết use case. Cần VPC? Dùng terraform-aws-modules/vpc/aws. Cần ECS? Có module cho đó. Đừng reinvent the wheel.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["ap-southeast-1a", "ap-southeast-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"]
}
3. terraform plan trước mọi thứ
Không bao giờ chạy apply mà không plan trước. Nhất là khi có resource đã tồn tại — plan sẽ show diff, tránh surprise.
4. Học cách đọc state
# Liệt kê tất cả resources đang quản lý
terraform state list
# Xem chi tiết một resource
terraform state show aws_s3_bucket.website
# Import resource đã tồn tại (dùng khi "rescue" ClickOps resources)
terraform import aws_s3_bucket.website my-existing-bucket
5. .gitignore đúng cách
# .gitignore
*.tfstate
*.tfstate.backup
.terraform/
.terraform.lock.hcl
terraform.tfvars
terraform.tfvars chứa giá trị riêng (account ID, API keys) — không commit.
6. Dùng tfvars cho environment khác nhau
# variables.tf
variable "environment" {}
variable "instance_type" {}
# dev.tfvars
environment = "dev"
instance_type = "t3.micro"
# prod.tfvars
environment = "prod"
instance_type = "t3.large"
terraform apply -var-file="dev.tfvars"
terraform apply -var-file="prod.tfvars"
Kết Luận + Roadmap
Terraform không phải giải pháp vạn năng. Nhưng nó giải quyết đúng vấn đề mà developer hay gặp: infrastructure không reproducible, không version-controlled, không reviewable.
Bài viết này covers phần cơ bản. Nếu bạn muốn đi sâu hơn:
Roadmap học tiếp:
- Tuần 1-2: Deploy S3 + EC2 + VPC cơ bản. Quen workflow
init → plan → apply. - Tuần 3-4: Học modules, workspaces, variables/output. Quản lý nhiều environments.
- Tháng 2: Terraform với CI/CD (GitHub Actions). Tự động plan trên PR, apply trên merge.
- Tháng 3: Advanced — custom modules, state management, import existing resources.
- Tháng 4+: Kết hợp với Ansible (provisioning sau khi tạo infrastructure), hoặc thử Pulumi nếu muốn dùng TypeScript.
Resources thực tế:
- Terraform Official Tutorials — học qua hands-on labs
- Terraform AWS Modules — module community
- Terraform Best Practices — quy ước cho team
Đây là bài đầu tiên trong series DevOps Handbook — hướng dẫn thực tế cho developer muốn mở rộng kỹ năng sang DevOps. Bài tiếp theo: CI/CD Pipeline với GitHub Actions — từ code đến production trong 5 phút.