Impor Infrastruktur yang Sudah Ada ke Terraform Tanpa Menulis HCL Manual

Foto oleh RukshanV via Wikimedia Commons (CC BY-SA 4.0)
Perintah CLI terraform import bersifat imperatif: ia langsung menulis resource ke dalam state dan tidak menghasilkan konfigurasi, tanpa meninggalkan jejak di kode Anda. Import block bersifat deklaratif — Anda commit ke file .tf, ia muncul di terraform plan, melewati code review, dan tidak ada yang berubah di state sampai terraform apply. Import block ditambahkan di Terraform 1.5.
Saat Anda menjalankan terraform plan -generate-config-out=file.tf, Terraform membaca setiap objek yang sudah ada yang dirujuk oleh import block tanpa resource cocok, lalu menulis HCL dengan tebakan terbaiknya untuk tiap argumen ke file itu. Ia memberi Anda scaffold konfigurasi resource alih-alih file kosong, sehingga Anda memperbaiki draf daripada menulis semuanya manual. Diperkenalkan di Terraform 1.5 dan masih eksperimental.
Tidak. Generasi config tidak bisa dipakai dengan import block yang menggunakan for_each, atau dengan resource yang menggunakan for_each atau count, dan tidak berfungsi untuk resource di dalam modul yang belum ada. Terraform 1.7 menambahkan dukungan for_each pada import block itu sendiri, tapi generasi config untuk instance tersebut masih belum tercakup. Generate terhadap alamat resource top-level biasa dulu, lalu refactor HCL yang sudah dibersihkan ke koleksi atau modul Anda secara manual.
Penyebab paling umum adalah mengarahkan flag ke file yang sudah ada — Terraform menolak menimpanya dan error, jadi hapus file yang dihasilkan antar-run. Ia juga gagal dengan remote backend atau HCP Terraform karena flag harus menulis ke filesystem lokal. Terakhir, config yang dihasilkan bisa memuat argumen berkonflik (misalnya ipv6_address_count dan ipv6_addresses bersamaan) yang gagal validasi sampai Anda menyuntingnya.
Tidak. Import block adalah scaffolding sekali pakai. Begitu terraform apply meng-commit objek ke state, blok tersebut tidak melakukan apa pun pada plan berikutnya, jadi Anda bisa menghapusnya dengan aman di pull request lanjutan. Menyimpannya di file khusus seperti imports.tf membuat pembersihan itu menjadi perubahan satu file.

