Home > Blog > tech

IaC Patterns คืออะไร? รูปแบบการจัดการ Infrastructure as Code สำหรับ DevOps 2026

infrastructure as code patterns guide
Infrastructure as Code Patterns Guide 2026
2026-04-10 | tech | 3900 words

Infrastructure as Code (IaC) เปลี่ยนวิธีที่เราจัดการ Infrastructure จากการ Click ผ่าน Console มาเป็นการเขียน Code ที่ Version Control ได้ Review ได้ Test ได้ และ Reproduce ได้ แต่แค่ใช้ IaC Tool อย่างเดียวยังไม่พอ — คุณต้องรู้ Patterns ที่ถูกต้องด้วย

บทความนี้จะสอน IaC Patterns ที่ใช้จริงใน Production ตั้งแต่หลักการพื้นฐานจนถึงแนวปฏิบัติขั้นสูง ครอบคลุม Terraform, Pulumi, CDK และ Tools อื่นๆ

หลักการพื้นฐานของ IaC

Idempotency

รัน Code เดิมกี่ครั้งก็ได้ผลลัพธ์เหมือนกัน ไม่สร้าง Resource ซ้ำ ไม่พัง State

Immutability

แทนที่จะแก้ไข Server ที่มีอยู่ (Mutable) ให้สร้าง Server ใหม่แล้วทำลายตัวเก่า (Immutable) ลด Configuration Drift

Declarative vs Imperative

แนวทางคำอธิบายตัวอย่าง Tools
Declarativeบอกว่า "ต้องการอะไร" — Tool จัดการให้Terraform, CloudFormation, Crossplane
Imperativeบอกว่า "ทำอย่างไร" — สั่งทีละขั้นตอนAnsible, Shell Scripts, Pulumi (บางส่วน)
Best Practice: ใช้ Declarative เป็นหลักสำหรับ Infrastructure (VM, Network, Database) และใช้ Imperative สำหรับ Configuration Management (ติดตั้งซอฟต์แวร์ภายใน Server)

IaC Tools Landscape 2026

Toolภาษาแนวทางจุดเด่นProvider Support
TerraformHCLDeclarativeEcosystem ใหญ่ที่สุด, Multi-cloudทุก Cloud + SaaS
OpenTofuHCLDeclarativeFork ของ Terraform (Open Source)เหมือน Terraform
PulumiPython/TS/Go/C#Declarative+ใช้ภาษา Programming จริงทุก Cloud
AWS CDKTS/Python/Java/C#Declarative+สร้าง CloudFormation จาก CodeAWS เท่านั้น
CDKTFTS/Python/Go/C#Declarative+CDK + Terraform Providerทุก Cloud
CloudFormationYAML/JSONDeclarativeNative AWS, ไม่ต้อง State fileAWS เท่านั้น
CrossplaneYAML (K8s CRDs)Declarativeจัดการ Cloud ผ่าน Kubernetesทุก Cloud
AnsibleYAMLImperativeConfiguration Management ดีมากทุก Cloud + On-prem

Module / Component Patterns

Module คือหน่วยย่อยที่สามารถ Reuse ได้ เหมือน Function ในการเขียนโปรแกรม:

Terraform Module Structure

# โครงสร้าง Module
modules/
├── vpc/
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── README.md
├── eks/
│   ├── main.tf
│   ├── variables.tf
│   └── outputs.tf
└── rds/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

# เรียกใช้ Module
module "vpc" {
  source  = "./modules/vpc"
  # หรือจาก Registry
  # source  = "terraform-aws-modules/vpc/aws"
  # version = "5.5.0"

  name       = "production-vpc"
  cidr_block = "10.0.0.0/16"
  azs        = ["ap-southeast-1a", "ap-southeast-1b", "ap-southeast-1c"]

  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = false
}

Module Versioning

# ใช้ Version Pinning เสมอ
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.5"   # >= 5.5.0, < 6.0.0
}

# สำหรับ Private Module Registry
module "app" {
  source  = "app.terraform.io/myorg/app-stack/aws"
  version = "2.1.0"
}

Environment Management

