Import #

Jarang sekali tim memulai dari infrastruktur yang benar-benar kosong. Lebih sering, ada server, database, dan jaringan yang sudah berjalan dan dikelola secara manual jauh sebelum Terraform diperkenalkan. Import adalah mekanisme yang memungkinkan Terraform mengambil alih pengelolaan resource-resource ini tanpa harus menghapus dan membuat ulangnya. Dilakukan dengan benar, import adalah jembatan yang mulus antara infrastruktur manual dan infrastruktur as code.

flowchart TD
    A["☁️ Resource sudah ada\ndi Cloud (manual)"] --> B["terraform import\naws_instance.web i-12345"]
    B --> C["Resource tercatat\ndi state"]
    C --> D["Tulis HCL config\nyang sesuai"]
    D --> E["terraform plan\nNo changes ✅"]

    style A fill:#fff3e0,stroke:#e65100
    style B fill:#3b82f6,stroke:#1e40af,color:#fff
    style C fill:#e8f5e9,stroke:#2e7d32
    style D fill:#3b82f6,stroke:#1e40af,color:#fff
    style E fill:#10b981,stroke:#059669,color:#fff

Apa yang Import Lakukan (dan Tidak Lakukan) #

YANG IMPORT LAKUKAN:
  ✓ Membaca kondisi aktual resource dari cloud provider
  ✓ Menambahkan resource ke state Terraform
  ✓ Memungkinkan Terraform mengelola resource ini ke depannya

YANG IMPORT TIDAK LAKUKAN:
  ✗ Membuat atau mengubah konfigurasi .tf secara otomatis
    (kamu harus tulis sendiri — kecuali pakai -generate-config-out)
  ✗ Mengubah resource yang ada di cloud sedikitpun
  ✗ Menjamin konfigurasi .tf yang kamu tulis sudah sesuai
    (butuh terraform plan untuk verifikasi)

Menemukan Resource ID untuk Import #

Setiap resource memerlukan ID unik yang dikenali provider untuk proses import. Format ID berbeda-beda per resource type.

# AWS: Cara menemukan resource ID

# EC2 Instance — gunakan instance ID
aws ec2 describe-instances --query 'Reservations[].Instances[].InstanceId'
# Output: ["i-0abcdef1234567890"]

# VPC — gunakan VPC ID
aws ec2 describe-vpcs --query 'Vpcs[].VpcId'
# Output: ["vpc-0abcdef1234567890"]

# S3 Bucket — gunakan nama bucket
aws s3api list-buckets --query 'Buckets[].Name'
# Output: ["my-existing-bucket"]

# RDS Instance — gunakan DB identifier
aws rds describe-db-instances --query 'DBInstances[].DBInstanceIdentifier'
# Output: ["production-database"]

# Security Group — gunakan security group ID
aws ec2 describe-security-groups --query 'SecurityGroups[].GroupId'
# Output: ["sg-0abcdef1234567890"]
FORMAT ID UMUM DI BERBAGAI RESOURCE:
  aws_instance            → i-0abcdef1234567890
  aws_vpc                 → vpc-0abcdef1234567890
  aws_subnet              → subnet-0abcdef1234567890
  aws_security_group      → sg-0abcdef1234567890
  aws_s3_bucket           → nama-bucket (bukan ARN)
  aws_rds_instance        → db-identifier (bukan ARN)
  aws_iam_role            → nama-role
  aws_route53_record      → ZONE_ID_RECORD-NAME_TYPE
  aws_ecs_service         → cluster-name/service-name

Metode 1: Import via CLI #

Metode klasik menggunakan perintah terraform import di command line.

# Format: terraform import <resource_address> <resource_id>

# Langkah 1: Tulis blok resource kosong (atau dengan konfigurasi)
# main.tf:
resource "aws_instance" "web" {
  # Isi ini setelah import
}

# Langkah 2: Import
terraform import aws_instance.web i-0abcdef1234567890

# Output:
# aws_instance.web: Importing from ID "i-0abcdef1234567890"...
# aws_instance.web: Import prepared!
#   Prepared aws_instance for import
# aws_instance.web: Refreshing state... [id=i-0abcdef1234567890]
#
# Import successful!

# Langkah 3: Lihat semua atribut yang tersimpan di state
terraform state show aws_instance.web

