Terraform Bulk Import: Bring Existing AWS Resources Under Management
Import dozens of existing AWS resources into Terraform at once using import blocks, for_each, and generate-config-out.
DevOps
Use Terraform import blocks to bring existing AWS resources under management. Code-based import without CLI commands, generate config automatically
The import block (Terraform 1.5+) lets you bring existing infrastructure under Terraform management without CLI commands. Write it in HCL, run terraform plan, and Terraform generates the import plan. This replaces the old terraform import CLI workflow for most use cases.
# One resource at a time, imperative
terraform import aws_instance.web i-0abc123def456
terraform import aws_s3_bucket.data my-bucket-name
terraform import aws_vpc.main vpc-0abc123Problems:
# Declarative, code-reviewed, bulk-capable
import {
to = aws_instance.web
id = "i-0abc123def456"
}
import {
to = aws_s3_bucket.data
id = "my-bucket-name"
}
import {
to = aws_vpc.main
id = "vpc-0abc123"
}terraform plan # Shows what will be imported — no changes yet
terraform apply # Imports into state# imports.tf
import {
to = aws_instance.web
id = "i-0abc123def456"
}# main.tf
resource "aws_instance" "web" {
ami = "ami-abc123"
instance_type = "t3.micro"
subnet_id = "subnet-xyz789"
tags = {
Name = "web-server"
}
}terraform plan
# aws_instance.web: Importing...
# Plan: 1 to import, 0 to add, 0 to change, 0 to destroy
terraform apply
# aws_instance.web: Importing... [id=i-0abc123def456]
# Import successful!After applying, the import block is no longer needed:
# imports.tf — can be deleted after successful import
# import {
# to = aws_instance.web
# id = "i-0abc123def456"
# }Don't want to write the resource block manually? Use -generate-config-out:
terraform plan -generate-config-out=generated.tfThis creates generated.tf with the full resource configuration based on the actual cloud resource:
# generated.tf (auto-generated)
resource "aws_instance" "web" {
ami = "ami-abc123"
instance_type = "t3.micro"
subnet_id = "subnet-xyz789"
vpc_security_group_ids = ["sg-abc123"]
associate_public_ip_address = true
key_name = "my-key"
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
tags = {
Name = "web-server"
}
}Review and clean up the generated config (remove computed attributes, add variables), then apply.
Import many resources at once:
# Import an entire VPC setup
import {
to = aws_vpc.main
id = "vpc-0abc123"
}
import {
to = aws_subnet.public[0]
id = "subnet-pub1"
}
import {
to = aws_subnet.public[1]
id = "subnet-pub2"
}
import {
to = aws_subnet.private[0]
id = "subnet-priv1"
}
import {
to = aws_subnet.private[1]
id = "subnet-priv2"
}
import {
to = aws_internet_gateway.main
id = "igw-xyz789"
}
import {
to = aws_nat_gateway.main
id = "nat-abc456"
}
import {
to = aws_route_table.public
id = "rtb-pub123"
}
import {
to = aws_route_table.private
id = "rtb-priv456"
}# Generate config for all
terraform plan -generate-config-out=generated_vpc.tf
# Review, clean up, then apply
terraform applylocals {
existing_buckets = {
logs = "company-logs-bucket"
data = "company-data-bucket"
backups = "company-backups-bucket"
}
}
import {
for_each = local.existing_buckets
to = aws_s3_bucket.imported[each.key]
id = each.value
}
resource "aws_s3_bucket" "imported" {
for_each = local.existing_buckets
bucket = each.value
}| Resource | Import ID Format | Example |
|---|---|---|
aws_instance | Instance ID | i-0abc123def456 |
aws_s3_bucket | Bucket name | my-bucket |
aws_vpc | VPC ID | vpc-0abc123 |
aws_subnet | Subnet ID | subnet-xyz789 |
aws_security_group | SG ID | sg-abc123 |
aws_db_instance | DB identifier | my-database |
aws_iam_role | Role name | my-role |
aws_route53_zone | Zone ID | Z1234567890 |
aws_lambda_function | Function name | my-function |
aws_ecs_cluster | Cluster ARN | arn:aws:ecs:... |
This is normal — the imported resource may differ from your written config:
terraform plan
# aws_instance.web will be updated in-place
# ~ instance_type = "t3.micro" -> "t3.small"Fix: update your HCL to match the actual resource, or accept the planned change.
Some attributes are read-only (computed). Remove them from generated config:
resource "aws_instance" "web" {
# Remove these — they're computed
# arn = "arn:aws:ec2:..."
# id = "i-0abc123"
# public_ip = "1.2.3.4"
ami = "ami-abc123"
instance_type = "t3.micro"
}Import only adds existing resources to state. It never creates, modifies, or deletes the actual cloud resource during the import step.
The import block replaces terraform import CLI for most workflows. It's declarative, code-reviewable, and supports bulk imports. Use -generate-config-out to auto-generate resource configurations, then clean up and apply. Remove import blocks after successful import — they're one-time use.
Import dozens of existing AWS resources into Terraform at once using import blocks, for_each, and generate-config-out.
Learn Terraform data sources to read existing AWS resources, look up AMIs, query remote state, and reference external information in your configurations.
Learn when to use Terraform depends_on for explicit resource dependencies. Understand implicit vs explicit dependencies, common use cases
Terraform for_each vs count explained with practical examples. Learn when to use each, how to migrate from count to for_each