Foto oleh RukshanV via Wikimedia Commons (CC BY-SA 4.0)
Ringkasan Utama
Terraform 1.5 menambahkan import block: impor deklaratif yang Anda tulis di konfigurasi, lihat di terraform plan, dan review seperti perubahan kode lainnya. Pasangkan dengan flag -generate-config-out dan Terraform menulis HCL tebakan-terbaik untuk tiap resource yang diimpor, sehingga Anda memperbaiki draf alih-alih menulis tiap argumen manual dari nol.
Hampir setiap pekerjaan infrastruktur yang saya ambil dimulai dengan masalah yang sama: sesuatu sudah ada. Load balancer yang dibuat lewat konsol dua tahun lalu, database yang dibuat saat insiden, zona DNS yang tidak ada yang ingat pernah menyentuhnya. Terraform paling senang dengan greenfield, tapi kenyataan hidup adalah brownfield. Pertanyaannya bukan apakah harus membawa infrastruktur yang sudah ada ke Terraform — tapi berapa banyak rasa sakit yang dibebankan migrasi itu.
Selama bertahun-tahun jawabannya adalah: banyak. Perintah CLI terraform import lama hanya menulis resource ke dalam state — tidak pernah menghasilkan konfigurasi yang cocok. Anda mengimpor resource, lalu menulis HCL manual, lalu menjalankan plan dan berdoa diff kembali kosong. Untuk apa pun yang lebih dari segelintir resource, loop itu melelahkan dan rawan kesalahan. Sejak Terraform 1.5, import block plus flag -generate-config-out mengubah bentuk pekerjaan itu sepenuhnya.
Impor CLI klasik bersifat imperatif: Anda menjalankan perintah, ia langsung memutasi state, dan tidak meninggalkan jejak di konfigurasi atau pull request Anda. Dua masalah muncul dari situ. Pertama, tidak ada yang bisa direview — rekan tim tidak bisa melihat di diff apa yang Anda impor. Kedua, Anda tetap harus menulis sendiri setiap argumen resource, mencocokkan apa pun yang sebenarnya dimiliki penyedia cloud, atau plan berikutnya menampilkan perubahan destruktif.
# The old imperative way — state changes instantly, no config generated
terraform import aws_instance.web i-0abc123def456
# Now you STILL have to hand-write this block to match reality:
resource "aws_instance" "web" {
# ...guess every argument, or the next plan wants to destroy it
}Import block adalah blok Terraform biasa yang Anda commit ke file .tf. Ia menamai alamat resource tempat objek yang diimpor akan berada (argumen to) dan ID spesifik-penyedia dari objek yang sudah ada (argumen id). Karena ia berada di konfigurasi Anda, ia muncul di terraform plan, melewati code review, dan tidak ada yang menyentuh state sampai terraform apply. Itulah keseluruhan pergeserannya: impor menjadi deklaratif dan bisa direview alih-alih perintah konsol sekali pakai.
# imports.tf — declarative, reviewable, applied on `terraform apply`
import {
to = aws_instance.web
id = "i-0abc123def456"
}
import {
to = aws_s3_bucket.assets
id = "my-company-assets"
}Simpan import block di file khusus seperti imports.tf. Mereka adalah scaffolding sekali pakai — begitu apply mendarat dan resource masuk ke state, blok tersebut tidak melakukan apa pun pada plan berikutnya dan Anda bisa menghapusnya di PR lanjutan. Mengelompokkannya membuat pembersihan itu menjadi perubahan satu file.
Import block sendirian masih mengasumsikan blok resource yang cocok sudah ada. Keajaibannya adalah flag -generate-config-out, diperkenalkan bersama import block di Terraform 1.5 dan masih berlabel eksperimental. Arahkan terraform plan ke path file baru dan Terraform membaca objek live untuk setiap import block yang belum punya resource cocok, lalu menulis HCL dengan tebakan terbaiknya untuk tiap argumen. Anda mendapat scaffold, bukan modul jadi — tapi scaffold lebih baik daripada file kosong.
# Point at a NEW file path — Terraform errors if the file already exists
terraform plan -generate-config-out="generated.tf"
# Terraform reads each imported object and writes something like:
resource "aws_instance" "web" {
ami = "ami-0abc1234"
instance_type = "t3.medium"
availability_zone = "ap-southeast-1a"
vpc_security_group_ids = ["sg-0123456789"]
subnet_id = "subnet-0abc123"
# ...every discoverable argument, filled in for you
}Jangan commit output -generate-config-out tanpa disentuh. Terraform cenderung ke arah kelengkapan, jadi ia memancarkan atribut read-only, nilai computed, dan default yang mengacaukan file atau justru merusak plan. Dokumentasinya menyebut contoh konkret: ia bisa menghasilkan baik ipv6_address_count maupun ipv6_addresses pada sebuah instance meski kedua argumen itu berkonflik, menghasilkan HCL yang tidak akan lolos validasi. Perlakukan file itu sebagai draf pertama yang Anda bersihkan.
-generate-config-out punya batasan nyata. Ia tidak bisa menghasilkan config untuk import block yang menggunakan for_each, atau untuk resource yang menggunakan for_each atau count, dan ia tidak bekerja dengan remote backend atau HCP Terraform karena harus menulis ke filesystem lokal. Terraform 1.7 menambahkan dukungan for_each pada import block itu sendiri, tapi generasi config untuk instance tersebut masih belum tercakup — generate terhadap alamat top-level biasa dulu, lalu refactor ke koleksi Anda secara manual.
Pada migrasi baru-baru ini saya punya beberapa lusin objek untuk dibawa masuk lintas satu penyedia. Pola yang berhasil: enumerasi ID nyata dengan CLI penyedia, buat template satu import block per ID ke imports.tf, lalu jalankan satu pass generate. Anda mereview file yang dihasilkan dalam satu duduk, membersihkannya, dan mendaratkannya sebagai satu PR yang bisa direview — jauh lebih baik daripada loop shell dari perintah terraform import yang tidak bisa diaudit siapa pun setelahnya.
# Enumerate real IDs, then template import blocks (AWS S3 buckets example)
aws s3api list-buckets --query 'Buckets[].Name' --output text \
| tr '\t' '\n' \
| while read -r b; do
printf 'import {\n to = aws_s3_bucket.%s\n id = "%s"\n}\n\n' \
"$(echo "$b" | tr '.-' '__')" "$b"
done > imports.tf
terraform plan -generate-config-out=generated.tf| Aspek | terraform import (CLI) | import block + generate-config-out |
|---|---|---|
| Gaya | Imperatif, langsung berjalan | Deklaratif, diterapkan saat apply |
| Bisa direview di PR | Tidak — tak meninggalkan jejak config | Ya — muncul di plan dan diff |
| Menghasilkan config | Tidak pernah — hanya state | Ya — draf HCL tebakan terbaik |
| Impor massal | Satu perintah per resource | Banyak blok, satu pass plan |
| for_each / count | Manual, per instance | Blok dukung for_each (1.7+); gen tidak |
| Remote backend / HCP | Berfungsi | generate-config-out perlu FS lokal |
Kemenangan sejatinya bukan hemat ketikan — tapi auditabilitas. Impor menjadi pull request yang bisa direview, disetujui, dan di-rollback tim Anda, dengan config yang dihasilkan sebagai titik awal. Perubahan tunggal itu memindahkan impor infrastruktur brownfield dari pekerjaan konsol menakutkan menjadi bagian normal dan membosankan dari alur kerja IaC saya.