IaC Terraform Ansible DevOps

Infrastructure as Code — จัดการ Server ด้วย Terraform & AnsibleInfrastructure as Code — Managing Servers with Terraform & Ansible

By Anirach Mingkhwan DevOps & Vibe Coding 2026
Infrastructure as Code — จัดการ Server ด้วย Terraform
ในบทความนี้
  1. IaC คืออะไร?
  2. Terraform vs Ansible — ใช้ตัวไหน?
  3. Terraform — สร้าง Infrastructure ด้วย Code
  4. Ansible — ตั้งค่า Server อัตโนมัติ
  5. Terraform + Ansible ทำงานร่วมกัน
  6. IaC Tools อื่นๆ ที่น่ารู้
  7. Best Practices
  8. สรุป
In this post
  1. What is IaC?
  2. Terraform vs Ansible — which one?
  3. Terraform — building infrastructure with code
  4. Ansible — configuring servers automatically
  5. Terraform + Ansible working together
  6. Other IaC tools worth knowing
  7. Best Practices
  8. Summary

จากโพสต์ที่แล้วเราเรียนรู้ Networking พื้นฐาน — DNS, Firewall, Load Balancer, SSH

แต่ลองนึกภาพว่าต้อง setup server 50 เครื่อง... จะ SSH เข้าไปตั้งค่าทีละเครื่องจริงเหรอ? 😰

นี่คือที่มาของ Infrastructure as Code (IaC) — เขียน code เพื่อสร้างและจัดการ infrastructure ทั้งหมด แทนที่จะทำด้วยมือ เหมือนเขียนสูตรอาหารแทนที่จะจำในหัว 📝🐕


IaC คืออะไร?

Infrastructure as Code คือแนวคิดที่ว่า — infrastructure (server, network, database, firewall) ควรถูก กำหนดด้วย code ไม่ใช่การคลิกบน console หรือพิมพ์คำสั่งทีละบรรทัด

ข้อดีของ IaC: repeatable (ทำซ้ำได้เหมือนเดิมทุกครั้ง), version controlled (เก็บใน Git ดู history ได้), reviewable (review infrastructure changes เหมือน code review), testable (ทดสอบก่อน apply ได้) — เปรียบเทียบก่อนและหลัง IaC:

ก่อน IaC (Manual) 😩

  1. Login เข้า AWS Console
  2. คลิก "Create EC2 Instance"
  3. เลือก Ubuntu, t3.medium, 50GB disk...
  4. SSH เข้าไปติดตั้ง Nginx, Node.js, PostgreSQL...
  5. ทำ 50 ครั้ง ❌

หลัง IaC (Automated) 🚀

  1. เขียน Terraform → สร้าง 50 servers
  2. เขียน Ansible → ติดตั้ง software ทั้ง 50 เครื่อง
  3. รัน 1 คำสั่ง ✅
💡 หลักการสำคัญ:
✅ Repeatable — ทำซ้ำได้ผลเหมือนเดิมทุกครั้ง
✅ Version-controlled — เก็บใน Git, ดู history ได้
✅ Reviewable — ทีม review ก่อน apply
✅ Testable — ทดสอบก่อน deploy ได้

Terraform vs Ansible — ใช้ตัวไหน?

คำตอบคือ ใช้ทั้งคู่! เพราะทำหน้าที่ต่างกัน — Terraform "สร้าง" infrastructure (VMs, networks, databases, load balancers) ส่วน Ansible "ตั้งค่า" สิ่งที่สร้างมา (install software, configure services, deploy apps) Terraform เป็น declarative (บอกว่า "ต้องการอะไร") Ansible เป็น procedural (บอกว่า "ทำอะไรทีละขั้น"):

