Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

terraform import

terraform import brings existing infrastructure that was created outside Terraform into Terraform’s state management. This lets you gradually adopt IaC without recreating resources — critical for migrating production infrastructure to Terraform without downtime.


The Problem terraform import Solves

Your team manually created an RDS database, several S3 buckets, and a VPC six months ago. Now you want to manage all of it with Terraform. You can’t just write the config and run apply — Terraform would try to create duplicates. Import tells Terraform: “This existing resource should be tracked in state as this resource block.”


Two Ways to Import (Old vs. New)

# import.tf — declare what to import
import {
to = aws_s3_bucket.data_lake
id = "my-existing-data-lake-bucket"
}
import {
to = aws_instance.web_server
id = "i-1234567890abcdef0"
}
import {
to = aws_db_instance.production
id = "prod-postgres-db"
}

Run plan to preview, then apply to import:

Terminal window
terraform plan # Shows: 3 resources to import
terraform apply # Imports and updates state

Classic: Command-Line Import

Terminal window
terraform import <resource_type>.<resource_name> <provider_resource_id>
# AWS examples
terraform import aws_s3_bucket.data_lake my-existing-bucket
terraform import aws_instance.web i-1234567890abcdef0
terraform import aws_security_group.web sg-0abc123def456
terraform import aws_vpc.main vpc-0abc123def456789
# Azure examples
terraform import azurerm_resource_group.main \
/subscriptions/{sub-id}/resourceGroups/my-resource-group
# GCP examples
terraform import google_compute_instance.web \
projects/my-project/zones/us-central1-a/instances/my-instance

Config Generation (Terraform 1.5+)

The most powerful new capability — Terraform can generate the configuration for you:

Terminal window
# 1. Write import blocks in import.tf
# 2. Generate the HCL configuration automatically
terraform plan -generate-config-out=generated.tf
# 3. Review and edit generated.tf
# 4. Apply to complete the import
terraform apply

Generated output example:

# generated.tf (auto-generated — review and clean up)
resource "aws_s3_bucket" "data_lake" {
bucket = "my-existing-data-lake-bucket"
tags = {
Environment = "production"
Owner = "data-team"
}
}
resource "aws_s3_bucket_versioning" "data_lake" {
bucket = aws_s3_bucket.data_lake.id
versioning_configuration {
status = "Enabled"
}
}

Step-by-Step Import Workflow

Step 1: Find the Resource ID

Each resource type uses a specific ID format. Look in the provider documentation or the cloud console:

ResourceID Format
aws_s3_bucketBucket name: my-bucket
aws_instanceInstance ID: i-1234567890abcdef0
aws_vpcVPC ID: vpc-0abc123
aws_security_groupSG ID: sg-0abc123
aws_iam_roleRole name: my-role
aws_rds_clusterCluster identifier: my-cluster
azurerm_virtual_machineFull ARM resource ID

Step 2: Write the Resource Block

Before importing, you must have a resource block in your configuration:

resource "aws_s3_bucket" "data_lake" {
bucket = "my-existing-data-lake-bucket"
# Attributes will be populated from state after import
}

Step 3: Import

Terminal window
terraform import aws_s3_bucket.data_lake my-existing-data-lake-bucket

Step 4: Reconcile Configuration

After import, terraform plan likely shows differences between your config and the actual resource. Update your config to match reality:

Terminal window
terraform plan
# Shows: ~ aws_s3_bucket.data_lake will be updated in-place
# ~ tags = {} -> {"Environment" = "production"}

Update the config to reflect the actual tags, then re-run plan until it shows No changes.


Importing Multiple Resources: Bulk Import Script

#!/bin/bash
# bulk_import.sh — import many resources at once
# S3 buckets
buckets=("logs-bucket" "data-lake" "artifacts" "backups")
for bucket in "${buckets[@]}"; do
terraform import "aws_s3_bucket.${bucket//-/_}" "$bucket"
done
# EC2 instances from a tag query
aws ec2 describe-instances \
--filters "Name=tag:Environment,Values=production" \
--query 'Reservations[].Instances[].InstanceId' \
--output text | \
while read -r id; do
name=$(aws ec2 describe-instances --instance-ids "$id" \
--query 'Reservations[0].Instances[0].Tags[?Key==`Name`].Value' \
--output text)
terraform import "aws_instance.${name}" "$id"
done

Common Import Pitfalls

Pitfall 1: Forgetting sub-resources

Importing aws_s3_bucket doesn’t automatically import associated resources like aws_s3_bucket_versioning, aws_s3_bucket_lifecycle_configuration, or aws_s3_bucket_policy. Each sub-resource must be imported separately.

Pitfall 2: Plan shows destroy after import

Your config doesn’t match the real resource’s attributes. Run terraform state show <resource> to see what Terraform imported, then update your config.

Terminal window
terraform state show aws_s3_bucket.data_lake
# Shows all actual attributes — copy relevant ones to your config

Pitfall 3: Duplicate resource

Terraform will error if you try to import into a resource name that already exists in state. Use terraform state list to check first.