Pattern 1: Workspaces (Terraform)

# สร้าง Workspace ต่อ Environment
terraform workspace new staging
terraform workspace new production

# สลับ
terraform workspace select staging

# ใน Code ใช้ terraform.workspace
locals {
  env = terraform.workspace
  instance_type = {
    staging    = "t3.small"
    production = "t3.xlarge"
  }
}

resource "aws_instance" "app" {
  instance_type = local.instance_type[local.env]
}

Pattern 2: Directory-based (แนะนำ)

# โครงสร้างแยก Directory ต่อ Environment
infrastructure/
├── modules/          # Shared modules
│   ├── vpc/
│   ├── eks/
│   └── rds/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   ├── staging/
│   │   ├── main.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   └── production/
│       ├── main.tf
│       ├── terraform.tfvars
│       └── backend.tf

# แต่ละ Environment มี State file แยก = ปลอดภัยกว่า
แนะนำ: Directory-based ดีกว่า Workspaces เพราะแต่ละ Environment มี State แยกอิสระ ลดความเสี่ยงที่ terraform destroy จะไปลบ Production โดยไม่ตั้งใจ

State Management Patterns

Remote State

# backend.tf — เก็บ State ใน S3 + DynamoDB Lock
terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "production/vpc/terraform.tfstate"
    region         = "ap-southeast-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

# สร้าง DynamoDB Table สำหรับ State Locking
resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

State Splitting

# แยก State ตาม Layer เพื่อลดความเสี่ยง
# Layer 1: Network (เปลี่ยนน้อย)
infrastructure/network/
  → state: s3://state/network/terraform.tfstate

# Layer 2: Data (Database, Cache)
infrastructure/data/
  → state: s3://state/data/terraform.tfstate

# Layer 3: Compute (เปลี่ยนบ่อย)
infrastructure/compute/
  → state: s3://state/compute/terraform.tfstate

# อ้างอิง State ข้าม Layer ด้วย data source
data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "mycompany-terraform-state"
    key    = "production/network/terraform.tfstate"
    region = "ap-southeast-1"
  }
}

resource "aws_instance" "app" {
  subnet_id = data.terraform_remote_state.network.outputs.private_subnet_ids[0]
}

Secret Management in IaC

# WRONG: อย่าเก็บ Secret ใน Code
resource "aws_db_instance" "main" {
  password = "SuperSecret123!"  # NEVER DO THIS!
}

# Pattern 1: Vault Integration
data "vault_generic_secret" "db" {
  path = "secret/data/production/db"
}
resource "aws_db_instance" "main" {
  password = data.vault_generic_secret.db.data["password"]
}

# Pattern 2: AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db" {
  secret_id = "production/db-password"
}
resource "aws_db_instance" "main" {
  password = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)["password"]
}

# Pattern 3: SOPS (Encrypted files in Git)
# sops --encrypt --age age1... secrets.yaml > secrets.enc.yaml
data "sops_file" "secrets" {
  source_file = "secrets.enc.yaml"
}
resource "aws_db_instance" "main" {
  password = data.sops_file.secrets.data["db_password"]
}

Testing IaC

Static Analysis (ไม่ต้อง Apply)

# Terraform Validate
terraform validate

# Terraform Plan Review
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan | jq .

# tfsec — Security Scanner
tfsec .
# ผลลัพธ์: แจ้งเตือนเรื่อง Security เช่น S3 bucket ไม่เข้ารหัส

# Checkov — Policy-as-Code Scanner
checkov -d .
# ตรวจ 1000+ rules ครอบคลุม AWS, Azure, GCP, K8s

# KICS — Infrastructure Queries
kics scan -p .

Integration Testing (Terratest)

// Go: ใช้ Terratest สร้าง Infrastructure จริง แล้ว Test แล้วทำลาย
func TestVpcModule(t *testing.T) {
    terraformOptions := &terraform.Options{
        TerraformDir: "../modules/vpc",
        Vars: map[string]interface{}{
            "name":       "test-vpc",
            "cidr_block": "10.99.0.0/16",
        },
    }

    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)

    vpcID := terraform.Output(t, terraformOptions, "vpc_id")
    assert.NotEmpty(t, vpcID)

    // ตรวจสอบว่า VPC สร้างจริง
    vpc := aws.GetVpcById(t, vpcID, "ap-southeast-1")
    assert.Equal(t, "10.99.0.0/16", vpc.CidrBlock)
}

