Di episode ini kita akan membahas tiga blok fundamental Terraform: provider sebagai jembatan menuju cloud API, resource sebagai bahan bangunan infrastruktur, serta data source untuk membaca infrastruktur yang sudah ada tanpa harus membuatnya ulang.

Setelah di episode 2 sebelumnya kita membahas anatomi HCL syntax dan empat langkah core workflow Terraform — terraform init, terraform plan, terraform apply, dan terraform destroy — pada episode kali ini kita akan masuk ke jantung dari semuanya: provider, resource, dan data source. Tanpa memahami ketiganya, seluruh perintah workflow yang tadi hanyalah gerakan tanpa makna, seperti punya mobil balap tapi tidak tahu cara menginjak pedalnya.
Kenapa topik ini krusial di dunia kerja nyata? Karena hampir semua pekerjaan seorang DevOps atau Cloud Engineer dimulai dari menulis blok resource untuk menciptakan VM, bucket, database, atau jaringan — lalu membaca data infrastruktur yang sudah ada lewat data source agar tidak membuat resource ganda secara tidak sengaja. Kesalahan kecil di lapisan ini, seperti salah memilih provider version atau salah menulis tipe resource, bisa mengakibatkan tagihan cloud membengkak atau konfigurasi yang tidak bisa di-apply sama sekali.
Di episode ini kalian tidak hanya akan belajar sintaksnya, tapi juga mengapa Terraform butuh provider, bagaimana cara kerja di balik terraform init saat mengunduh provider, dan kapan harus memakai resource versus data source. Mari kita mulai.
Secara bahasa, provider adalah penyedia. Dalam konteks Terraform, provider adalah sebuah plugin yang menjadi jembatan antara kode HCL yang kalian tulis dengan API cloud provider (AWS, GCP, Azure, dan lain-lain).
Coba bayangkan kalian membawa universal travel adapter saat ke luar negeri. Colokan laptop kalian berbahasa HCL, sementara stopkontak cloud di negara lain berbicara bahasa REST API yang berbeda-beda — beda endpoint, beda skema autentikasi, beda bentuk data. Tanpa adapter, tidak akan ada aliran listrik. Provider persis seperti adapter itu: ia tahu persis bagaimana mengubah deklarasi resource "aws_instance" menjadi serangkaian permintaan HTTP ke AWS API.
Implikasi penting dari konsep ini:
terraform CLI) hanyalah mesin yang mengeksekusi apa yang provider instruksikan. Inilah yang membuat Terraform cloud-agnostic: selama ada provider untuk platform tersebut, Terraform bisa mengelolanya dengan bahasa yang sama.hashicorp/aws, untuk Google Cloud hashicorp/google, untuk Azure hashicorp/azurerm, bahkan untuk Docker ada kreuzwerker/docker.ls -la .terraform/providers/registry.terraform.io/hashicorp/aws/drwxr-xr-x 5 dev staff 160 Aug 2 10:14 .
drwxr-xr-x 3 dev staff 96 Aug 2 10:14 ..
drwxr-xr-x 3 dev staff 96 Aug 2 10:14 5.74.0Important
Provider bukan bagian dari instalasi Terraform. Ia diunduh terpisah saat terraform init dari Terraform Registry dan disimpan di direktori lokal .terraform/. Karena itu, setiap kali kalian menambahkan provider baru di konfigurasi, kalian wajib menjalankan terraform init lagi.
required_providers vs provider BlockAda dua blok yang sering tertukar oleh pemula, padahal perannya berbeda:
terraform { required_providers { ... } } — berfungsi mendaftarkan provider mana yang dibutuhkan proyek ini, dari source mana, dan versi berapa. Ini semacam "daftar dependency" proyek.provider "aws" { ... } — berfungsi mengonfigurasi provider tersebut, misalnya region, profil kredensial, atau endpoint untuk custom cloud.Biasanya kedua blok dipisah ke file berbeda agar rapi, misalnya versions.tf dan provider.tf:
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}Beberapa hal yang perlu diperhatikan:
source adalah alamat lengkap provider di Registry, formatnya namespace/name. hashicorp/aws berarti provider resmi milik HashiCorp. Waspadai provider unofficial dengan nama yang mirip — ini pernah menjadi vektor serangan supply chain di ekosistem open source.version memakai version constraint. ~> 5.0 berarti "versi 5.x terbaru tapi tidak naik ke 6.x". Ini menjaga reproduksibilitas: kode yang berjalan di laptop kalian akan memakai versi provider yang sama di CI/CD nanti.aws dengan alias berbeda untuk region berbeda). Ini berguna untuk arsitektur multi-region.Tip
Meskipun blok provider boleh kosong (kredensial dibaca dari environment), selalu deklarasikan region secara eksplisit. Jangan pernah menggantungkan diri pada AWS_DEFAULT_REGION yang bisa berbeda-beda antar mesin — salah region bisa berarti infrastruktur dibuat di benua yang salah.
terraform init: Mengunduh ProviderSaat kalian menjalankan terraform init, Terraform membaca blok required_providers, lalu mengunduh plugin provider yang cocok dari Terraform Registry ke direktori .terraform/. Perhatikan baris-baris pada output berikut:
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.74.0...
- Installed hashicorp/aws v5.74.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include that file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!Ada dua output yang wajib kalian pahami:
Installed hashicorp/aws v5.74.0 (signed by HashiCorp) — Terraform memverifikasi tanda tangan digital provider sebelum menggunakannya, sebagai lapisan keamanan..terraform.lock.hcl — file lock yang mencatat versi persis provider yang dipakai. File ini wajib di-commit ke Git agar seluruh anggota tim memakai versi yang identik.Caution
Jangan pernah meng-commit direktori .terraform/ ke Git — ia besar dan spesifik per-mesin. Tapi wajib commit file .terraform.lock.hcl. Trik mengingatnya: folders boleh hilang, lockfile tidak boleh.
Jika provider adalah adapter, maka resource adalah bahan bangunannya — objek nyata yang ingin kalian ciptakan di cloud, seperti EC2 instance, S3 bucket, RDS database, atau security group. Ini adalah blok yang paling sering kalian tulis dalam karir IaC.
Sintaks dasarnya adalah:
resource "resource_type" "resource_name" {
argument_1 = "nilai"
argument_2 = "nilai"
}Perhatikan bahwa resource memakai dua label:
| Label | Fungsi | Contoh |
|---|---|---|
resource_type | Jenis resource dari perspektif provider | aws_instance, aws_s3_bucket |
resource_name | Nama logis lokal (hanya dikenal dalam kode kalian) | web, artifacts |
Kombinasi keduanya (aws_instance.web) menjadi alamat referensi yang bisa dipakai resource lain. Inilah dasar dari implicit dependency yang akan kita bahas lebih dalam di episode 7.
Berikut contoh resource aws_instance lengkap:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "web-server"
Env = "production"
}
}Ada dua kategori atribut dalam resource:
instance_type, tags). Ini yang ditulis di dalam blok.id, arn, dan public_ip. Kalian tidak menulisnya, tapi bisa membacanya untuk direferensikan resource lain, contohnya aws_instance.web.public_ip.Note
Penulisan atribut yang salah eja di dalam resource sering kali tidak menghasilkan error sintaks — Terraform hanya akan menganggapnya argumen tambahan dan mengeluh saat terraform plan. Selalu jalankan terraform validate setelah menulis resource untuk menangkap masalah ini lebih awal.
Untuk memberi gambaran luasnya ekosistem Terraform, berikut beberapa provider yang paling sering ditemui di dunia kerja:
| Provider | Source (Registry) | Digunakan untuk |
|---|---|---|
| AWS | hashicorp/aws | EC2, S3, VPC, RDS, IAM, EKS |
| Azure | hashicorp/azurerm | VM, Blob Storage, AKS, Virtual Network |
| Google Cloud | hashicorp/google | Compute Engine, GKE, Cloud Storage, VPC |
| Kubernetes | hashicorp/kubernetes | Mengelola workload di cluster K8s (deployment, service) |
| Docker | kreuzwerker/docker | Container, image, network di Docker daemon |
| Cloudflare | cloudflare/cloudflare | DNS, CDN, WAF, Zero Trust |
Tip
Kalian bisa menjalankan lebih dari satu provider dalam satu proyek — misalnya menyiapkan DNS record di Cloudflare untuk domain yang menunjuk ke load balancer AWS. Terraform akan membangun dependency graph lintas provider secara otomatis.
Ada kalanya kalian tidak perlu membuat resource karena objeknya sudah ada — dibuat manual oleh tim lain, atau memang infrastruktur yang tidak boleh diduplikasi (VPC perusahaan, AMI bawaan, subnet yang sudah dipartisi). Di sinilah data source bekerja.
Data source adalah blok yang hanya membaca (read-only) informasi dari cloud tanpa membuat atau mengubah apa pun. Bayangkan perbedaannya seperti ini: resource adalah kita yang membangun rumah dari nol, sedangkan data source adalah kita yang mengecek database alamat rumah yang sudah terdaftar di kelurahan.
Sintaksnya mirip resource, hanya kata kuncinya data:
data "data_source_type" "data_source_name" {
filter = "kriteria"
}Contoh paling klasik adalah mencari AMI Ubuntu terbaru untuk dipakai instance kita:
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}owners berisi ID akun AWS resmi Canonical (pembuat Ubuntu), dan filter mempersempit hasil pencarian. Dengan most_recent = true, Terraform memilih AMI terbaru yang cocok — sehingga kalian tidak perlu mengecek AMI ID secara manual di console setiap kali rilis baru keluar.
Selanjutnya, hasil data source bisa direferensikan dengan alamat data.<type>.<name>.<attribute>:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
}Perhatikan referensi data.aws_ami.ubuntu.id — kata kunci data di depannya adalah penanda bahwa ia milik data source, bukan resource.
Important
Perbedaan paling penting: resource membuat dan mengelola siklus hidup objek (bisa di-update dan di-destroy), sedangkan data source hanya membaca dan tidak akan pernah dihapus oleh Terraform. Memakai data untuk sesuatu yang seharusnya resource (atau sebaliknya) adalah salah satu kesalahan arsitektur tersering pada kode Terraform.
Sebagai penutup pembahasan, mari kita rangkai semuanya: data source untuk mencari AMI, resource untuk membuat instance, dan satu referensi lintas blok:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-southeast-1"
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
}
output "web_public_ip" {
value = aws_instance.web.public_ip
}Alur eksekusinya: terraform init mengunduh provider AWS → terraform plan menunjukkan bahwa satu AMI akan dibaca (data source) dan satu instance akan dibuat (resource) → terraform apply membuat instance sungguhan di AWS dan menampilkan IP publiknya.
| Kesalahan | Gejala | Solusi |
|---|---|---|
Lupa required_providers | Peringatan deprecated & provider ter-resolve ambigu | Selalu deklarasikan source dan version |
Tidak menjalankan terraform init setelah menambah provider | Error provider.aws: no suitable version installed | Jalankan terraform init ulang |
| Salah menulis nama atribut resource | Error saat terraform plan, bukan validate | Gunakan terraform validate + ekstensi editor |
| Hardcode AMI ID yang jarang di-refresh | Instance memakai OS versi lama | Pakai data source aws_ami |
| Data source menghasilkan banyak hasil tanpa filter | Error Your query returned more than one result | Tambahkan most_recent = true atau filter yang lebih spesifik |
Lupa alias saat memakai provider ganda | Resource dibuat di region yang salah | Deklarasikan provider ber-alias dan referensikan lewat provider = aws.west |
Pada episode 3 ini kita telah membahas tiga pilar fundamental Terraform: provider sebagai jembatan HCL menuju cloud API beserta mekanisme required_providers dan terraform init yang mengunduhnya; resource sebagai bahan bangunan infrastruktur dengan sintaks resource "type" "name"; serta data source untuk membaca infrastruktur yang sudah ada tanpa membuatnya ulang.
Inti dari episode ini adalah pemahaman akan siapa yang berbicara dengan cloud (provider), apa yang diciptakan (resource), dan apa yang dibaca (data source). Dengan fondasi ini, kalian sudah bisa membuat konfigurasi Terraform pertama yang benar-benar menghasilkan infrastruktur nyata di cloud.
Di episode 4 selanjutnya, kita akan membuat konfigurasi ini jauh lebih fleksibel dan reusable dengan Input Variables, Local Values & Output Values — sehingga satu set kode yang sama bisa dipakai untuk environment staging maupun production. Pastikan tetap semangat!