📐 Terraform
สร้าง Infrastructure
→
⚙️ Ansible
ตั้งค่า Software
→
🚀 Production
พร้อมใช้งาน!
Feature Terraform Ansible
หน้าที่หลัก สร้าง/จัดการ infrastructure ตั้งค่า/ติดตั้ง software
ประเภท Provisioning tool Configuration Management tool
ภาษา HCL (HashiCorp Config Language) YAML (Playbooks)
วิธีทำงาน Declarative — บอก "ต้องการอะไร" Procedural + Declarative
State ✅ มี state file ติดตาม ❌ Agentless, ไม่มี state
ตัวอย่าง สร้าง EC2, VPC, RDS, S3 ติดตั้ง Nginx, Deploy app
Cloud Support AWS, GCP, Azure, DigitalOcean, +100 providers SSH เข้าเครื่องไหนก็ได้
💡 จำง่ายๆ: Terraform = สร้างเครื่อง | Ansible = ตั้งค่าเครื่อง

Terraform — สร้าง Infrastructure ด้วย Code

Terraform โดย HashiCorp เป็น IaC tool ที่ได้รับความนิยมสูงสุด — รองรับ cloud providers ทุกเจ้า (AWS, Azure, GCP, DigitalOcean, etc.) ด้วย provider plugins ใช้ภาษา HCL (HashiCorp Configuration Language) ที่อ่านง่าย และจัดการ state ของ infrastructure ให้อัตโนมัติ

Terraform ทำงานยังไง?

Terraform workflow มี 3 ขั้นตอนหลัก: Write (เขียน .tf files), Plan (ดูว่าจะเปลี่ยนอะไร — เหมือน dry run), Apply (สร้าง/แก้ infrastructure จริง) สิ่งสำคัญคือ terraform plan แสดง diff ก่อน apply เสมอ — ไม่มีเซอร์ไพรส์!

📝 Write
เขียน .tf files
→
📋 Plan
ดู preview การเปลี่ยนแปลง
→
🚀 Apply
สร้างจริง!
→
💾 State
บันทึกสถานะ

ตัวอย่าง: สร้าง AWS EC2 Instance

ตัวอย่าง main.tf ด้านล่างสร้าง EC2 instance บน AWS — สังเกตว่า HCL อ่านง่ายมาก: กำหนด provider (AWS, region), resource (instance type, AMI, security group), และ output (IP address) ทุกอย่างเป็น declarative:

# กำหนด provider (Cloud ที่จะใช้)
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-southeast-1"    # Singapore
}

# สร้าง EC2 Instance
resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"   # Ubuntu 22.04
  instance_type = "t3.medium"

  tags = {
    Name        = "web-server-01"
    Environment = "production"
    ManagedBy   = "terraform"
  }

  # Security Group
  vpc_security_group_ids = [aws_security_group.web_sg.id]
}

# สร้าง Security Group
resource "aws_security_group" "web_sg" {
  name        = "web-sg"
  description = "Allow HTTP and SSH"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]     # HTTP จากทุกที่
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"]    # SSH จาก private network เท่านั้น
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Output — แสดง IP ที่ได้
output "server_ip" {
  value = aws_instance.web_server.public_ip
}
# Terraform commands
terraform init      # ดาวน์โหลด provider plugins
terraform plan      # ดู preview ว่าจะสร้างอะไร
terraform apply     # สร้างจริง! (ถาม yes/no)
terraform destroy   # ลบทั้งหมด (ระวัง!)

# ตัวอย่าง output ของ terraform plan:
# + aws_instance.web_server
#     ami:           "ami-0c55b159cbfafe1f0"
#     instance_type: "t3.medium"
#
# Plan: 2 to add, 0 to change, 0 to destroy.

Terraform สร้างหลายเครื่องพร้อมกัน

พลังของ IaC เห็นชัดเมื่อต้องสร้างหลายเครื่อง — แค่ใช้ count หรือ for_each ก็สร้าง 10, 50, 100 เครื่องได้ในคำสั่งเดียว ลองทำแบบ manual ดู!

# ใช้ count สร้าง 5 เครื่องรวด!
resource "aws_instance" "web_cluster" {
  count         = 5
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.medium"

  tags = {
    Name = "web-server-${count.index + 1}"
  }
}

# Output ทุก IP
output "server_ips" {
  value = aws_instance.web_cluster[*].public_ip
}
# → ["10.0.1.1", "10.0.1.2", "10.0.1.3", "10.0.1.4", "10.0.1.5"]

