Tool Imperatif #
Infrastructure as Code memiliki dua paradigma utama: imperatif dan deklaratif. Terraform memilih jalur deklaratif — mendefinisikan apa yang diinginkan dan membiarkan tool yang menentukan cara mencapainya. Tapi sebelum deklaratif populer, dan bahkan sampai sekarang, banyak tool yang mengandalkan pendekatan imperatif: kamu menulis langkah-langkah eksekusi secara berurutan, dari langkah pertama sampai terakhir. Script bash, Ansible, Chef, Puppet, bahkan AWS CLI yang kamu jalankan di terminal — semuanya beroperasi dengan model imperatif.
Memahami cara kerja tool imperatif bukan sekadar pengetahuan historis. Banyak tim yang masih menggunakan kombinasi Terraform dan tool imperatif, dan mengetahui kapan pendekatan imperatif tepat — serta kapan sebaiknya dihindari — adalah keterampilan penting bagi setiap engineer yang bekerja dengan infrastruktur.
Apa Itu Pendekatan Imperatif #
Pendekatan imperatif berarti kamu memberitahu komputer bagaimana melakukan sesuatu, langkah demi langkah. Analoginya: kalau kamu ingin pergi dari rumah ke kantor, kamu menulis instruksi “belok kiri di lampu merah, lurus 500 meter, belok kanan di bundaran, gedung ketiga di kiri”. Kalau ada jalan yang ditutup, kamu harus menulis ulang instruksinya.
flowchart TD
A["Mulai"] --> B["Langkah 1:\nBuat VPC"]
B --> C{"Berhasil?"}
C -->|"Ya"| D["Langkah 2:\nBuat Subnet"]
C -->|"Tidak"| E["Handle Error\n(ditulis manual)"]
E --> B
D --> F{"Berhasil?"}
F -->|"Ya"| G["Langkah 3:\nBuat Security Group"]
F -->|"Tidak"| H["Handle Error\n(ditulis manual)"]
H --> D
G --> I["Langkah 4:\nBuat EC2 Instance"]
I --> J["Selesai"]
style A fill:#e3f2fd,stroke:#1565c0
style J fill:#e8f5e9,stroke:#2e7d32
style E fill:#ffebee,stroke:#c62828
style H fill:#ffebee,stroke:#c62828Perhatikan bagaimana setiap langkah harus didefinisikan secara eksplisit beserta error handling-nya. Kalau ada langkah baru yang perlu ditambahkan di tengah-tengah (misalnya buat Internet Gateway sebelum buat Subnet), kamu harus mengubah seluruh urutan script. Kalau langkah kedua gagal, kamu harus menulis logika retry secara manual. Semakin banyak langkah, semakin kompleks dan rapuh script-nya.
Perbedaan Fundamental dengan Deklaratif #
Perbedaan antara imperatif dan deklaratif bukan hanya soal sintaks, tapi soal siapa yang bertanggung jawab atas logika eksekusi:
// DEKLARATIF (Terraform) — kamu mendefinisikan AKHIR STATE
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
// Terraform yang menentukan:
// - Urutan pembuatan (berdasarkan dependency)
// - Error handling dan retry
// - Apa yang perlu diubah vs yang sudah benar
# IMPERATIF (bash) — kamu mendefinisikan LANGKAH EKSEKUSI
# Kamu yang menentukan:
# - Urutan pembuatan (secara manual)
# - Error handling (secara manual)
# - Apa yang perlu dicek sebelum membuat
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
--query 'Vpc.VpcId' --output text)
# Kalau command di atas gagal, apa yang terjadi?
# Script tetap jalan ke langkah berikutnya!
# Kamu harus tambahkan error handling sendiri:
if [ -z "$VPC_ID" ]; then
echo "Gagal membuat VPC!"
exit 1
fi
SUBNET_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID \
--cidr-block 10.0.1.0/24 \
--query 'Subnet.SubnetId' --output text)
Perbedaan paling signifikan: dengan pendekatan deklaratif, kamu mendefinisikan tujuan dan tool yang menentukan jalannya. Dengan imperatif, kamu mendefinisikan jalannya dan berharap tujuan tercapai.
Script Bash: Tool Imperatif Paling Dasar #
Script bash adalah bentuk paling primitif dari Infrastructure as Code imperatif. Banyak tim memulai journey IaC mereka dengan menulis script bash yang menjalankan CLI command secara berurutan. Tampak sederhana dan langsung, tapi menyimpan banyak masalah yang baru terasa beberapa bulan kemudian.
Contoh Script Bash untuk Provisioning #
Script berikut adalah contoh nyata bagaimana seseorang mem-provision infrastruktur lengkap menggunakan bash. Perhatikan kompleksitas yang meningkat seiring bertambahnya resource:
#!/bin/bash
# provision-infra.sh — Contoh provisioning infrastruktur dengan bash
set -e # Stop jika ada error
echo "=== Memulai provisioning infrastruktur ==="
# Buat VPC
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
--query 'Vpc.VpcId' --output text)
aws ec2 create-tags --resources $VPC_ID \
--tags Key=Name,Value=main-vpc
echo "VPC dibuat: $VPC_ID"
# Buat Subnet
SUBNET_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID \
--cidr-block 10.0.1.0/24 --availability-zone ap-southeast-1a \
--query 'Subnet.SubnetId' --output text)
aws ec2 create-tags --resources $SUBNET_ID \
--tags Key=Name,Value=public-subnet
echo "Subnet dibuat: $SUBNET_ID"
# Buat Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway \
--query 'InternetGateway.InternetGatewayId' --output text)
aws ec2 attach-internet-gateway --internet-gateway-id $IGW_ID \
--vpc-id $VPC_ID
echo "IGW dibuat: $IGW_ID"
# Buat Route Table
RT_ID=$(aws ec2 create-route-table --vpc-id $VPC_ID \
--query 'RouteTable.RouteTableId' --output text)
aws ec2 create-route --route-table-id $RT_ID \
--destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID
aws ec2 associate-route-table --route-table-id $RT_ID \
--subnet-id $SUBNET_ID
echo "Route table dibuat: $RT_ID"
# Buat Security Group
SG_ID=$(aws ec2 create-security-group --group-name web-sg \
--description "Web SG" --vpc-id $VPC_ID \
--query 'GroupId' --output text)
aws ec2 authorize-security-group-ingress --group-id $SG_ID \
--protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id $SG_ID \
--protocol tcp --port 80 --cidr 0.0.0.0/0
echo "Security group dibuat: $SG_ID"
# Launch Instance
INSTANCE_ID=$(aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.medium \
--subnet-id $SUBNET_ID \
--security-group-ids $SG_ID \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-server}]' \
--query 'Instances[0].InstanceId' --output text)
echo "Instance diluncurkan: $INSTANCE_ID"
echo "=== Provisioning selesai ==="
Masalah Script Bash #
Script di atas tampak berfungsi, tapi punya banyak masalah yang akan muncul seiring waktu:
Tidak idempotent. Kalau kamu menjalankan script ini dua kali, kamu akan punya dua VPC, dua subnet, dua instance — semuanya duplikat. Tidak ada mekanisme yang mengecek “apakah VPC sudah ada?” sebelum membuat yang baru. Menambahkan logika pengecekan ke setiap langkah membuat script jadi sangat kompleks.
Tidak ada state tracking. Script ini tidak tahu resource apa yang sudah dibuat. Kalau script gagal di tengah jalan (misalnya gagal membuat security group), kamu tidak tahu resource mana yang sudah berhasil dibuat dan mana yang belum. Kamu harus mengecek manual di console.
Error handling minimal. set -e hanya menghentikan script saat ada error. Tidak ada rollback — resource yang sudah dibuat sebelum error tetap ada dan tidak termanage. Kamu bisa berakhir dengan orphaned resource yang terus menambah tagihan.
Tidak bisa plan. Kamu tidak bisa melihat “apa yang akan terjadi” sebelum menjalankan script. Satu-satunya cara tahu hasilnya adalah menjalankan script — dan kalau ada yang salah, sudah terlambat.
flowchart LR
subgraph Bash["Script Bash"]
B1["Jalankan Script"] --> B2{"Gagal di\nTengah Jalan?"}
B2 -->|"Ya"| B3["Resource Sebagian\nAda, Sebagian Tidak"]
B3 --> B4["Manual Cleanup\ndi Console"]
B4 --> B5["Fix Script"]
B5 --> B1
B2 -->|"Tidak"| B6["Berhasil\n(Tapi Duplikat?)"]
end
subgraph Terraform["Terraform"]
T1["terraform plan"] --> T2["Review\nPerubahan"]
T2 --> T3["terraform apply"]
T3 --> T4{"Gagal?"}
T4 -->|"Ya"| T5["State Tercatat\nRollback Bisa\nDilakukan"]
T4 -->|"Tidak"| T6["Berhasil\nState Konsisten"]
end
style B3 fill:#ffebee,stroke:#c62828
style B4 fill:#ffebee,stroke:#c62828
style T5 fill:#fff3e0,stroke:#e65100
style T6 fill:#e8f5e9,stroke:#2e7d32Ansible: Imperatif dengan Struktur #
Ansible mengambil pendekatan imperatif tapi menambahkan struktur yang lebih terorganisir dibanding script bash. Menggunakan YAML sebagai bahasa definisi, Ansible mendefinisikan task secara berurutan yang harus dijalankan pada target host. Ansible populer untuk konfigurasi server, tapi juga bisa digunakan untuk provisioning resource cloud melalui module-module yang tersedia.
Cara Kerja Ansible #
Ansible bekerja dengan model agentless — tidak perlu menginstall agent di target host. Semua komunikasi dilakukan melalui SSH (untuk Linux) atau WinRM (untuk Windows). Ansible mengirim module ke target, menjalankannya, mengambil hasilnya, lalu membersihkan module yang dikirim.
# playbook.yml — Ansible playbook untuk provisioning EC2
---
- name: Provision web server infrastructure
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Create VPC
amazon.aws.ec2_vpc_net:
name: main-vpc
cidr_block: 10.0.0.0/16
region: ap-southeast-1
tags:
Environment: production
ManagedBy: ansible
register: vpc_result
- name: Create public subnet
amazon.aws.ec2_vpc_subnet:
vpc_id: "{{ vpc_result.vpc.id }}"
cidr: 10.0.1.0/24
az: ap-southeast-1a
tags:
Name: public-subnet
register: subnet_result
- name: Create security group
amazon.aws.ec2_security_group:
name: web-sg
description: Web security group
vpc_id: "{{ vpc_result.vpc.id }}"
rules:
- proto: tcp
ports:
- 80
- 443
cidr_ip: 0.0.0.0/0
register: sg_result
- name: Launch EC2 instance
amazon.aws.ec2_instance:
name: web-server
image_id: ami-0abcdef1234567890
instance_type: t3.medium
subnet_id: "{{ subnet_result.subnet.id }}"
security_groups:
- "{{ sg_result.group_id }}"
tags:
Environment: production
ManagedBy: ansible
Kelebihan Ansible #
Ansible punya beberapa keunggulan yang membuatnya tetap relevan, terutama untuk tugas konfigurasi server:
# Konfigurasi server dengan Ansible — area terkuatnya
---
- name: Configure web servers
hosts: webservers
become: true
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Copy nginx configuration
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
mode: '0644'
notify: restart nginx
- name: Ensure nginx is running
systemd:
name: nginx
state: started
enabled: yes
handlers:
- name: restart nginx
systemd:
name: nginx
state: restarted
Kelebihan utama Ansible: agentless (tidak perlu install apa-apa di target), mudah dipelajari (YAML yang readable), module yang kaya (ribuan module untuk berbagai platform), dan baik untuk konfigurasi server (install package, manage service, copy file).
Kekurangan Ansible untuk Provisioning #
Meskipun Ansible bisa digunakan untuk provisioning cloud resource, ada beberapa kelemahan mendasar yang membuatnya kurang ideal dibanding Terraform untuk tugas ini:
Masalah idempotency. Beberapa module Ansible idempotent (seperti apt dan systemd), tapi module untuk resource cloud tidak selalu demikian. Module amazon.aws.ec2_instance bisa membuat duplikat instance kalau parameter yang kamu gunakan tidak cukup spesifik untuk mengidentifikasi instance yang sudah ada.
Tidak punya dependency graph yang kuat. Ansible menjalankan task secara berurutan dari atas ke bawah. Kalau task 3 bergantung pada output task 5, kamu harus menyusun ulang urutannya secara manual. Terraform membangun dependency graph secara otomatis dari referensi antar resource.
State management terbatas. Ansible tidak punya konsep state file. Dia hanya tahu apa yang terakhir kali dia lakukan berdasarkan task yang dijalankan. Kalau seseorang mengubah resource secara manual di console, Ansible tidak tahu perubahan itu terjadi.
flowchart TD
A["Ansible: Dependency Harus Didefinisikan Manual"] --> B["Task 1\nBuat VPC"]
B --> C["Task 2\nBuat Subnet\n(harus tahu VPC ID)"]
C --> D["Task 3\nBuat Security Group\n(harus tahu VPC ID)"]
D --> E["Task 4\nBuat Instance\n(harus tahu Subnet ID + SG ID)"]
F["Terraform: Dependency Dibangun Otomatis"] --> G["resource vpc\n(ref: tidak ada)"]
F --> H["resource subnet\n(ref: vpc.id → otomatis\nmenunggu vpc selesai)"]
F --> I["resource sg\n(ref: vpc.id → otomatis\nbisa paralel dengan subnet)"]
F --> J["resource instance\n(ref: subnet.id, sg.id →\notomatis menunggu keduanya)"]
style A fill:#fff3e0,stroke:#e65100
style F fill:#e3f2fd,stroke:#1565c0Chef: Imperatif Berbasis Ruby #
Chef menggunakan Ruby sebagai bahasa utama untuk mendefinisikan konfigurasi infrastruktur. Berbeda dengan Ansible yang agentless, Chef memerlukan agent (chef-client) yang berjalan di setiap target node. Agent ini menarik konfigurasi dari Chef Server dan menerapkannya secara periodik.
Model Arsitektur Chef #
Chef menggunakan arsitektur client-server di mana:
- Chef Server menyimpan semua resep (recipes) dan data konfigurasi
- Chef Client berjalan di setiap node dan menarik konfigurasi dari server
- Workstation adalah tempat developer menulis dan meng-upload resep
# web_server.rb — Chef recipe untuk mengkonfigurasi web server
# Install nginx
package 'nginx' do
action :install
end
# Pastikan service nginx berjalan
service 'nginx' do
action [:enable, :start]
end
# Deploy konfigurasi nginx
template '/etc/nginx/nginx.conf' do
source 'nginx.conf.erb'
owner 'root'
group 'root'
mode '0644'
variables(
server_name: node['web']['server_name'],
upstream_servers: node['web']['upstream']
)
notifies :restart, 'service[nginx]', :delayed
end
# Deploy website
deploy '/var/www/app' do
repo node['app']['repo_url']
revision node['app']['branch']
action :deploy
notifies :restart, 'service[nginx]', :immediately
end
Kekuatan dan Kelemahan Chef #
Chef sangat kuat untuk konfigurasi server berulang yang kompleks. Konsep cookbook dan recipe memungkinkan modularity yang tinggi, dan integrasi dengan Ruby memberikan fleksibilitas penuh untuk logika konfigurasi yang kompleks.
Namun, Chef punya beberapa kelemahan signifikan:
Kurva pembelajaran tinggi. Kamu perlu menguasai Ruby, konsep Chef (cookbook, recipe, resource, provider, data bags, environments), dan cara mengelola Chef Server. Ini jauh lebih kompleks dibanding YAML-nya Ansible atau HCL-nya Terraform.
Infrastruktur overhead. Chef memerlukan Chef Server yang harus di-deploy, di-maintain, dan di-scale sendiri. Ini berarti ada satu infrastruktur lagi yang harus kamu kelola hanya untuk mengelola infrastruktur lainnya.
Tidak ideal untuk provisioning. Sama seperti Ansible, Chef dirancang untuk konfigurasi server (menginstall package, manage service, deploy aplikasi), bukan untuk provisioning resource cloud. Bisa dilakukan, tapi bukan kekuatannya.
# ANTI-PATTERN: Menggunakan Chef untuk provisioning infrastruktur
# Chef bukan tool yang tepat untuk membuat VPC, subnet, dll.
# Menggunakan Chef provisioner untuk EC2 — bisa, tapi bukan area terkuatnya
aws_instance 'web-server' do
image_id 'ami-0abcdef1234567890'
instance_type 't3.medium'
key_name 'my-key'
security_groups ['web-sg']
action :create
end
# LEBIH BAIK: Gunakan Terraform untuk provisioning,
# Chef untuk konfigurasi server setelah server hidup.
Puppet: Deklaratif yang Sering Dikira Imperatif #
Puppet menarik karena sebenarnya menggunakan pendekatan deklaratif — meskipun sering dikelompokkan bersama tool imperatif. Puppet mendefinisikan state yang diinginkan dari resource, bukan langkah-langkah untuk mencapainya. Puppet Agent di target node yang menentukan perubahan apa yang perlu dilakukan.
Model Kerja Puppet #
# web_server.pp — Puppet manifest untuk web server
class profile::web_server {
package { 'nginx':
ensure => installed,
}
service { 'nginx':
ensure => running,
enable => true,
require => Package['nginx'],
}
file { '/etc/nginx/nginx.conf':
ensure => file,
content => template('profile/nginx.conf.erb'),
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
}
file { '/var/www/html':
ensure => directory,
owner => 'www-data',
group => 'www-data',
}
}
Perhatikan bahwa Puppet menggunakan sintaks yang berbeda dari Ansible. Alih-alih mendefinisikan task secara berurutan, Puppet mendefinisikan resource beserta state yang diinginkan. Puppet yang menentukan urutan eksekusi berdasarkan dependency yang didefinisikan dengan require dan notify.
Kelebihan Puppet #
- Model deklaratif yang kuat untuk konfigurasi server
- Compliance reporting bawaan yang bagus
- Module ecosystem yang matang di Puppet Forge
- Facter untuk mengumpulkan informasi tentang target node
Kelemahan Puppet #
- Memerlukan Puppet Server — infrastruktur tambahan yang harus dikelola
- Agent-based — harus install Puppet Agent di setiap node
- Sintaks yang rumit — Ruby DSL atau Puppet DSL yang butuh waktu untuk dikuasai
- Kurang fleksibel untuk provisioning resource cloud dibanding Terraform
Perbandingan Komprehensif Tool Imperatif #
Setiap tool punya kekuatan di area yang berbeda. Tabel berikut membandingkan semua tool yang dibahas berdasarkan berbagai aspek penting:
| Aspek | Script Bash | Ansible | Chef | Puppet | Terraform |
|---|---|---|---|---|---|
| Paradigma | Imperatif murni | Imperatif | Imperatif | Deklaratif | Deklaratif |
| Bahasa | Bash | YAML | Ruby | Puppet DSL | HCL |
| Agent | Tidak | Tidak | Ya | Ya | Tidak |
| State Management | Tidak ada | Terbatas | Ya | Ya | Ya (state file) |
| Idempotency | Tidak | Parsial | Parsial | Ya | Ya |
| Dependency Graph | Manual | Manual | Parsial | Ya | Ya (otomatis) |
| Plan/Preview | Tidak | Ya (–check) | Ya (dry-run) | Ya (–noop) | Ya (plan) |
| Terbaik untuk | One-off tasks | Config management | Config management | Config management | Infrastructure provisioning |
flowchart TD
subgraph Provisioning["Provisioning Resource Cloud"]
P1["VPC, Subnet, EC2,\nRDS, S3, Load Balancer"]
end
subgraph ConfigMgmt["Configuration Management"]
C1["Install Package,\nDeploy App, Manage\nService, Copy File"]
end
Provisioning --> TF["✅ Terraform\n(paling tepat)"]
Provisioning --> ANS1["⚠️ Ansible\n(bisa, tapi bukan kekuatannya)"]
Provisioning --> BASH1["❌ Script Bash\n(hindari untuk production)"]
ConfigMgmt --> ANS2["✅ Ansible\n(terbaik untuk ini)"]
ConfigMgmt --> CHEF["✅ Chef\n(kuat, tapi kompleks)"]
ConfigMgmt --> PUP["✅ Puppet\n(kuat, tapi butuh agent)"]
style TF fill:#e8f5e9,stroke:#2e7d32
style ANS2 fill:#e8f5e9,stroke:#2e7d32
style CHEF fill:#e8f5e9,stroke:#2e7d32
style PUP fill:#e8f5e9,stroke:#2e7d32
style ANS1 fill:#fff3e0,stroke:#e65100
style BASH1 fill:#ffebee,stroke:#c62828Masalah Idempotency pada Tool Imperatif #
Idempotency adalah konsep di mana menjalankan operasi yang sama berkali-kali menghasilkan hasil yang sama dengan menjalankannya sekali. Ini kritis untuk Infrastructure as Code karena kamu pasti akan menjalankan tool yang sama berkali-kali — baik saat update, scaling, maupun recovery.
# ANTI-PATTERN: Script bash yang TIDAK idempotent
#!/bin/bash
# Menjalankan sekali: membuat VPC baru ✓
# Menjalankan lagi: membuat VPC DUPLIKAT ✗
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
--query 'Vpc.VpcId' --output text)
# Menjalankan sekali: membuat S3 bucket ✓
# Menjalankan lagi: ERROR "BucketAlreadyExists" ✗
aws s3 mb s3://my-app-bucket
# BENAR: Script bash dengan idempotency manual
#!/bin/bash
# Cek apakah VPC sudah ada
EXISTING_VPC=$(aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values=main-vpc" \
--query 'Vpcs[0].VpcId' --output text)
if [ "$EXISTING_VPC" = "None" ]; then
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
--query 'Vpc.VpcId' --output text)
aws ec2 create-tags --resources $VPC_ID \
--tags Key=Name,Value=main-vpc
echo "VPC dibuat: $VPC_ID"
else
VPC_ID=$EXISTING_VPC
echo "VPC sudah ada: $VPC_ID"
fi
Masalah dengan solusi di atas: setiap resource memerlukan logika pengecekan yang berbeda. VPC perlu dicek berdasarkan tag, S3 bucket berdasarkan nama, security group berdasarkan nama dan VPC ID. Script yang tadinya sederhana menjadi sangat kompleks hanya untuk memastikan idempotency.
Terraform menangani ini secara otomatis melalui state file. Sebelum membuat resource baru, Terraform selalu mengecek state file untuk mengetahui resource apa yang sudah ada dan apa yang perlu diubah. Inilah mengapa terraform apply bisa dijalankan berkali-kali tanpa risiko duplikasi.
Kapan Tool Imperatif Masih Tepat Digunakan #
Meskipun Terraform unggul untuk provisioning infrastruktur, ada skenario di mana tool imperatif masih lebih tepat:
Skenario 1: Konfigurasi Server #
Setelah Terraform membuat EC2 instance, kamu perlu mengkonfigurasi server tersebut — install nginx, deploy aplikasi, atur firewall di level OS. Ini adalah area di mana Ansible, Chef, atau Puppet jauh lebih kuat dari Terraform.
# Ansible lebih tepat untuk tugas ini
- name: Configure application server
hosts: app_servers
become: true
tasks:
- name: Install dependencies
apt:
name:
- nginx
- python3
- postgresql-client
state: present
update_cache: yes
- name: Deploy application
git:
repo: https://github.com/company/app.git
dest: /var/www/app
version: main
- name: Configure nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
Skenario 2: Task Operasional One-off #
Kadang kamu perlu menjalankan tugas sekali saja — seperti mem-backup database sebelum migrasi, menjalankan script migrasi, atau mengirim bulk notification. Untuk tugas seperti ini, script bash atau Ansible ad-hoc command jauh lebih praktis dibanding menulis Terraform module.
# Ansible ad-hoc command untuk one-off task
# Menjalankan update security patch di semua server
$ ansible webservers -m apt -a "upgrade=dist" --become
# Backup database sebelum migrasi
$ ansible db_servers -m shell \
-a "pg_dump -Fc mydb > /backup/mydb_$(date +%Y%m%d).dump" \
--become
Skenario 3: Pipeline Deployment Aplikasi #
Proses deployment aplikasi — build, test, push artifact, update service — adalah proses imperatif yang berurutan. CI/CD pipeline yang menangani ini (Jenkins, GitLab CI, GitHub Actions) pada dasarnya adalah tool imperatif.
# GitLab CI — deployment pipeline (imperatif)
deploy_production:
stage: deploy
script:
- docker build -t app:$CI_COMMIT_SHA .
- docker push registry.company.com/app:$CI_COMMIT_SHA
- kubectl set image deployment/app app=registry.company.com/app:$CI_COMMIT_SHA
- kubectl rollout status deployment/app --timeout=300s
only:
- main
environment:
name: production
Kombinasi Terraform + Tool Imperatif #
Pendekatan terbaik di production nyata bukan memilih salah satu, tapi mengkombinasikan keduanya. Terraform untuk provisioning infrastruktur, tool imperatif untuk konfigurasi server dan operasional.
# Terraform: Provision infrastruktur
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.medium"
subnet_id = aws_subnet.private.id
vpc_security_group_ids = [aws_sg.web.id]
tags = {
Name = "web-server"
}
}
# Setelah instance hidup, jalankan Ansible untuk konfigurasi
resource "null_resource" "configure_server" {
triggers = {
instance_id = aws_instance.web.id
}
provisioner "local-exec" {
command = <<-EOT
ansible-playbook -i '${aws_instance.web.public_ip},' \
configure-server.yml \
--extra-vars "db_host=${aws_db_instance.main.address}"
EOT
}
}
ANTIPATTERN vs BENAR:
ANTI-PATTERN:
✗ Menggunakan Terraform untuk konfigurasi server
(remote-exec, file provisioner — fragile dan tidak idempotent)
✗ Menggunakan Ansible untuk provisioning VPC, RDS, EKS
(bisa, tapi tidak punya state management dan plan)
✗ Menggunakan script bash untuk semuanya
(tidak idempotent, tidak ada plan, tidak ada state)
BENAR:
✓ Terraform untuk provisioning (VPC, EC2, RDS, S3, IAM)
✓ Ansible/Chef/Puppet untuk konfigurasi server
✓ Script bash untuk one-off task dan prototyping
✓ CI/CD pipeline untuk deployment aplikasi
Ringkasan #
- Pendekatan imperatif mendefinisikan bagaimana infrastruktur dibuat secara berurutan, berbeda dengan deklaratif yang mendefinisikan apa yang diinginkan.
- Script bash adalah bentuk paling dasar dari IaC imperatif — cepat untuk prototyping tapi tidak idempotent, tidak punya state tracking, dan sangat rentan terhadap error.
- Ansible menambahkan struktur pada pendekatan imperatif dengan YAML-based playbook — sangat baik untuk konfigurasi server tapi kurang ideal untuk provisioning resource cloud.
- Chef dan Puppet adalah tool konfigurasi server yang mature — kuat untuk compliance dan config management tapi memerlukan infrastruktur tambahan (Chef Server, Puppet Server).
- Masalah idempotency adalah kelemahan terbesar tool imperatif untuk provisioning — menjalankan operasi yang sama bisa menghasilkan duplikat resource.
- Kombinasi terbaik di production: Terraform untuk provisioning infrastruktur, Ansible/Chef/Puppet untuk konfigurasi server, script bash untuk one-off task.
- Jangan memaksakan satu tool untuk semuanya — setiap tool punya domain di mana ia paling kuat, dan menggunakan kombinasi yang tepat menghasilkan sistem yang lebih robust.
← Sebelumnya: Infrastruktur Manual Berikutnya: Alternatif Terraform →