Policy-as-Code (OPA / Sentinel)

# OPA (Open Policy Agent) Rego Policy
# ห้ามสร้าง Public S3 Bucket
deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_s3_bucket"
  resource.change.after.acl == "public-read"
  msg := sprintf("S3 bucket '%s' must not be public", [resource.address])
}

# ห้ามใช้ Instance ใหญ่เกินไป
deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_instance"
  not startswith(resource.change.after.instance_type, "t3.")
  msg := sprintf("Instance '%s' must use t3.* type", [resource.address])
}

CI/CD for IaC

GitOps Workflow

# GitHub Actions: Terraform CI/CD
name: Terraform
on:
  pull_request:
    paths: ['infrastructure/**']
  push:
    branches: [main]
    paths: ['infrastructure/**']

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Terraform Init
        run: terraform init
        working-directory: infrastructure/environments/production

      - name: Terraform Format Check
        run: terraform fmt -check -recursive

      - name: Terraform Validate
        run: terraform validate

      - name: tfsec Security Scan
        uses: aquasecurity/tfsec-action@v1.0.0

      - name: Terraform Plan
        if: github.event_name == 'pull_request'
        run: terraform plan -no-color -out=plan.tfplan

      - name: Comment Plan on PR
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            // Post plan output as PR comment

      - name: Terraform Apply
        if: github.ref == 'refs/heads/main'
        run: terraform apply -auto-approve plan.tfplan

Atlantis (Self-hosted)

# atlantis.yaml — Terraform PR Automation
version: 3
projects:
  - name: production-vpc
    dir: infrastructure/environments/production/vpc
    workflow: production
    autoplan:
      when_modified: ["*.tf", "*.tfvars"]
      enabled: true

workflows:
  production:
    plan:
      steps:
        - init
        - plan:
            extra_args: ["-lock=false"]
    apply:
      steps:
        - apply

# PR Comment Commands:
# atlantis plan — Run plan
# atlantis apply — Apply changes (requires approval)

Drift Detection and Remediation

Drift เกิดเมื่อ Infrastructure จริงไม่ตรงกับ Code (เช่น คนไปแก้ผ่าน Console):

# ตรวจจับ Drift
terraform plan -detailed-exitcode
# Exit code 0 = no changes
# Exit code 1 = error
# Exit code 2 = changes detected (DRIFT!)

# Remediation Options:
# 1. terraform apply — บังคับให้ตรงกับ Code
# 2. terraform import — Import resource ที่สร้างมือเข้ามา
# 3. terraform state rm — ลบ resource จาก State (ไม่ลบจริง)

# Automated Drift Detection (Cron)
# รัน terraform plan ทุก 6 ชั่วโมง แล้ว Alert ถ้าเจอ Drift
0 */6 * * * cd /infra/production && terraform plan -detailed-exitcode || alert "Drift detected!"

IaC for Multi-Cloud

# Terraform: Multi-Cloud in one project
provider "aws" {
  region = "ap-southeast-1"
  alias  = "singapore"
}

provider "google" {
  project = "my-gcp-project"
  region  = "asia-southeast1"
}

# AWS Resources
resource "aws_s3_bucket" "primary" {
  provider = aws.singapore
  bucket   = "my-primary-data"
}

# GCP Resources (DR)
resource "google_storage_bucket" "dr" {
  name     = "my-dr-data"
  location = "ASIA-SOUTHEAST1"
}

# Cross-cloud networking
resource "aws_vpn_gateway" "aws_side" {
  vpc_id = module.vpc.vpc_id
}

resource "google_compute_vpn_gateway" "gcp_side" {
  name    = "aws-interconnect"
  network = module.gcp_vpc.network_id
}

Monorepo vs Polyrepo for IaC