Ansible — ตั้งค่า Server อัตโนมัติ

Ansible โดย Red Hat เป็น configuration management tool ที่ใช้ง่ายที่สุด — เขียน playbooks ด้วย YAML ธรรมดา ไม่ต้องเรียนภาษาใหม่ เหมาะกับ: install packages, configure services, deploy applications, manage users/permissions

Ansible ทำงานยังไง?

Ansible เป็น agentless — ไม่ต้องติดตั้งอะไรบนเครื่องเป้าหมาย ใช้ SSH เข้าไปรันคำสั่งเลย! ต่างจาก Chef/Puppet ที่ต้องติดตั้ง agent บนทุกเครื่อง ทำให้ setup ง่ายและ maintain น้อยกว่า มี 2 ส่วนหลัก: Inventory (รายชื่อ servers) และ Playbook (สิ่งที่ต้องทำ):

Ansible Architecture
Control Node (เครื่องเรา)
Playbook (YAML)
Inventory (host list)
Ansible Engine
SSH ↓ ↓ ↓
Server 1
ไม่ต้องติดตั้งอะไร!
Server 2
ไม่ต้องติดตั้งอะไร!
Server 3
ไม่ต้องติดตั้งอะไร!

Step 1: Inventory — บอกว่ามี server อะไรบ้าง

Inventory คือไฟล์ที่บอก Ansible ว่า "มี server อะไรบ้าง แบ่งเป็นกลุ่มอะไร" สามารถจัดกลุ่มเป็น webservers, databases, monitoring ได้ — แล้วรัน playbook เฉพาะกลุ่ม:

all:
  children:
    webservers:
      hosts:
        web1:
          ansible_host: 10.0.1.1
        web2:
          ansible_host: 10.0.1.2
        web3:
          ansible_host: 10.0.1.3
    databases:
      hosts:
        db1:
          ansible_host: 10.0.2.1
  vars:
    ansible_user: deploy
    ansible_ssh_private_key_file: ~/.ssh/id_ed25519

Step 2: Playbook — บอกว่าต้องทำอะไร

Playbook คือ YAML file ที่อธิบาย "ต้องทำอะไรบ้าง" ทีละ task — Ansible จะรันตามลำดับ แต่ละ task ใช้ module (เช่น apt, copy, service, template) ที่เป็น idempotent คือรันกี่ครั้งก็ได้ผลเหมือนกัน ตัวอย่าง setup web server:

---
- name: Setup Web Servers
  hosts: webservers
  become: yes          # ใช้ sudo

  tasks:
    # อัพเดท system
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    # ติดตั้ง Nginx
    - name: Install Nginx
      apt:
        name: nginx
        state: present

    # ติดตั้ง Node.js 20
    - name: Add NodeSource repository
      shell: curl -fsSL https://deb.nodesource.com/setup_20.x | bash -

    - name: Install Node.js
      apt:
        name: nodejs
        state: present

    # Copy config file
    - name: Deploy Nginx config
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/myapp
      notify: Restart Nginx

    # เปิด firewall
    - name: Allow HTTP through firewall
      ufw:
        rule: allow
        port: "80"
        proto: tcp

    # ตรวจสอบว่า Nginx ทำงาน
    - name: Ensure Nginx is running
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted
# รัน playbook
ansible-playbook -i inventory.yml setup-webserver.yml

# ดูผลลัพธ์:
# PLAY [Setup Web Servers] *************************************
# TASK [Install Nginx] *****************************************
# changed: [web1]
# changed: [web2]
# changed: [web3]
#
# PLAY RECAP ***************************************************
# web1: ok=7  changed=5  unreachable=0  failed=0
# web2: ok=7  changed=5  unreachable=0  failed=0
# web3: ok=7  changed=5  unreachable=0  failed=0

Ansible Ad-hoc Commands — ไม่ต้องเขียน playbook

สำหรับงานเร็วๆ ที่ไม่ต้องเขียน playbook — ใช้ ad-hoc commands รันคำสั่งเดียวบนหลายเครื่องพร้อมกัน เช่น check uptime ทุกเครื่อง, restart service, copy file:

