Resource #

Resource adalah unit dasar dari konfigurasi Terraform. Setiap hal yang kamu ingin Terraform buat dan kelola — sebuah VM, sebuah database, sebuah DNS record, sebuah IAM role — dideklarasikan sebagai blok resource. Memahami cara resource bekerja, bagaimana ia berinteraksi satu sama lain, dan bagaimana Terraform mengelola lifecycle-nya adalah pondasi dari semua yang lebih kompleks di Terraform.

Anatomi Blok Resource #

Setiap blok resource memiliki struktur yang konsisten. Bagian-bagiannya sederhana tapi masing-masing punya peran yang sangat spesifik.

resource "<provider>_<type>" "<nama_lokal>" {
  argumen = nilai
}

# Contoh nyata:
resource "aws_instance" "web_server" {
  #  │          │           │
  #  │          │           └── Nama lokal (hanya untuk referensi internal)
  #  │          └────────────── Tipe resource (dari provider AWS)
  #  └───────────────────────── Provider prefix (wajib, otomatis menentukan provider)

  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"

  tags = {
    Name        = "web-server"
    Environment = "production"
  }
}

Nama lokal (web_server di atas) hanya bermakna di dalam konfigurasi Terraform — ia tidak muncul di AWS Console. Yang muncul di AWS adalah nilai dari argumen tags.Name. Nama lokal digunakan untuk mereferensikan resource ini dari tempat lain dalam konfigurasi.

Tipe resource (aws_instance) terdiri dari dua bagian yang dipisahkan underscore: prefix provider (aws) dan tipe spesifik (instance). Terraform menggunakan prefix ini untuk menentukan provider mana yang bertanggung jawab mengelola resource.

# Contoh berbagai tipe resource dari provider berbeda:
resource "aws_instance" "web" { }           # AWS EC2
resource "aws_s3_bucket" "data" { }         # AWS S3
resource "google_compute_instance" "app" { }# GCP Compute
resource "cloudflare_record" "dns" { }      # Cloudflare DNS
resource "kubernetes_deployment" "api" { }   # Kubernetes
resource "postgresql_role" "admin" { }       # PostgreSQL

Referensi Antar Resource #

Satu resource bisa mereferensikan atribut dari resource lain. Inilah yang membentuk dependency graph dan memungkinkan resource saling terhubung secara dinamis. Format referensi selalu <type>.<nama_lokal>.<atribut>.

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  # Referensi ke atribut resource lain
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "ap-southeast-1a"
}

resource "aws_security_group" "web" {
  name   = "web-sg"
  vpc_id = aws_vpc.main.id  # Referensi ke VPC yang sama

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "web" {
  ami                    = "ami-0abcdef1234567890"
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.public.id        # dari subnet
  vpc_security_group_ids = [aws_security_group.web.id] # dari security group
}

Referensi ini bukan hanya memasukkan nilai — ia juga mendeklarasikan dependency. Ketika kamu menulis aws_vpc.main.id, Terraform tahu bahwa subnet harus dibuat setelah VPC. Dependency graph dihitung otomatis dari referensi ini.

flowchart TD
    VPC["aws_vpc.main"] --> SUBNET["aws_subnet.public"]
    VPC --> SG["aws_security_group.web"]
    SUBNET --> EC2["aws_instance.web"]
    SG --> EC2

    style VPC fill:#e3f2fd,stroke:#1565c0
    style SUBNET fill:#e8f5e9,stroke:#2e7d32
    style SG fill:#e8f5e9,stroke:#2e7d32
    style EC2 fill:#fff3e0,stroke:#e65100

Atribut yang Digenerate #

Beberapa atribut hanya tersedia setelah resource dibuat — misalnya IP address, ARN, atau ID yang digenerate oleh cloud provider. Atribut ini tersimpan di state dan bisa direferensikan oleh resource lain atau di-ekspos sebagai output.

resource "aws_instance" "web" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
}

# Atribut ini baru tersedia setelah apply
output "public_ip" {
  value = aws_instance.web.public_ip  # IP dari AWS
}

output "arn" {
  value = aws_instance.web.arn  # ARN dari AWS
}

