Infrastructure as Code (IaC) คือแนวคิดที่เปลี่ยนวิธีการจัดการ infrastructure จากการคลิก GUI ทีละขั้นตอนมาเป็นการเขียน code ที่ version controlled ได้ reviewable ได้ และ reproducible ได้ 100 เปอร์เซ็นต์ Terraform ของ HashiCorp คือเครื่องมือ IaC ที่ได้รับความนิยมสูงสุดในโลก รองรับ cloud provider มากกว่า 3,000 ตัว ตั้งแต่ AWS, Azure, GCP ไปจนถึง Proxmox, vSphere และ Cloudflare
บทความนี้จะพาคุณเรียนรู้ Terraform ตั้งแต่ concept พื้นฐานจนถึง production-grade configuration พร้อม modules, remote state, workspaces และ CI/CD integration ที่ใช้งานได้จริงในองค์กรครับ
ลองนึกภาพว่าคุณมี AWS account ที่ทีม 10 คนใช้ร่วมกัน ทุกคนสร้าง EC2, RDS, S3 ผ่าน Console ตามใจชอบ หลังจากผ่านไป 6 เดือน ไม่มีใครรู้ว่า infrastructure ทั้งหมดหน้าตาเป็นอย่างไร security group ไหนเปิด port อะไรบ้าง IAM role ไหนมี permission อะไร และถ้า disaster recovery จะ recreate ได้หมดไหม
IaC แก้ปัญหานี้ทั้งหมด ทุก resource ถูกเขียนเป็น code เก็บใน Git ทุกการเปลี่ยนแปลงต้องผ่าน pull request review ได้ audit trail ชัดเจน สามารถ recreate infrastructure ทั้งหมดจาก code ได้ภายในนาที และที่สำคัญที่สุดคือ consistency ไม่ว่าจะ deploy กี่ครั้ง ผลลัพธ์เหมือนกันทุกครั้ง ไม่มี configuration drift อีกต่อไปครับ
CloudFormation ใช้ได้เฉพาะ AWS และใช้ JSON หรือ YAML ที่ verbose มาก Pulumi ให้เขียนด้วยภาษา programming จริงเช่น Python, TypeScript แต่ ecosystem ยังเล็กกว่า Terraform ใช้ HCL (HashiCorp Configuration Language) ที่อ่านง่าย เรียนรู้เร็ว รองรับ multi-cloud และมี provider มากที่สุด เป็นตัวเลือกที่ดีที่สุดสำหรับทีมส่วนใหญ่ครับ
# วิธีที่ 1: ผ่าน HashiCorp repository (แนะนำ) sudo apt update && sudo apt install -y gnupg software-properties-common wget -O- https://apt.releases.hashicorp.com/gpg | \ gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \ https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \ sudo tee /etc/apt/sources.list.d/hashicorp.list sudo apt update && sudo apt install -y terraform # ตรวจสอบ terraform version # Terraform v1.10.x on linux_amd64
# วิธีที่ 2: Binary download (สำหรับ air-gapped)
TERRAFORM_VERSION="1.10.3"
wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip
sudo mv terraform /usr/local/bin/
terraform version
ติดตั้ง autocomplete สำหรับ shell ที่ใช้ จะช่วยเรื่อง productivity มาก Tab completion สำหรับ terraform command, subcommand และ flag ทั้งหมด
# เปิด autocomplete terraform -install-autocomplete source ~/.bashrc
เริ่มจากโปรเจ็คง่ายๆ สร้าง AWS VPC พร้อม subnet และ EC2 instance สร้างโฟลเดอร์ใหม่แล้วสร้างไฟล์ตามนี้
# สร้างโปรเจ็ค
mkdir ~/terraform-demo && cd ~/terraform-demo
# main.tf — Provider configuration
terraform {
required_version = ">= 1.9"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# network.tf — VPC และ Subnet
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project}-vpc"
Environment = var.environment
ManagedBy = "terraform"
}
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project}-public-${count.index + 1}"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = { Name = "${var.project}-igw" }
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = { Name = "${var.project}-public-rt" }
}
# ขั้นตอนการใช้งาน terraform init # ดาวน์โหลด provider plugins terraform fmt # จัด format code terraform validate # ตรวจสอบ syntax terraform plan # preview การเปลี่ยนแปลง terraform apply # สร้าง infrastructure จริง terraform destroy # ลบทั้งหมด
คำสั่ง terraform init จะดาวน์โหลด AWS provider plugin มาเก็บในโฟลเดอร์ .terraform จากนั้น terraform plan จะแสดง execution plan ว่าจะสร้าง resource อะไรบ้าง ให้คุณ review ก่อนตัดสินใจ และ terraform apply จะสร้าง infrastructure จริงบน AWS ทุกขั้นตอนชัดเจนและ predictable ครับ
Terraform state file คือหัวใจสำคัญที่สุดของ Terraform เป็น JSON file ที่เก็บ mapping ระหว่าง resource ใน code กับ resource จริงบน cloud ถ้าไม่มี state file Terraform จะไม่รู้ว่า infrastructure ปัจจุบันมีอะไรบ้าง
# backend.tf — Remote state ใน S3
terraform {
backend "s3" {
bucket = "siamcafe-terraform-state"
key = "production/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
# สร้าง S3 bucket และ DynamoDB table สำหรับ state aws s3api create-bucket \ --bucket siamcafe-terraform-state \ --region ap-southeast-1 \ --create-bucket-configuration LocationConstraint=ap-southeast-1 aws s3api put-bucket-versioning \ --bucket siamcafe-terraform-state \ --versioning-configuration Status=Enabled 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
Remote state มีข้อดีหลายอย่าง ข้อแรกคือ state locking ป้องกันสองคนรัน apply พร้อมกัน ข้อที่สองคือ versioning ย้อนกลับได้ถ้า apply ผิด ข้อที่สามคือ encryption ป้องกัน sensitive data ที่อยู่ใน state file และข้อที่สี่คือ collaboration ทุกคนในทีมใช้ state เดียวกัน ไม่มีปัญหา state ไม่ sync ครับ
# variables.tf
variable "aws_region" {
description = "AWS region for resources"
type = string
default = "ap-southeast-1"
}
variable "project" {
description = "Project name for resource tagging"
type = string
default = "siamcafe"
}
variable "environment" {
description = "Environment name (dev/staging/production)"
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Environment must be dev, staging, or production."
}
}
variable "availability_zones" {
description = "List of AZs"
type = list(string)
default = ["ap-southeast-1a", "ap-southeast-1b", "ap-southeast-1c"]
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.medium"
}
# outputs.tf
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "Public subnet IDs"
value = aws_subnet.public[*].id
}
output "ec2_public_ip" {
description = "EC2 public IP"
value = aws_instance.web.public_ip
sensitive = false
}
# terraform.tfvars — ค่าเฉพาะ environment aws_region = "ap-southeast-1" project = "siamcafe" environment = "production" instance_type = "t3.large"
Variable validation เป็น feature ที่มีประโยชน์มาก ช่วยป้องกันค่าผิดตั้งแต่ขั้นตอน plan ไม่ต้องรอให้ API error ตอน apply ใส่ validation ให้ทุก variable ที่มี constraint เช่น instance_type ต้องเป็น t3 family, CIDR block ต้องเป็น /16 หรือ /24 เท่านั้น จะช่วยลด human error ได้มากครับ
Module คือวิธีจัดระเบียบ Terraform code ให้ reusable และ maintainable แทนที่จะเขียน resource ซ้ำทุกโปรเจ็ค สร้าง module ครั้งเดียวแล้วเรียกใช้ได้ทุกที่ เหมือน function ในภาษา programming
# โครงสร้างโฟลเดอร์
terraform-project/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars
├── modules/
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── ec2/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── rds/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── environments/
├── dev.tfvars
├── staging.tfvars
└── production.tfvars
# modules/vpc/main.tf
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(var.tags, { Name = "${var.name}-vpc" })
}
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.this.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.azs[count.index]
map_public_ip_on_launch = true
tags = merge(var.tags, { Name = "${var.name}-public-${count.index + 1}" })
}
# modules/vpc/variables.tf
variable "name" { type = string }
variable "vpc_cidr" { type = string; default = "10.0.0.0/16" }
variable "public_subnets" { type = list(string) }
variable "azs" { type = list(string) }
variable "tags" { type = map(string); default = {} }
# modules/vpc/outputs.tf
output "vpc_id" { value = aws_vpc.this.id }
output "subnet_ids" { value = aws_subnet.public[*].id }
# main.tf — เรียกใช้ module
module "vpc" {
source = "./modules/vpc"
name = var.project
vpc_cidr = "10.0.0.0/16"
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
azs = var.availability_zones
tags = local.common_tags
}
module "web_server" {
source = "./modules/ec2"
name = "${var.project}-web"
instance_type = var.instance_type
subnet_id = module.vpc.subnet_ids[0]
vpc_id = module.vpc.vpc_id
tags = local.common_tags
}
นอกจาก local module ยังใช้ module จาก Terraform Registry ได้อีกด้วย เช่น terraform-aws-modules/vpc/aws ที่มีคนใช้หลายล้าน download มี feature ครบถ้วนทั้ง NAT Gateway, VPN Gateway, Transit Gateway ทำให้ไม่ต้องเขียนเอง ประหยัดเวลาได้มากครับ
Terraform Workspaces ช่วยให้ใช้ code ชุดเดียวกัน deploy ได้หลาย environment โดยแยก state file แต่ละ workspace ออกจากกัน
# สร้างและสลับ workspace terraform workspace new dev terraform workspace new staging terraform workspace new production terraform workspace list # * default # dev # staging # production terraform workspace select production
# ใช้ workspace name ใน configuration
locals {
env_config = {
dev = {
instance_type = "t3.small"
instance_count = 1
enable_monitoring = false
}
staging = {
instance_type = "t3.medium"
instance_count = 2
enable_monitoring = true
}
production = {
instance_type = "t3.large"
instance_count = 3
enable_monitoring = true
}
}
config = local.env_config[terraform.workspace]
}
resource "aws_instance" "web" {
count = local.config.instance_count
instance_type = local.config.instance_type
monitoring = local.config.enable_monitoring
# ...
}
Workspace เหมาะสำหรับโปรเจ็คที่ infrastructure structure เหมือนกันทุก environment แต่ขนาดต่างกัน ถ้า environment มี resource ต่างกันมาก เช่น production มี multi-AZ RDS แต่ dev ใช้ single instance แนะนำใช้ directory-based separation แทน workspace จะ maintain ง่ายกว่าครับ
การรัน Terraform ด้วยมือจากเครื่อง developer เป็นวิธีที่อันตรายสำหรับ production เพราะไม่มี audit trail ไม่มี review process และไม่มี consistent execution environment ควรใช้ CI/CD pipeline ที่ enforce review ก่อน apply เสมอ
# .github/workflows/terraform.yml
name: Terraform
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
pull-requests: write
contents: read
jobs:
terraform:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./infrastructure
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.10.3"
- name: Terraform Init
run: terraform init
- name: Terraform Format Check
run: terraform fmt -check
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
id: plan
run: terraform plan -no-color -out=tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Comment PR with Plan
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const plan = `${{ steps.plan.outputs.stdout }}`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '```terraform\n' + plan + '\n```'
});
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve tfplan
Pipeline นี้จะรัน plan ทุก pull request แล้วแสดง output เป็น comment ใน PR ให้ทีม review ก่อน merge เมื่อ merge เข้า main จึงรัน apply จริง ป้องกัน accidental destruction ได้ดีมาก และทุก apply มี audit trail ใน Git history ครับ
องค์กรส่วนใหญ่มี infrastructure ที่สร้างด้วยมือก่อนจะเริ่มใช้ Terraform คำสั่ง terraform import ช่วย bring existing resource เข้ามาอยู่ใต้การจัดการของ Terraform ได้
# Import EC2 instance ที่มีอยู่
terraform import aws_instance.web i-0123456789abcdef0
# Import VPC
terraform import aws_vpc.main vpc-0123456789abcdef0
# Import RDS
terraform import aws_db_instance.main mydb-instance
# Terraform 1.5+ — Import block (แนะนำ)
import {
to = aws_instance.web
id = "i-0123456789abcdef0"
}
# จากนั้นรัน
terraform plan -generate-config-out=generated.tf
ตั้งแต่ Terraform 1.5 เป็นต้นมา มี import block ที่ทำให้ import ง่ายขึ้นมาก ไม่ต้องเขียน resource block ก่อน แค่ระบุ import block แล้วรัน plan ด้วย flag -generate-config-out Terraform จะ generate configuration ให้อัตโนมัติ เหลือแค่ review และปรับแต่ง code ให้สวยงามครับ
แยกไฟล์ตาม concern ไม่ใส่ทุกอย่างใน main.tf ไฟล์เดียว ใช้ network.tf สำหรับ VPC และ subnet, compute.tf สำหรับ EC2 และ ASG, database.tf สำหรับ RDS, security.tf สำหรับ security groups และ IAM
ตั้งชื่อ resource ให้สื่อความหมาย ใช้ snake_case สำหรับ resource name ใช้ tags สำหรับ human-readable name และใส่ tag ManagedBy = "terraform" ทุก resource เพื่อแยกแยะว่า resource ไหนจัดการด้วย Terraform
ไม่เก็บ state file ใน Git เพราะมี sensitive data เช่น database password, API keys ใช้ remote backend ที่ encrypt at rest เปิด versioning เพื่อ recovery และใช้ DynamoDB locking สำหรับ AWS หรือ equivalent สำหรับ cloud อื่น
ทุก Terraform change ต้องผ่าน pull request review เช่นเดียวกับ application code ใส่ PR template ที่บอกว่า plan output เป็นอย่างไร resource อะไรจะถูกสร้าง แก้ไข หรือลบ มี cost estimation หรือไม่ เพื่อให้ reviewer ตัดสินใจได้อย่างมีข้อมูลครับ
ถ้า Terraform apply ค้างหรือ crash state lock อาจไม่ถูก release ใช้ terraform force-unlock LOCK_ID เพื่อ unlock แต่ต้องมั่นใจว่าไม่มีคนอื่นกำลัง apply อยู่ ตรวจสอบด้วย DynamoDB scan ก่อน unlock ครับ
เมื่อ upgrade provider version อาจเจอ breaking change ใช้ version constraint เช่น ~> 5.0 เพื่อ pin major version แล้ว upgrade ทีละ minor version ทดสอบด้วย plan ก่อนทุกครั้ง
ถ้ามีคน manual change resource บน console Terraform จะตรวจพบตอน plan แสดงเป็น update in-place ถ้าต้องการให้ Terraform เป็น source of truth รัน apply เพื่อ reconcile ถ้าต้องการเก็บ manual change ใช้ terraform state pull แล้ว update code ให้ตรงกับ state ครับ
Terraform เป็น declarative tool สำหรับ provisioning infrastructure เช่น สร้าง VM, VPC, Load Balancer ส่วน Ansible เป็น procedural tool สำหรับ configuration management เช่น ติดตั้ง software ตั้งค่า config ใช้คู่กันได้ดี โดย Terraform สร้าง infrastructure ก่อน แล้ว Ansible เข้าไป configure เป็น workflow ที่องค์กรส่วนใหญ่ใช้ครับ
สำหรับทีม ควรเก็บใน remote backend เช่น AWS S3 + DynamoDB locking, Google Cloud Storage, Azure Blob Storage หรือ Terraform Cloud ไม่ควรเก็บใน local filesystem หรือ commit เข้า git เพราะอาจมี sensitive data เช่น database password ที่ Terraform เก็บไว้ใน state ครับ
Terraform CLI เป็น open source ใช้ได้ฟรี 100% ภายใต้ BSL license ส่วน Terraform Cloud มี free tier สำหรับทีมเล็กไม่เกิน 5 users และ HCP Terraform เป็น paid version สำหรับองค์กรขนาดใหญ่ที่ต้องการ feature เพิ่มเติม เช่น SSO, audit log และ policy as code ครับ
terraform plan แสดง preview ว่าจะเปลี่ยนแปลงอะไรบ้าง สร้าง resource ใหม่กี่ตัว แก้ไขกี่ตัว ลบกี่ตัว โดยไม่ทำจริง ส่วน terraform apply จะทำการเปลี่ยนแปลงจริง ควรรัน plan ก่อนทุกครั้งเพื่อตรวจสอบว่าผลลัพธ์ตรงตามที่คาดหวัง ป้องกัน accidental destruction ครับ
Module คือ reusable package ของ Terraform configuration ที่รวม resource หลายตัวเข้าด้วยกัน เช่น module vpc อาจสร้าง VPC, Subnets, Route Tables, NAT Gateway ทั้งหมดในคำสั่งเดียว ช่วยลด code ซ้ำซ้อน maintain ง่ายขึ้น และใช้ module จาก Terraform Registry ที่คนอื่นเขียนไว้แล้วได้ด้วยครับ
อ่านเพิ่มเติมเกี่ยวกับ IT Infrastructure และ DevOps Automation รวมถึง Network Solutions และ Cloud Management
ใช้ Ansible Automation ควบคู่กับ Terraform สำหรับ configuration management หรืออ่าน Kubernetes HA Cluster เพื่อ deploy container orchestration ด้วย Terraform
สนใจระบบ เทรด Forex อัตโนมัติ หรือ สัญญาณเทรด XMSignal ติดตามได้ที่เว็บไซต์ครับ
Terraform เปลี่ยนวิธีที่ทีม DevOps จัดการ infrastructure อย่างสิ้นเชิง จากการคลิก GUI ที่ทำซ้ำไม่ได้มาเป็น code ที่ version controlled, reviewable และ reproducible ได้ 100 เปอร์เซ็นต์ ด้วย HCL ที่เรียนรู้ง่าย provider ecosystem ที่กว้างที่สุดในโลก และ module system ที่ยืดหยุ่น Terraform เป็นเครื่องมือที่ทุกทีม DevOps ควรเรียนรู้และนำไปใช้ครับ
เริ่มจากโปรเจ็คเล็กๆ เช่น สร้าง VPC กับ EC2 แล้วค่อยขยายไปจัดการ infrastructure ทั้งหมด ลงทุนเวลาเรียนรู้ Terraform จะคืนทุนภายในเดือนแรก เมื่อคุณสามารถ recreate production environment ได้ภายในนาทีแทนที่จะเป็นวัน และไม่ต้อง worry เรื่อง configuration drift อีกต่อไปครับ