Anti-Pattern: Terraform as CM #
Terraform bisa membuat instance EC2, dan instance EC2 perlu dikonfigurasi setelah dibuat — install nginx, copy file konfigurasi, setup monitoring agent. Karena Terraform sudah ada di tangan dan bisa menjalankan remote-exec provisioner, godaan untuk melakukan konfigurasi server langsung dari Terraform sangat besar. Ini adalah anti-pattern yang terlihat seperti solusi sederhana di awal, tapi menciptakan masalah yang makin besar seiring waktu. Terraform dirancang untuk provisioning infrastruktur, bukan configuration management.
Apa yang Dimaksud Terraform sebagai CM #
Configuration Management (CM) adalah praktik mengelola konfigurasi dalam server: install package, manage file, set service state, create user, dan seterusnya. Tool yang dirancang untuk ini adalah Ansible, Chef, Puppet, dan SaltStack. Terraform adalah provisioning tool — ia membuat dan mengelola infrastruktur cloud, bukan konten di dalam infrastruktur tersebut.
BATAS YANG SEHARUSNYA JELAS:
TERRAFORM (provisioning):
✓ Buat EC2 instance
✓ Buat VPC, security group, load balancer
✓ Buat RDS database
✓ Buat S3 bucket
✓ Kelola IAM roles
CONFIGURATION MANAGEMENT TOOL (konfigurasi dalam server):
✓ Install nginx di dalam EC2 instance
✓ Copy file konfigurasi ke server
✓ Start dan enable service
✓ Buat user dan set permission
✓ Deploy aplikasi
GARIS INI TIDAK BOLEH DICAMPUR.
Mengapa remote-exec Provisioner Bermasalah #
# ANTI-PATTERN: Menggunakan remote-exec untuk konfigurasi server
resource "aws_instance" "web" {
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
key_name = aws_key_pair.deployer.key_name
provisioner "remote-exec" {
inline = [
"sudo apt-get update -y",
"sudo apt-get install -y nginx",
"sudo systemctl enable nginx",
"sudo systemctl start nginx",
"sudo bash -c 'echo Hello > /var/www/html/index.html'",
]
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa") # ✗ Path hardcoded, tidak portable
host = self.public_ip
}
}
}
Masalah dengan pendekatan ini:
MASALAH remote-exec:
1. TIDAK IDEMPOTEN
Jalankan terraform apply dua kali → kemungkinan error karena
apt-get atau nginx sudah ada. Atau lebih buruk: silent success
yang sebenarnya tidak mengeksekusi apa yang diharapkan.
2. TIDAK BISA DIVERIFIKASI
terraform plan tidak bisa menunjukkan apa yang akan dilakukan
provisioner. Kamu tidak tahu efeknya sebelum dijalankan.
3. BUTUH SSH/WinRM ACCESS
EC2 instance harus bisa diakses dari runner yang menjalankan Terraform.
Ini berarti security group harus buka port 22 ke CI/CD server —
security concern yang tidak perlu.
4. KONFIGURASI TIDAK TERSIMPAN DI STATE
Jika provisioner sudah berjalan, Terraform tidak tahu dan tidak melacak
apa yang sudah dikonfigurasi. Tidak ada idempotency check.
5. PROVISIONER HANYA BERJALAN SAAT CREATE
Jika konfigurasi server perlu diupdate, Terraform tidak bisa
"re-run" provisioner tanpa destroy dan recreate instance.
6. TERRAFORM PLAN MENJADI TIDAK BISA DIPERCAYA
"No changes" tidak berarti konfigurasi server sesuai harapan.
Masalah file Provisioner #
# ANTI-PATTERN: Gunakan file provisioner untuk deploy konfigurasi
resource "aws_instance" "app" {
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
provisioner "file" {
source = "config/nginx.conf" # ✗ Path relatif yang rapuh
destination = "/etc/nginx/nginx.conf"
connection {
type = "ssh"
# ...
}
}
provisioner "remote-exec" {
inline = ["sudo nginx -t && sudo systemctl reload nginx"]
# ...
}
}
# Masalah:
# - Jika nginx.conf berubah, Terraform tidak tahu karena tidak ada di state
# - terraform plan selalu "No changes" meski config file sudah berubah
# - Tidak ada cara untuk update konfigurasi server tanpa recreate instance
Cara yang Benar: Pisahkan Provisioning dan Konfigurasi #
# BENAR: Terraform hanya provisioning infrastruktur
# Konfigurasi server diserahkan ke tool yang tepat
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
# Minimal bootstrap via user_data (cloud-init)
# Hanya untuk setup awal yang sangat dasar
user_data = base64encode(<<-EOF
#!/bin/bash
# Install SSM agent agar bisa dimanage AWS Systems Manager
snap install amazon-ssm-agent --classic
systemctl enable snap.amazon-ssm-agent.amazon-ssm-agent
systemctl start snap.amazon-ssm-agent.amazon-ssm-agent
# Install Ansible untuk config management selanjutnya
apt-get update
apt-get install -y ansible
EOF
)
# Tidak ada provisioner — tidak ada SSH dari Terraform
# Tidak ada remote-exec — tidak ada file provisioner
}
# Output yang akan dipakai Ansible atau pipeline berikutnya
output "instance_ids" {
value = aws_instance.web[*].id
}
# Pipeline yang benar: Terraform untuk infra, Ansible untuk konfigurasi
# .github/workflows/deploy.yml
jobs:
provision:
name: Terraform — Provision Infrastructure
steps:
- name: Terraform Apply
run: terraform apply -auto-approve
- name: Save Instance IDs
run: terraform output -json instance_ids > instances.json
configure:
name: Ansible — Configure Servers
needs: provision # Jalankan setelah provision selesai
steps:
- name: Generate Ansible Inventory
run: |
cat instances.json | python3 scripts/generate-inventory.py > inventory.ini
- name: Run Ansible Playbook
run: |
ansible-playbook \
-i inventory.ini \
playbooks/web-server.yml
Kapan user_data Boleh Digunakan #
user_data (cloud-init) adalah pengecualian yang dapat diterima untuk konfigurasi minimal saat instance pertama kali boot.
# user_data yang TEPAT — minimal bootstrap saja
resource "aws_instance" "app" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.medium"
user_data = base64encode(<<-EOF
#!/bin/bash
# 1. Install agent untuk configuration management
apt-get install -y ansible
# atau: install Puppet agent, Chef client, SSM agent
# 2. Register ke CM tool untuk konfigurasi selanjutnya
ansible-pull -U https://github.com/myorg/infra-playbooks.git
# JANGAN: install aplikasi lengkap, copy banyak file, setup kompleks
# Minimal bootstrap hanya untuk "bootstrap" ke CM tool
EOF
)
lifecycle {
# user_data biasanya tidak perlu diupdate jika sudah pakai CM tool
ignore_changes = [user_data]
}
}
Alternatif: Immutable Infrastructure #
Pendekatan terbaik menghindari configuration management sama sekali adalah immutable infrastructure — buat AMI baru yang sudah berisi semua konfigurasi, lalu ganti instance lama dengan yang baru.
# Pola immutable infrastructure dengan Packer + Terraform
# 1. Packer buat AMI dengan semua konfigurasi di dalamnya
# (lihat: packer build web-server.pkr.hcl)
# 2. Terraform hanya perlu tahu AMI ID terbaru
data "aws_ami" "web_server" {
most_recent = true
owners = ["self"] # AMI yang dibuat tim sendiri
filter {
name = "name"
values = ["web-server-*"]
}
filter {
name = "tag:Version"
values = [var.app_version]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.web_server.id # AMI yang sudah dikonfigurasi
instance_type = "t3.micro"
# Tidak ada provisioner, tidak ada user_data yang kompleks
# Semua konfigurasi sudah ada di dalam AMI
}
Ringkasan #
- Terraform adalah provisioning tool, bukan configuration management tool — membuat resource cloud adalah ranahnya, mengkonfigurasi isi server bukan.
remote-execprovisioner menciptakan masalah idempotency, keamanan (butuh SSH terbuka), dan visibility (tidak ada di plan output).fileprovisioner tidak melacak perubahan di state — konfigurasi yang berubah tidak terdeteksi olehterraform plan.- Pisahkan pipeline: Terraform untuk infrastruktur, Ansible/Chef/Puppet untuk konfigurasi server — keduanya bisa dijalankan berurutan di pipeline CI/CD.
user_databoleh digunakan untuk minimal bootstrap (install agent CM tool) — bukan untuk konfigurasi server yang lengkap.- Immutable infrastructure adalah pendekatan terbaik — gunakan Packer untuk buat AMI yang sudah dikonfigurasi, Terraform hanya deploy AMI tersebut.
← Sebelumnya: Performance Optimization Berikutnya: Anti-Pattern: Over-Complex Module →