# Referensi ke resource lain
resource "aws_route53_record" "app" {
  zone_id = var.dns_zone_id
  name    = "app.example.com"
  type    = "A"
  ttl     = 300
  records = [aws_instance.web.public_ip]  # IP dari instance di atas
}

Daftar lengkap atribut yang tersedia untuk setiap resource type bisa dilihat di dokumentasi provider masing-masing di registry.terraform.io. Attribute yang bisa kamu set disebut argument, sedangkan yang hanya bisa kamu baca setelah create disebut attribute.

Lifecycle Resource #

Setiap resource memiliki lifecycle: dibuat, mungkin diubah, dan akhirnya dihapus. Terraform mengelola ini secara otomatis, tapi kamu bisa mengkustomisasi perilakunya melalui blok lifecycle.

create_before_destroy #

Secara default, saat Terraform perlu me-replace resource (misalnya karena force_new attribute berubah), ia akan menghapus yang lama dulu baru membuat yang baru. Dengan create_before_destroy = true, urutannya dibalik — resource baru dibuat dulu, baru yang lama dihapus. Ini penting untuk resource yang tidak boleh ada downtime.

resource "aws_instance" "web" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"

  lifecycle {
    create_before_destroy = true
    # Jika AMI berubah → buat instance baru dulu → baru hapus yang lama
    # Tanpa ini: instance lama dihapus dulu → ada jeda tanpa server
  }
}

prevent_destroy #

Mencegah resource dihapus secara tidak sengaja. Jika terraform plan menghasilkan operasi destroy untuk resource ini, Terraform akan error.

resource "aws_s3_bucket" "critical_data" {
  bucket = "company-critical-data"

  lifecycle {
    prevent_destroy = true
    # terraform destroy atau menghapus blok dari config → ERROR
    # Proteksi untuk resource yang tidak boleh dihapus
  }
}

ignore_changes #

Memberitahu Terraform untuk mengabaikan perubahan pada atribut tertentu. Berguna ketika atribut dikelola di luar Terraform (misalnya oleh auto-scaling, monitoring agent, atau manual operation).

resource "aws_instance" "web" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"

  tags = {
    Name        = "web-server"
    LastModified = timestamp()
  }

  lifecycle {
    ignore_changes = [
      tags["LastModified"],  # Tag ini berubah setiap apply
      ami,                   # AMI dikelola oleh pipeline terpisah
    ]
  }
}

replace_triggered_by #

Memicu replace pada resource ini ketika resource atau attribute tertentu berubah. Berguna untuk memastikan resource di-replace bersamaan dengan dependency-nya.

resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t3.micro"

  lifecycle {
    replace_triggered_by = [var.ami_id]
    # Jika var.ami_id berubah → instance ini di-replace
    # Lebih eksplisit dari force_new yang ditentukan oleh provider
  }
}

Meta-Arguments: count dan for_each #

Terraform menyediakan meta-arguments untuk membuat beberapa instance resource sekaligus. Meta-arguments bukan atribut resource — mereka adalah instruksi untuk Terraform tentang cara membuat resource.

count — untuk Resource Identik #

resource "aws_subnet" "public" {
  count             = 3
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]

  tags = {
    Name = "public-subnet-${count.index + 1}"
  }
}

# Referensi:
# aws_subnet.public[0].id
# aws_subnet.public[1].id
# aws_subnet.public[2].id

for_each — untuk Resource dengan Identitas Berbeda #

resource "aws_iam_user" "team" {
  for_each = toset(["alice", "bob", "charlie"])
  name     = each.key

  tags = {
    Name = each.key
  }
}

# Referensi:
# aws_iam_user.team["alice"].arn
# aws_iam_user.team["bob"].arn
# aws_iam_user.team["charlie"].arn

# Juga bisa dari map:
resource "aws_s3_bucket" "buckets" {
  for_each = {
    logs     = "app-logs-2024"
    assets   = "app-assets-2024"
    backups  = "app-backups-2024"
  }

  bucket = each.value
  tags = {
    Purpose = each.key
  }
}
flowchart TD
    A["Pilih meta-argument"] --> B{"Resource identik\natau berbeda?"}
    B -->|"Identik"| C["count"]
    B -->|"Berbeda identitas"| D["for_each"]
    C --> E["Referensi: resource.name[index]"]
    D --> F["Referensi: resource.name[key]"]
    E --> G["Masalah: hapus elemen tengah\n→ index bergeser\n→ resource di-recreate"]
    F --> H["Aman: hapus elemen tengah\n→ key lain tidak terpengaruh"]

    style C fill:#fff3e0,stroke:#e65100
    style D fill:#e8f5e9,stroke:#2e7d32
    style G fill:#ffebee,stroke:#c62828
    style H fill:#e8f5e9,stroke:#2e7d32