# Langkah 4: Lengkapi konfigurasi berdasarkan output state show
resource "aws_instance" "web" {
  ami                    = "ami-0abcdef1234567890"
  instance_type          = "t3.micro"
  subnet_id              = "subnet-0abcdef1234"
  vpc_security_group_ids = ["sg-0abcdef1234567890"]

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

# Langkah 5: Verifikasi
terraform plan
# Hasil yang diharapkan: "No changes. Your infrastructure matches the configuration."

Metode 2: Import Blok Deklaratif (Terraform 1.5+) #

Blok import dalam konfigurasi lebih deklaratif, bisa di-commit ke version control, dan bisa digunakan bersama -generate-config-out.

# import.tf — file khusus untuk mendefinisikan import
import {
  to = aws_instance.web
  id = "i-0abcdef1234567890"
}

import {
  to = aws_vpc.main
  id = "vpc-0abcdef1234567890"
}

import {
  to = aws_security_group.web
  id = "sg-0abcdef1234567890"
}
# Generate konfigurasi otomatis berdasarkan kondisi aktual di cloud
terraform plan -generate-config-out=generated_resources.tf

# Output: file generated_resources.tf berisi konfigurasi yang di-generate
# Review file ini — mungkin ada atribut yang perlu disesuaikan

# Apply import
terraform apply

# Setelah selesai, hapus blok import (sudah tidak diperlukan)
# dan pindahkan konfigurasi dari generated_resources.tf ke file yang tepat

Import Massal: Strategi untuk Infrastruktur Besar #

Mengimport puluhan atau ratusan resource satu per satu tidak praktis. Ada strategi yang lebih efisien.

# Strategi 1: Script bash untuk import massal
#!/bin/bash

# Dapatkan semua instance ID
INSTANCES=$(aws ec2 describe-instances \
  --filters "Name=tag:ManagedBy,Values=manual" \
  --query 'Reservations[].Instances[].InstanceId' \
  --output text)

# Import setiap instance
for INSTANCE_ID in $INSTANCES; do
  # Buat nama resource dari tag Name
  NAME=$(aws ec2 describe-instances \
    --instance-ids $INSTANCE_ID \
    --query 'Reservations[0].Instances[0].Tags[?Key==`Name`].Value' \
    --output text | tr '[:upper:]' '[:lower:]' | tr ' ' '_')

  echo "Importing $NAME ($INSTANCE_ID)..."
  terraform import "aws_instance.$NAME" "$INSTANCE_ID"
done
# Strategi 2: for_each dalam blok import (Terraform 1.7+)
locals {
  instances = {
    web_1    = "i-0abcdef1234567890"
    web_2    = "i-0abcdef2345678901"
    worker_1 = "i-0abcdef3456789012"
  }
}

import {
  for_each = local.instances
  to       = aws_instance.servers[each.key]
  id       = each.value
}

resource "aws_instance" "servers" {
  for_each      = local.instances
  ami           = var.ami_id
  instance_type = "t3.micro"
}

Jebakan Umum Saat Import #

# JEBAKAN 1: Konfigurasi tidak cocok dengan kondisi aktual
# Setelah import, terraform plan menampilkan perubahan
# → konfigurasi .tf berbeda dari kondisi aktual
#
# terraform plan output:
# ~ resource "aws_instance" "web" {
#     ~ instance_type = "t3.micro" -> "t3.small"
#   }
#
# Ini berarti instance aktualnya t3.small, bukan t3.micro seperti yang kamu tulis
# Sesuaikan konfigurasi .tf dengan kondisi aktual

# JEBAKAN 2: Import resource dengan computed attributes
# Beberapa atribut hanya bisa diketahui setelah resource ada
# Jangan tulis nilai yang bisa berubah atau tidak deterministik di konfigurasi

# JEBAKAN 3: Import resource yang sudah punya dependency
# Jika VPC di-import tapi subnet belum, dependency graph tidak lengkap
# Import selalu dimulai dari resource "pondasi" (VPC, IAM role, dll.)
# sebelum resource yang bergantung padanya

# JEBAKAN 4: Lupa hapus blok import setelah selesai
# Blok import yang dibiarkan tidak menyebabkan error,
# tapi membuang waktu di setiap plan karena Terraform mencoba import ulang


Batch Import untuk Banyak Resource #

Import satu per satu memakan waktu. Untuk migrasi besar, gunakan loop script.

# Script batch import untuk EC2 instances
INSTANCES=$(aws ec2 describe-instances \
  --filters "Name=tag:ManagedBy,Values=manual" \
  --query 'Reservations[].Instances[].InstanceId' \
  --output text)

for INSTANCE_ID in $INSTANCES; do
  NAME=$(aws ec2 describe-instances \
    --instance-ids $INSTANCE_ID \
    --query 'Reservations[].Instances[].Tags[?Key==\`Name\`].Value' \
    --output text)
  
  RESOURCE_NAME="web_server_$(echo $NAME | tr '-' '_')"
  
  echo "Importing $INSTANCE_ID as aws_instance.$RESOURCE_NAME"
  terraform import "aws_instance.$RESOURCE_NAME" "$INSTANCE_ID"
done
flowchart TD
    A["AWS Console
(manual resources)"] --> B["Discover resources
(AWS CLI/SDK)"]
    B --> C["Generate Terraform
config (HCL)"]
    C --> D["terraform import
(each resource)"]
    D --> E["Verify: terraform plan
should show 'No changes'"]
    E --> F["Resources now
managed by Terraform ✅"]

    style A fill:#fff3e0,stroke:#e65100
    style B fill:#e3f2fd,stroke:#1565c0
    style C fill:#e3f2fd,stroke:#1565c0
    style D fill:#e3f2fd,stroke:#1565c0
    style E fill:#e8f5e9,stroke:#2e7d32
    style F fill:#e8f5e9,stroke:#2e7d32

Import dengan for_each #

Untuk import banyak resource yang serupa, gunakan pola for_each.

# Definisikan resource dengan for_each
resource "aws_security_group_rule" "ingress" {
  for_each = var.security_group_rules

  type              = "ingress"
  from_port         = each.value.from_port
  to_port           = each.value.to_port
  protocol          = each.value.protocol
  cidr_blocks       = each.value.cidr_blocks
  security_group_id = aws_security_group.main.id
}

# Import satu per satu
terraform import 'aws_security_group_rule.ingress["http"]'   sg-12345_ingress_tcp_80_80_0.0.0.0/0

terraform import 'aws_security_group_rule.ingress["https"]'   sg-12345_ingress_tcp_443_443_0.0.0.0/0
# Script helper untuk import massal
#!/bin/bash
RULES=("http:80:80:tcp:0.0.0.0/0" "https:443:443:tcp:0.0.0.0/0")
SG_ID="sg-12345"

for RULE in "${RULES[@]}"; do
  IFS=':' read -r NAME FROM TO PROTO CIDR <<< "$RULE"
  IMPORT_ID="${SG_ID}_ingress_${PROTO}_${FROM}_${TO}_${CIDR}"
  echo "Importing aws_security_group_rule.ingress["$NAME"]"
  terraform import "aws_security_group_rule.ingress["$NAME"]" "$IMPORT_ID"
done

Import Troubleshooting #

# Masalah 1: "Cannot import non-existent remote object"
# Artinya: resource tidak ditemukan di cloud provider
# Solusi: pastikan ID benar, cek region yang tepat

# Masalah 2: "Resource already managed by Terraform"
# Artinya: resource sudah ada di state
# Solusi: hapus dari state dulu, baru import lagi
terraform state rm aws_instance.web
terraform import aws_instance.web i-12345

# Masalah 3: "Import ID tidak match dengan resource"
# Contoh: import EC2 tapi ID-nya bukan instance ID
# Cek dokumentasi provider untuk format import ID yang benar

# Masalah 4: "Error: Cannot import to non-existent resource address"
# Artinya: resource block belum ada di HCL
# Solusi: tulis resource block dulu, baru jalankan import

Multi-Resource Import #

# Import beberapa resource sekaligus (script)
#!/bin/bash
resources=(
  "aws_instance.web:i-12345"
  "aws_security_group.web:sg-67890"
  "aws_subnet.main:subnet-abcde"
)

for item in "${resources[@]}"; do
  IFS=":" read -r addr id <<< "$item"
  echo "Importing $addr with ID $id"
  terraform import "$addr" "$id"
done
# Import dengan Terraform Cloud
# Terraform Cloud mendukung import via UI
# Navigate ke workspace → Settings → Import
# Atau gunakan API:
curl -X POST "https://app.terraform.io/api/v2/workspaces/$WS_ID/actions/import"   -H "Authorization: Bearer $TOKEN"   -d '{"data":{"type":"imports","attributes":{"address":"aws_instance.web","id":"i-12345"}}}'

Ringkasan #

  • Import tidak mengubah resource di cloud — ia hanya menambahkan resource ke state Terraform.
  • Import tidak membuat konfigurasi .tf otomatis (kecuali dengan -generate-config-out) — kamu harus tulis konfigurasi sendiri lalu verifikasi dengan terraform plan.
  • Tujuan akhir import yang benar: terraform plan menampilkan “No changes” setelah import selesai.
  • Blok import deklaratif (Terraform 1.5+) lebih clean dari CLI command — bisa di-commit, bisa di-review, bisa digunakan bersama -generate-config-out.
  • Import dari resource pondasi dulu — VPC sebelum subnet, subnet sebelum instance — agar dependency graph terbentuk dengan benar.
  • Hapus blok import setelah selesai — resource sudah di state, blok import tidak diperlukan lagi.

← Sebelumnya: Migration   Berikutnya: Anti-Pattern →

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