Introduction
You have cloud resources running in production that weren’t created with Terraform. Maybe they were created manually in the AWS console, or through a different tool. Now you want to manage them with Terraform without recreating them.
That’s exactly what terraform import does — it brings existing infrastructure under Terraform management by adding it to the state file.
Quick Start
Import an existing AWS EC2 instance:
# 1. Write the resource block in your .tf file
# main.tf
resource "aws_instance" "web" {
# Configuration will be filled after import
}
# 2. Import the resource
terraform import aws_instance.web i-1234567890abcdef0
# 3. Verify the import
terraform state show aws_instance.web
# 4. Fill in the configuration to match the actual resource
# Run terraform plan — it should show "No changes"
Two Methods: CLI Import vs Import Blocks
Method 1: terraform import Command (All Versions)
The classic approach — one resource at a time:
terraform import <resource_type>.<name> <resource_id>
Examples:
# AWS EC2 instance
terraform import aws_instance.web i-1234567890abcdef0
# AWS S3 bucket
terraform import aws_s3_bucket.data my-bucket-name
# AWS Security Group
terraform import aws_security_group.web sg-0123456789abcdef0
# AWS VPC
terraform import aws_vpc.main vpc-0123456789abcdef0
# Azure Resource Group
terraform import azurerm_resource_group.main /subscriptions/SUB_ID/resourceGroups/my-rg
# GCP Compute Instance
terraform import google_compute_instance.web projects/my-project/zones/us-central1-a/instances/my-vm
Method 2: Import Blocks (Terraform 1.5+)
Import blocks are declarative — add them to your .tf files and run terraform plan:
# imports.tf
import {
to = aws_instance.web
id = "i-1234567890abcdef0"
}
import {
to = aws_s3_bucket.data
id = "my-bucket-name"
}
import {
to = aws_security_group.web
id = "sg-0123456789abcdef0"
}
Then run:
terraform plan -generate-config-out=generated.tf
Terraform generates the configuration automatically. Review generated.tf, clean it up, and merge it into your main configuration.
Advantages of import blocks:
- Can import multiple resources at once
- Configuration can be auto-generated
- Import is part of the plan/apply workflow
- Works with Terraform Cloud
Step-by-Step: Import an AWS EC2 Instance
Step 1: Find the Resource ID
aws ec2 describe-instances --filters "Name=tag:Name,Values=my-server" \
--query "Reservations[].Instances[].InstanceId" --output text
Output: i-0abc123def456789
Step 2: Write the Resource Block
Create a minimal resource block in your .tf file:
resource "aws_instance" "web" {
# Required attributes — we'll fill these after import
ami = "ami-placeholder"
instance_type = "t3.micro"
}
Step 3: Run the Import
terraform import aws_instance.web i-0abc123def456789
Output:
aws_instance.web: Importing from ID "i-0abc123def456789"...
aws_instance.web: Import prepared!
Prepared aws_instance for import
aws_instance.web: Refreshing state... [id=i-0abc123def456789]
Import successful!
Step 4: Get the Full Configuration
terraform state show aws_instance.web
This prints all attributes. Copy the relevant ones into your resource block:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
subnet_id = "subnet-0123456789abcdef0"
key_name = "my-key"
vpc_security_group_ids = ["sg-0123456789abcdef0"]
root_block_device {
volume_size = 20
volume_type = "gp3"
}
tags = {
Name = "my-server"
}
}
Step 5: Verify — No Changes
terraform plan
Should show:
No changes. Your infrastructure matches the configuration.
If it shows changes, update your configuration to match the actual resource state.
Bulk Import: Multiple Resources
Using Import Blocks (Recommended)
# imports.tf — import your entire VPC setup
import {
to = aws_vpc.main
id = "vpc-0123456789"
}
import {
to = aws_subnet.public_a
id = "subnet-aaa111"
}
import {
to = aws_subnet.public_b
id = "subnet-bbb222"
}
import {
to = aws_internet_gateway.main
id = "igw-0123456789"
}
import {
to = aws_route_table.public
id = "rtb-0123456789"
}
terraform plan -generate-config-out=generated_vpc.tf
Using a Shell Script
For many resources of the same type:
#!/bin/bash
# Import all EC2 instances with a specific tag
INSTANCES=$(aws ec2 describe-instances \
--filters "Name=tag:Environment,Values=production" \
--query "Reservations[].Instances[].InstanceId" \
--output text)
i=0
for id in $INSTANCES; do
terraform import "aws_instance.prod[$i]" "$id"
((i++))
done
Import for Different Cloud Providers
AWS Common Resources
# VPC
terraform import aws_vpc.main vpc-12345
# Subnet
terraform import aws_subnet.public subnet-12345
# Security Group
terraform import aws_security_group.web sg-12345
# RDS Instance
terraform import aws_db_instance.main my-database
# IAM Role
terraform import aws_iam_role.lambda my-lambda-role
# S3 Bucket
terraform import aws_s3_bucket.logs my-log-bucket
# Route53 Zone
terraform import aws_route53_zone.main Z1234567890
Azure Common Resources
# Resource Group
terraform import azurerm_resource_group.main \
/subscriptions/SUB_ID/resourceGroups/my-rg
# Virtual Network
terraform import azurerm_virtual_network.main \
/subscriptions/SUB_ID/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet
# Storage Account
terraform import azurerm_storage_account.main \
/subscriptions/SUB_ID/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mystorageaccount
GCP Common Resources
# Compute Instance
terraform import google_compute_instance.web \
projects/my-project/zones/us-central1-a/instances/my-vm
# GKE Cluster
terraform import google_container_cluster.primary \
projects/my-project/locations/us-central1/clusters/my-cluster
# Cloud Storage Bucket
terraform import google_storage_bucket.data my-bucket
Common Errors and Fixes
“Resource already managed by Terraform”
Error: Resource already managed by Terraform
The resource is already in your state. Check with:
terraform state list | grep aws_instance
“Cannot import non-existent remote object”
Error: Cannot import non-existent remote object
The resource ID is wrong or the resource was deleted. Verify it exists:
aws ec2 describe-instances --instance-ids i-1234567890abcdef0
Configuration drift after import
If terraform plan shows changes after import, your .tf configuration doesn’t match the real resource. Update your config to match, or accept the drift by running apply.
Best Practices
- Import to a separate state first: Test imports in a workspace before importing into production state
- Use import blocks (Terraform 1.5+): They’re declarative, reviewable, and support auto-generated configs
- Run plan immediately after import: Verify no unexpected changes
- Back up your state:
cp terraform.tfstate terraform.tfstate.backupbefore importing - Don’t import and modify in the same apply: Import first, verify, then make changes
- Tag resources before importing: Makes identification easier
- Remove import blocks after apply: They’re only needed for the initial import
Hands-On Courses
Learn by doing with interactive courses on CopyPasteLearn:
Conclusion
terraform import bridges the gap between manually created infrastructure and Terraform management. Use the CLI command for quick one-off imports, and import blocks (Terraform 1.5+) for bulk imports with auto-generated configurations. Always verify with terraform plan after importing to ensure your configuration matches the real infrastructure.