Hati-hati dengan count pada resource yang sudah ada. Jika kamu menghapus elemen di tengah daftar, semua index setelahnya bergeser — Terraform akan menghapus dan membuat ulang semua resource setelah elemen yang dihapus. Gunakan for_each untuk resource dengan identitas unik agar lebih aman.

Conditional Resource dengan Count #

count juga bisa digunakan untuk membuat resource secara kondisional — count = 0 berarti resource tidak dibuat.

resource "aws_nat_gateway" "main" {
  count = var.enable_nat ? 1 : 0

  allocation_id = aws_eip.nat[0].id
  subnet_id     = aws_subnet.public[0].id
}

# Referensi ke conditional resource harus hati-hati:
# aws_nat_gateway.main[0].id → error jika count = 0

# Solusi: gunakan try() atau conditional
locals {
  nat_gateway_id = try(aws_nat_gateway.main[0].id, null)
}

provider Meta-Argument #

Ketika menggunakan multi-region atau multi-account, kamu bisa menentukan provider mana yang mengelola resource tertentu.

resource "aws_s3_bucket" "replica" {
  provider = aws.us_east  # Gunakan provider dengan alias
  bucket   = "app-replica-us"
}

Resource yang Dihapus dari Konfigurasi #

Ketika kamu menghapus blok resource dari file .tf dan menjalankan terraform apply, Terraform akan menghapus resource tersebut dari infrastruktur. Ini perilaku default yang harus dipahami — menghapus dari config = menghapus dari infrastruktur.

# Sebelumnya ada di config:
# resource "aws_instance" "old_server" {
#   ami           = "ami-0abcdef1234567890"
#   instance_type = "t3.micro"
# }

# Setelah dihapus dari config:
# $ terraform plan
# aws_instance.old_server will be destroyed
# Plan: 0 to add, 0 to change, 1 to destroy.

# Jika tidak ingin resource dihapus, ada beberapa opsi:
# 1. prevent_destroy (sebelum menghapus blok)
# 2. terraform state rm (hapus dari state tanpa destroy)
# 3. moved block (pindah ke resource baru)

moved Block untuk Rename/Restructure #

Ketika kamu ingin mengubah nama lokal resource atau memindahkannya ke module tanpa menghapus dan membuat ulang, gunakan moved block.

# Sebelumnya:
resource "aws_instance" "web" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
}

# Ingin rename menjadi "app_server" tanpa recreate:
resource "aws_instance" "app_server" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
}

moved {
  from = aws_instance.web
  to   = aws_instance.app_server
  # Terraform tahu ini resource yang sama → tidak ada destroy/create
}

Ringkasan #

  • Resource adalah unit dasar Terraform — setiap infrastruktur dideklarasikan sebagai blok resource "<type>" "<nama_lokal>".
  • Nama lokal hanya untuk referensi internal — tidak mempengaruhi nama resource di cloud, hanya digunakan untuk referensi dalam konfigurasi.
  • Referensi antar resource (aws_vpc.main.id) membentuk dependency graph otomatis — Terraform tahu urutan eksekusi dari referensi ini.
  • Lifecycle block mengizinkan kustomisasi perilaku: create_before_destroy (zero-downtime), prevent_destroy (proteksi), ignore_changes (abaikan atribut eksternal).
  • Gunakan for_each bukan count untuk resource dengan identitas berbeda — lebih aman saat ada perubahan di tengah daftar karena key tidak bergeser.
  • count untuk conditionalcount = 0 membuat resource tidak dibuat, tapi hati-hati saat mereferensi conditional resource.
  • moved block memungkinkan rename/restructure tanpa destroy/create — sangat berguna saat refactoring konfigurasi.

← Sebelumnya: Provider   Berikutnya: State →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact