Apa itu Resource? #
Di section Concept, kamu sudah berkenalan dengan resource sebagai unit dasar konfigurasi Terraform. Di section ini, kita masuk lebih dalam — bagaimana resource berinteraksi satu sama lain, bagaimana Terraform mengelola lifecycle-nya, dan bagaimana menulis resource yang robust untuk infrastruktur production. Artikel ini membangun fondasi untuk topik-topik yang lebih dalam di section ini: lifecycle, dependency, dan operation.
Resource vs Data Source #
Salah satu kebingungan paling umum di Terraform adalah perbedaan antara resource dan data. Keduanya adalah blok konfigurasi, tapi perannya sangat berbeda.
flowchart LR
subgraph "resource (Manage)"
R1["aws_vpc.main"] --> R2["Terraform BUAT\nresource ini"]
R2 --> R3["Terraform UPDATE\njika config berubah"]
R3 --> R4["Terraform HAPUS\njika di-destroy"]
end
subgraph "data (Read)"
D1["aws_ami.ubuntu"] --> D2["Terraform BACA\ndari yang sudah ada"]
D2 --> D3["Tidak bisa\ndibuat/dihapus"]
D3 --> D4["Hanya referensi\nuntuk resource lain"]
end
style R1 fill:#e3f2fd,stroke:#1565c0
style D1 fill:#e8f5e9,stroke:#2e7d32# resource — Terraform MEMBUAT dan MENGELOLA ini
# Terraform bertanggung jawab atas lifecycle-nya: create, update, destroy
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "main-vpc"
}
}
# data — Terraform MEMBACA ini, tidak membuatnya
# Resource sudah ada di luar Terraform, kamu hanya ingin referensikan
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-*-22.04-amd64-server-*"]
}
}
# Menggunakan keduanya bersama
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id # AMI dari data source
instance_type = "t3.micro"
vpc_id = aws_vpc.main.id # VPC dari resource
}
| Aspek | resource | data |
|---|---|---|
| Tujuan | Buat dan kelola resource baru | Baca resource yang sudah ada |
| Lifecycle | Create, update, destroy | Tidak ada |
| Di state? | ✅ Ya, disimpan dan dilacak | ✅ Ya, tapi hanya sebagai cache |
| Dibuat oleh Terraform? | ✅ Ya | ❌ Tidak, sudah ada di luar |
| Contoh | Buat VPC, EC2, S3 | Baca AMI, baca VPC existing |
Anatomi Lengkap Blok Resource #
flowchart TD
A["Blok Resource"] --> B["Argumen Required\n(ami, instance_type)"]
A --> C["Argumen Optional\n(subnet_id, tags)"]
A --> D["Nested Block\n(tags, root_block_device)"]
A --> E["Meta-Arguments\n(depends_on, count,\nfor_each, provider,\nlifecycle)"]
B --> B1["Wajib ada\nDisediakan provider"]
C --> C1["Opsional\nDisediakan provider"]
D --> D1["Struktur bersarang\nDisediakan provider"]
E --> E1["Disediakan Terraform\nBerlaku untuk SEMUA\nresource"]
style A fill:#e3f2fd,stroke:#1565c0
style E fill:#fff3e0,stroke:#e65100
style E1 fill:#fff3e0,stroke:#e65100# Struktur lengkap blok resource dengan semua bagiannya
resource "<PROVIDER>_<TYPE>" "<NAME>" {
# ─────────────────────────────────────────
# ARGUMEN — konfigurasi spesifik resource ini
# ─────────────────────────────────────────
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
# Blok bersarang (nested block)
tags = {
Name = "web-server"
Environment = var.environment
}
# ─────────────────────────────────────────
# META-ARGUMENTS — berlaku untuk semua resource
# ─────────────────────────────────────────
# Dependency eksplisit
depends_on = [aws_iam_role_policy_attachment.node]
# Buat beberapa instance
count = 3
# atau: for_each = var.instance_map
# Gunakan provider non-default
provider = aws.us_east
# Kustomisasi lifecycle
lifecycle {
create_before_destroy = true
prevent_destroy = false
ignore_changes = [tags["LastModified"]]
}
}
Meta-arguments (depends_on, count, for_each, provider, lifecycle) adalah argumen khusus yang dikenali Terraform sendiri — bukan argumen dari provider. Mereka berlaku untuk semua resource, apapun tipe dan providernya.
Bagaimana Resource Direpresentasikan di State #
Setiap resource yang dibuat Terraform disimpan di state dengan identitas uniknya. Memahami struktur ini membantu saat kamu perlu berinteraksi dengan state secara langsung.
flowchart TD
A["State File"] --> B["Resource Type\naws_instance"]
B --> C["Resource Name\nweb"]
C --> D["Resource Address\naws_instance.web"]
D --> E["Atribut\nid, ami, ip, tags"]
F["Resource + count"] --> G["aws_instance.workers[0]"]
F --> H["aws_instance.workers[1]"]
F --> I["aws_instance.workers[2]"]
J["Resource + for_each"] --> K["aws_subnet.public[\"a\"]"]
J --> L["aws_subnet.public[\"b\"]"]
J --> M["aws_subnet.public[\"c\"]"]
style D fill:#e3f2fd,stroke:#1565c0
style G fill:#e8f5e9,stroke:#2e7d32
style K fill:#e8f5e9,stroke:#2e7d32# Lihat semua resource di state
terraform state list
# Output:
# aws_instance.web
# aws_instance.workers[0]
# aws_instance.workers[1]
# aws_instance.workers[2]
# module.vpc.aws_vpc.main
# module.vpc.aws_subnet.public["ap-southeast-1a"]
# Lihat detail satu resource di state
terraform state show aws_instance.web
# Output:
# # aws_instance.web:
# resource "aws_instance" "web" {
# ami = "ami-0abcdef1234567890"
# id = "i-0abcdef1234567890"
# instance_type = "t3.micro"
# private_ip = "10.0.1.42"
# public_ip = "54.123.45.67"
# ... (semua atribut, termasuk yang digenerate AWS)
# }
Address resource di state mengikuti format: <type>.<name> untuk resource biasa, <type>.<name>[index] untuk resource dengan count, dan <type>.<name>["key"] untuk resource dengan for_each.
| Pola Resource | Format Address | Contoh |
|---|---|---|
| Resource biasa | <type>.<name> | aws_instance.web |
Dengan count | <type>.<name>[index] | aws_instance.workers[0] |
Dengan for_each | <type>.<name>["key"] | aws_subnet.public["a"] |
| Di dalam module | module.<name>.<type>.<name> | module.vpc.aws_vpc.main |
Resource Address dan Cara Mereferensikannya #
Setiap resource memiliki address yang bisa direferensikan dari tempat lain dalam konfigurasi.
flowchart LR
A["Referensi antar\nresource"] --> B["Format:\ntype.name.attribute"]
B --> C["aws_vpc.main.id\n(id dari VPC)"]
B --> D["aws_subnet.public.id\n(id dari subnet)"]
B --> E["aws_instance.web.public_ip\n(IP dari instance)"]
F["Splat expression"] --> G["aws_subnet.private[*].id\nSemua ID subnet sekaligus"]
style B fill:#e3f2fd,stroke:#1565c0# Format referensi: <TYPE>.<NAME>.<ATTRIBUTE>
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id # Referensi ke atribut "id" dari aws_vpc.main
}
# Untuk resource dengan count:
resource "aws_subnet" "private" {
count = 3
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 10}.0/24"
}
resource "aws_route_table_association" "private" {
count = 3
subnet_id = aws_subnet.private[count.index].id # Akses per index
route_table_id = aws_route_table.private.id
}
# Semua subnet sekaligus menggunakan splat expression:
output "private_subnet_ids" {
value = aws_subnet.private[*].id # ["subnet-001", "subnet-002", "subnet-003"]
}
Pola Penulisan yang Konsisten #
Konsistensi dalam cara menulis resource membuat konfigurasi lebih mudah dibaca dan di-review.
# POLA YANG DIREKOMENDASIKAN:
resource "aws_instance" "web" {
# 1. Argumen required (wajib) di atas
ami = var.ami_id
instance_type = var.instance_type
# 2. Argumen optional yang penting
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
iam_instance_profile = aws_iam_instance_profile.web.name
# 3. Blok bersarang
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
# 4. Tags selalu di akhir
tags = merge(
var.common_tags,
{
Name = "web-server"
Role = "web"
}
)
# 5. Lifecycle di paling akhir (jika ada)
lifecycle {
create_before_destroy = true
}
}
Resource Addressing #
Resource addressing memungkinkan referensi spesifik ke resource tertentu.
# Format: module.module_name.resource_type.resource_name[index]
# Root level resource
aws_instance.web
# Resource dalam module
module.networking.aws_vpc.main
# Resource dalam count
aws_instance.web[0]
aws_instance.web[1]
# Resource dalam for_each
aws_instance.web["app-server"]
aws_instance.web["db-server"]
# Nested module
module.networking.module.subnets.aws_subnet.private[0]
# Referensi resource untuk dependency
resource "aws_instance" "app" {
subnet_id = module.networking.private_subnet_ids[0]
# Implicit dependency ke module.networking
}
Resource Metadata #
# Setiap resource memiliki metadata yang bisa diakses
resource "aws_instance" "web" {
ami = "ami-12345"
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
}
# Metadata yang tersedia:
# aws_instance.web.id → Instance ID
# aws_instance.web.arn → ARN
# aws_instance.web.private_ip → Private IP
# aws_instance.web.public_ip → Public IP (jika ada)
# aws_instance.web.availability_zone → AZ
# aws_instance.web.instance_state → State (running, stopped)
# Lihat semua attributes resource
terraform state show aws_instance.web
# Menampilkan SEMUA atribut yang tersimpan di state
Resource Lifecycle States #
RESOURCE STATES:
1. pending_create: Akan dibuat saat apply
2. creating: Sedang dibuat
3. created: Sudah dibuat (aktif)
4. pending_update: Akan diupdate saat apply
5. updating: Sedang diupdate
6. pending_destroy: Akan dihapus saat apply
7. destroying: Sedang dihapus
8. destroyed: Sudah dihapus
TERRAFORM PLAN ICONS:
+ Create (resource baru)
~ Update (resource berubah)
- Destroy (resource dihapus)
-/+ Replace (destroy + create)
<= Read (data source)
# Lihat state resource
terraform state show aws_instance.web
# List semua resource di state
terraform state list
# List dengan filter
terraform state list | grep aws_instance
Ringkasan #
resourceuntuk membuat,datauntuk membaca — resource dikelola Terraform, data source hanya dibaca dari yang sudah ada.- Meta-arguments (
depends_on,count,for_each,provider,lifecycle) berlaku untuk semua resource, bukan argumen dari provider.- Address resource di state mengikuti format
<type>.<name>— penting diketahui untuk operasiterraform statedan referensi antar resource.- Referensi menggunakan format
<type>.<name>.<attribute>— ini yang membentuk dependency graph otomatis Terraform.- Splat expression (
[*]) untuk mengakses semua instance sekaligus dari resource dengancount.- Konsistensi penulisan — required args dulu, optional, nested blocks, tags, lifecycle — membuat review lebih mudah.