แนวทางจุดเด่นจุดด้อยเหมาะกับ
Monorepoค้นหาง่าย, Atomic changes, Shared modules ง่ายCI ช้า, Permission ซับซ้อนทีมเล็ก-กลาง
Polyrepoแยก Permission ชัด, CI เร็ว, Team ownershipCode duplication, Dependency managementทีมใหญ่, หลาย Business Unit
# Monorepo Structure
infrastructure/
├── modules/           # Shared modules
├── environments/
│   ├── dev/
│   ├── staging/
│   └── production/
├── policies/          # OPA policies
├── tests/             # Terratest
├── .github/workflows/ # CI/CD
└── Makefile

# Polyrepo Structure
org/infra-modules      # Shared module library
org/infra-network      # Network team owns this
org/infra-compute      # Compute team owns this
org/infra-data         # Data team owns this

Cost Estimation (Infracost)

# ติดตั้ง
brew install infracost

# ดูค่าใช้จ่ายก่อน Apply
infracost breakdown --path .

# ผลลัพธ์:
# Name                    Monthly Qty  Unit   Monthly Cost
# aws_instance.app
# ├─ Instance usage        730  hrs     $60.74
# ├─ root_block_device     50   GB      $5.00
# aws_rds_cluster.main
# ├─ Instance usage        730  hrs     $175.20
# ├─ Storage               100  GB      $10.00
# OVERALL TOTAL                         $250.94/mo

# เปรียบเทียบ Cost ก่อน/หลังเปลี่ยน
infracost diff --path . --compare-to infracost-base.json

# ใส่ใน PR Comment
infracost comment github \
  --path /tmp/infracost.json \
  --repo myorg/infra \
  --pull-request 42 \
  --github-token $GITHUB_TOKEN

IaC Documentation (terraform-docs)

# ติดตั้ง
brew install terraform-docs

# สร้าง README.md อัตโนมัติจาก Terraform code
terraform-docs markdown table ./modules/vpc > ./modules/vpc/README.md

# ผลลัพธ์: สร้างตาราง Inputs, Outputs, Resources, Requirements
# ใส่ใน pre-commit hook เพื่อ Auto-update

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/terraform-docs/terraform-docs
    rev: v0.18.0
    hooks:
      - id: terraform-docs-go
        args: ["markdown", "table", "--output-file", "README.md"]

Migration from ClickOps to IaC

  1. Inventory — สำรวจ Resource ทั้งหมดที่มี (ใช้ AWS Config, Cloud Asset Inventory)
  2. Import — นำ Resource เข้า Terraform State
  3. Generate — ใช้ Tool สร้าง Code จาก Resource ที่มี
  4. Validateterraform plan ต้องแสดง "No changes"
  5. Iterate — Refactor Code ให้ใช้ Modules
# Import Resource ที่สร้างมือ
terraform import aws_instance.app i-0123456789abcdef

# ใช้ Terraformer สร้าง Code อัตโนมัติ
terraformer import aws --resources=vpc,subnet,sg --regions=ap-southeast-1

# ใช้ terraform plan เช็คว่า Import ถูกต้อง
terraform plan
# Expected: "No changes. Your infrastructure matches the configuration."

IaC Anti-Patterns (สิ่งที่ไม่ควรทำ)

สรุป

Infrastructure as Code ไม่ใช่แค่เรื่องของ Tool แต่เป็นวิธีคิดที่ว่า Infrastructure ควรถูกจัดการเหมือน Software — มี Version Control, Code Review, Testing, CI/CD และ Documentation ที่ดี

ในปี 2026 Terraform (หรือ OpenTofu) ยังคงเป็น Tool ที่ได้รับความนิยมสูงสุด แต่ Pulumi และ CDK กำลังเติบโตเร็วมากสำหรับทีมที่ต้องการใช้ภาษา Programming จริงๆ ไม่ว่าจะเลือก Tool ไหน สิ่งสำคัญที่สุดคือ Patterns ที่ถูกต้อง: แยก State, ใช้ Modules, Test ก่อน Apply, และอย่าเก็บ Secret ใน Code


Back to Blog | iCafe Forex | SiamLanCard | Siam2R