# Ping ทุกเครื่อง
ansible all -i inventory.yml -m ping

# ดู disk space ทุก webserver
ansible webservers -i inventory.yml -a "df -h"

# ติดตั้ง package ด่วน
ansible webservers -i inventory.yml -m apt -a "name=htop state=present" --become

# Restart service
ansible webservers -i inventory.yml -m service -a "name=nginx state=restarted" --become

Terraform + Ansible ทำงานร่วมกัน

Workflow ที่ดีที่สุดคือใช้ ทั้งคู่ร่วมกัน — Terraform สร้าง infrastructure (VMs, VPC, Load Balancer) แล้วส่ง output (IP addresses) ให้ Ansible ไปตั้งค่า (install Docker, deploy app, configure monitoring) เหมือน Terraform "สร้างบ้าน" แล้ว Ansible "ตกแต่งภายใน":

Complete IaC Pipeline
Terraform
สร้าง VPC
สร้าง EC2 × 5
สร้าง RDS
สร้าง Load Balancer
→
Ansible
ติดตั้ง Docker
Deploy Application
ตั้งค่า Monitoring
Harden Security
→
Production
✅ Ready!
🔒 Secured
📊 Monitored
# 1. Terraform สร้าง infrastructure
cd terraform/
terraform apply -auto-approve

# 2. Export IP ที่ได้ เป็น Ansible inventory
terraform output -json server_ips | \
  jq -r '.[] | "  " + . ' > ../ansible/hosts.txt

# 3. Ansible ตั้งค่าทุกเครื่อง
cd ../ansible/
ansible-playbook -i inventory.yml setup-all.yml

IaC Tools อื่นๆ ที่น่ารู้

นอกจาก Terraform + Ansible ยังมี IaC tools อื่นๆ ที่น่าสนใจ — แต่ละตัวมีจุดแข็งต่างกัน เลือกตามความเหมาะสมของทีมและ use case:

Tool ประเภท จุดเด่น
Terraform Provisioning HCL, multi-cloud, state management
Ansible Config Management YAML, agentless, simple
Pulumi Provisioning เขียนด้วย Python/TypeScript/Go แทน HCL
CloudFormation Provisioning AWS native, JSON/YAML
Chef / Puppet Config Management Enterprise-grade, agent-based
OpenTofu Provisioning Terraform fork (open-source)

Best Practices

IaC ทำให้ infrastructure เป็น code ดังนั้นต้อง treat เหมือน code จริงๆ — version control, code review, testing, modularization หลักการสำคัญที่ต้องทำ:

  1. เก็บ IaC ใน Git — ทุก infrastructure change ต้องผ่าน PR review
  2. ใช้ modules — แยก code เป็น module ที่ reuse ได้
  3. แยก environment — dev / staging / production ใช้ config คนละชุด
  4. อย่าเก็บ secrets ใน code — ใช้ Vault, AWS Secrets Manager, หรือ environment variables
  5. ใช้ remote state — Terraform state เก็บใน S3 + DynamoDB lock ไม่ใช่ local
  6. Plan ก่อน Apply เสมอ — อ่าน plan ให้ดีก่อนกด yes!
  7. Idempotent — รันกี่ครั้งก็ได้ผลเหมือนเดิม
# Terraform remote state (best practice)
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "ap-southeast-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}
🐕 กฎทอง: ถ้า infrastructure ไม่ได้อยู่ใน code = มันไม่มีอยู่จริง ทุกอย่างต้อง reproducible!

สรุป

Infrastructure as Code เปลี่ยนวิธีจัดการ infrastructure จาก "จำในหัว + ทำด้วยมือ" เป็น "เขียน code + automate ทั้งหมด" ผลลัพธ์คือ: สร้าง environment ใหม่ได้ในนาที, disaster recovery เร็วขึ้น 100 เท่า, ทุกการเปลี่ยนแปลงมี audit trail ใน Git:

  • IaC = จัดการ infrastructure ด้วย code แทนการทำมือ
  • Terraform = สร้าง infrastructure (EC2, VPC, RDS) ด้วย HCL → plan → apply
  • Ansible = ตั้งค่า software (Nginx, Docker, app) ด้วย YAML Playbooks ผ่าน SSH
  • ใช้ทั้งคู่ = Terraform สร้างเครื่อง → Ansible ตั้งค่าเครื่อง → Production ready!
  • Best Practices = Git, modules, remote state, แยก env, ไม่เก็บ secrets ใน code
  • เริ่มต้นง่ายๆ: terraform init → plan → apply + ansible-playbook 📝🐕

In the previous post we covered the networking fundamentals — DNS, firewalls, load balancers, SSH.

But picture having to set up 50 servers... are you really going to SSH into each one and configure it by hand? 😰

This is where Infrastructure as Code (IaC) comes in — writing code to create and manage your entire infrastructure instead of doing it by hand. Like writing the recipe down instead of keeping it in your head 📝🐕


What is IaC?

Infrastructure as Code is the idea that infrastructure — servers, networks, databases, firewalls — should be defined in code, not clicked together in a console or typed one command at a time.

The benefits of IaC: repeatable (the same result every time), version controlled (kept in Git, with a history you can read), reviewable (infrastructure changes go through review, just like code), testable (you can test before you apply) — compare life before and after IaC:

Before IaC (Manual) 😩

  1. Log in to the AWS Console
  2. Click "Create EC2 Instance"
  3. Pick Ubuntu, t3.medium, 50GB disk...
  4. SSH in and install Nginx, Node.js, PostgreSQL...
  5. Do it 50 times ❌

After IaC (Automated) 🚀

  1. Write Terraform → 50 servers created
  2. Write Ansible → software installed on all 50
  3. Run one command ✅
💡 The core principles:
✅ Repeatable — the same result on every run
✅ Version-controlled — kept in Git, with a readable history
✅ Reviewable — the team reviews before you apply
✅ Testable — test it before you deploy

Terraform vs Ansible — which one?

The answer is both! Because they do different jobs — Terraform "builds" infrastructure (VMs, networks, databases, load balancers) while Ansible "configures" what was built (install software, configure services, deploy apps). Terraform is declarative (you state "what you want"); Ansible is procedural (you state "what to do, step by step"):

📐 Terraform
Builds the infrastructure
→
⚙️ Ansible
Configures the software
→
🚀 Production
Ready to serve!
Feature Terraform Ansible
Primary job Create/manage infrastructure Configure/install software
Category Provisioning tool Configuration Management tool
Language HCL (HashiCorp Config Language) YAML (Playbooks)
How it works Declarative — you state "what you want" Procedural + Declarative
State ✅ Tracks a state file ❌ Agentless, no state
Examples Create EC2, VPC, RDS, S3 Install Nginx, deploy an app
Cloud Support AWS, GCP, Azure, DigitalOcean, +100 providers Any machine you can SSH into
💡 An easy way to remember it: Terraform = build the machine | Ansible = configure the machine

Terraform — building infrastructure with code

Terraform, by HashiCorp, is the most widely adopted IaC tool — it supports every cloud provider (AWS, Azure, GCP, DigitalOcean, etc.) through provider plugins, uses the readable HCL (HashiCorp Configuration Language), and manages your infrastructure's state for you.

How does Terraform work?

The Terraform workflow has three main steps: Write (author your .tf files), Plan (see what will change — a dry run), Apply (actually create or modify the infrastructure). The important part is that terraform plan always shows you the diff before you apply — no surprises!

📝 Write
Author the .tf files
→
📋 Plan
Preview the changes
→
🚀 Apply
Build it for real!
→
💾 State
Record the state

Example: creating an AWS EC2 instance

The main.tf below creates an EC2 instance on AWS — notice how readable HCL is: you declare the provider (AWS, region), the resource (instance type, AMI, security group), and an output (the IP address). Everything is declarative:

# Declare the provider (the cloud you will use)
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-southeast-1"    # Singapore
}

# Create an EC2 instance
resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"   # Ubuntu 22.04
  instance_type = "t3.medium"

  tags = {
    Name        = "web-server-01"
    Environment = "production"
    ManagedBy   = "terraform"
  }

  # Security Group
  vpc_security_group_ids = [aws_security_group.web_sg.id]
}

# Create the Security Group
resource "aws_security_group" "web_sg" {
  name        = "web-sg"
  description = "Allow HTTP and SSH"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]     # HTTP from anywhere
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"]    # SSH from the private network only
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Output — show the IP we got
output "server_ip" {
  value = aws_instance.web_server.public_ip
}
# Terraform commands
terraform init      # download the provider plugins
terraform plan      # preview what will be created
terraform apply     # build it for real! (asks yes/no)
terraform destroy   # tear it all down (careful!)

# Sample terraform plan output:
# + aws_instance.web_server
#     ami:           "ami-0c55b159cbfafe1f0"
#     instance_type: "t3.medium"
#
# Plan: 2 to add, 0 to change, 0 to destroy.

Terraform builds many machines at once

The power of IaC becomes obvious the moment you need more than one machine — count or for_each will build 10, 50 or 100 servers from a single command. Try that by hand!

# Use count to build 5 machines in one go!
resource "aws_instance" "web_cluster" {
  count         = 5
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.medium"

  tags = {
    Name = "web-server-${count.index + 1}"
  }
}

# Output every IP
output "server_ips" {
  value = aws_instance.web_cluster[*].public_ip
}
# → ["10.0.1.1", "10.0.1.2", "10.0.1.3", "10.0.1.4", "10.0.1.5"]

Ansible — configuring servers automatically

Ansible, by Red Hat, is the easiest configuration management tool to pick up — you write playbooks in ordinary YAML, with no new language to learn. It is a good fit for installing packages, configuring services, deploying applications, and managing users and permissions.

How does Ansible work?

Ansible is agentless — nothing has to be installed on the target machine; it simply SSHes in and runs the commands. That is unlike Chef or Puppet, which need an agent on every host, and it makes setup easier and maintenance lighter. There are two main pieces: the Inventory (the list of servers) and the Playbook (what to do):

Ansible Architecture
Control Node (your machine)
Playbook (YAML)
Inventory (host list)
Ansible Engine
SSH ↓ ↓ ↓
Server 1
Nothing to install!
Server 2
Nothing to install!
Server 3
Nothing to install!

Step 1: Inventory — tell it which servers exist

The Inventory is the file that tells Ansible "these are the servers, and these are the groups they fall into". You can group them as webservers, databases, monitoring — then run a playbook against one group only:

all:
  children:
    webservers:
      hosts:
        web1:
          ansible_host: 10.0.1.1
        web2:
          ansible_host: 10.0.1.2
        web3:
          ansible_host: 10.0.1.3
    databases:
      hosts:
        db1:
          ansible_host: 10.0.2.1
  vars:
    ansible_user: deploy
    ansible_ssh_private_key_file: ~/.ssh/id_ed25519

Step 2: Playbook — tell it what to do

A Playbook is a YAML file describing what has to happen, one task at a time — Ansible runs them in order. Each task uses a module (apt, copy, service, template, and so on) that is idempotent: run it any number of times and the result is the same. Here is a web server setup:

---
- name: Setup Web Servers
  hosts: webservers
  become: yes          # use sudo

  tasks:
    # Update the system
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    # Install Nginx
    - name: Install Nginx
      apt:
        name: nginx
        state: present

    # Install Node.js 20
    - name: Add NodeSource repository
      shell: curl -fsSL https://deb.nodesource.com/setup_20.x | bash -

    - name: Install Node.js
      apt:
        name: nodejs
        state: present

    # Copy config file
    - name: Deploy Nginx config
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/myapp
      notify: Restart Nginx

    # Open the firewall
    - name: Allow HTTP through firewall
      ufw:
        rule: allow
        port: "80"
        proto: tcp

    # Check that Nginx is running
    - name: Ensure Nginx is running
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted
# Run the playbook
ansible-playbook -i inventory.yml setup-webserver.yml

# The output:
# PLAY [Setup Web Servers] *************************************
# TASK [Install Nginx] *****************************************
# changed: [web1]
# changed: [web2]
# changed: [web3]
#
# PLAY RECAP ***************************************************
# web1: ok=7  changed=5  unreachable=0  failed=0
# web2: ok=7  changed=5  unreachable=0  failed=0
# web3: ok=7  changed=5  unreachable=0  failed=0

Ansible ad-hoc commands — no playbook required

For quick jobs that do not deserve a playbook, use ad-hoc commands to run a single command across many machines at once — check uptime everywhere, restart a service, copy a file:

# Ping every machine
ansible all -i inventory.yml -m ping

# Check disk space on every webserver
ansible webservers -i inventory.yml -a "df -h"

# Install a package in a hurry
ansible webservers -i inventory.yml -m apt -a "name=htop state=present" --become

# Restart a service
ansible webservers -i inventory.yml -m service -a "name=nginx state=restarted" --become

Terraform + Ansible working together

The best workflow uses both together — Terraform creates the infrastructure (VMs, VPC, load balancer), then hands its output (the IP addresses) to Ansible, which configures it (install Docker, deploy the app, set up monitoring). Terraform "builds the house"; Ansible "furnishes the rooms":

Complete IaC Pipeline
Terraform
Create VPC
Create EC2 × 5
Create RDS
Create Load Balancer
→
Ansible
Install Docker
Deploy Application
Set up Monitoring
Harden Security
→
Production
✅ Ready!
🔒 Secured
📊 Monitored
# 1. Terraform builds the infrastructure
cd terraform/
terraform apply -auto-approve

# 2. Export the IPs it returned as an Ansible inventory
terraform output -json server_ips | \
  jq -r '.[] | "  " + . ' > ../ansible/hosts.txt

# 3. Ansible configures every machine
cd ../ansible/
ansible-playbook -i inventory.yml setup-all.yml

Other IaC tools worth knowing

Beyond Terraform + Ansible there are other IaC tools worth a look — each has a different strength, so pick the one that suits your team and your use case:

Tool Category Strength
Terraform Provisioning HCL, multi-cloud, state management
Ansible Config Management YAML, agentless, simple
Pulumi Provisioning Written in Python/TypeScript/Go instead of HCL
CloudFormation Provisioning AWS native, JSON/YAML
Chef / Puppet Config Management Enterprise-grade, agent-based
OpenTofu Provisioning Terraform fork (open-source)

Best Practices

IaC turns infrastructure into code, so you have to treat it like real code — version control, code review, testing, modularization. The principles that matter:

  1. Keep your IaC in Git — every infrastructure change goes through PR review
  2. Use modules — split the code into reusable modules
  3. Separate your environments — dev / staging / production each get their own config
  4. Never keep secrets in code — use Vault, AWS Secrets Manager, or environment variables
  5. Use remote state — keep Terraform state in S3 with a DynamoDB lock, not locally
  6. Always plan before you apply — read the plan carefully before pressing yes!
  7. Idempotent — run it any number of times, get the same result
# Terraform remote state (best practice)
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "ap-southeast-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}
🐕 The golden rule: if the infrastructure is not in code, it does not really exist. Everything has to be reproducible!

Summary

Infrastructure as Code changes how you manage infrastructure — from "remember it and do it by hand" to "write the code and automate all of it". The results: a new environment in minutes, disaster recovery 100 times faster, and an audit trail in Git for every change:

  • IaC = manage infrastructure with code instead of by hand
  • Terraform = build the infrastructure (EC2, VPC, RDS) in HCL → plan → apply
  • Ansible = configure the software (Nginx, Docker, the app) with YAML playbooks over SSH
  • Use both = Terraform builds the machine → Ansible configures it → production ready!
  • Best Practices = Git, modules, remote state, separate environments, no secrets in code
  • An easy place to start: terraform init → plan → apply plus ansible-playbook 📝🐕
บทความจากซีรีส์ DevOps & Vibe Coding 2026An article from the DevOps & Vibe Coding 2026 series
← Previous
Networking พื้นฐานสำหรับ DevOps — รู้จักเครือข่ายก่อน Deploy
Next →
Monitoring & Observability — รู้ทุกอย่างที่เกิดขึ้นใน System