TerraformPilot

DevOps

Terraform Import — Quick Start to Bring Existing Resources Under Management

Step-by-step guide to importing existing cloud resources into Terraform state. Import blocks, generate config, import CLI, and avoid common pitfalls.

LLuca Berton2 min read

Quick Answer

#

Use terraform import CLI or import blocks (Terraform 1.5+) to bring existing cloud resources under Terraform management without recreating them. Write the matching HCL config first, then import. Always run terraform plan after importing to check for drift.

Why Import?

#

Organizations often have resources created manually via console, CLI, or other tools. Terraform import lets you manage these existing resources as code — gaining version control, change tracking, and reproducibility.

#
# main.tf
import {
  to = aws_s3_bucket.data
  id = "my-existing-bucket"
}
 
resource "aws_s3_bucket" "data" {
  bucket = "my-existing-bucket"
}
# Generate config automatically
terraform plan -generate-config-out=generated.tf
 
# Review the generated config, then apply
terraform apply

Benefits of import blocks:

  • Declarative — import is part of your config, not a manual CLI step
  • Config generationgenerate-config-out writes the HCL for you
  • Reviewable — shows in terraform plan before applying
  • Batch imports — multiple import blocks in one file

Bulk Import with for_each

#
locals {
  existing_buckets = {
    data   = "my-data-bucket"
    logs   = "my-logs-bucket"
    backup = "my-backup-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
}

Method 2: CLI Import

#
# Format: terraform import <resource_address> <cloud_id>
 
# AWS examples
terraform import aws_instance.web i-1234567890abcdef0
terraform import aws_s3_bucket.data my-bucket-name
terraform import aws_vpc.main vpc-abc123
terraform import aws_iam_role.lambda my-lambda-role
terraform import aws_security_group.web sg-12345678
 
# Azure examples
terraform import azurerm_resource_group.main /subscriptions/SUB_ID/resourceGroups/my-rg
terraform import azurerm_virtual_network.main /subscriptions/SUB_ID/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet
 
# GCP examples
terraform import google_compute_instance.web projects/my-project/zones/us-central1-a/instances/my-vm
terraform import google_storage_bucket.data my-project/my-bucket

Step-by-Step Workflow

#

1. Write the Resource Config

#
# Write a minimal config that matches the existing resource
resource "aws_instance" "web" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
 
  tags = {
    Name = "web-server"
  }
}

2. Import

#
terraform import aws_instance.web i-1234567890abcdef0

3. Plan to Check Drift

#
terraform plan
# Review what Terraform wants to change
# Update your config to match the actual resource

4. Iterate Until Clean

#

Keep adjusting your HCL until terraform plan shows no changes:

# Common drift: tags, security groups, IAM policies
terraform plan
# Adjust config → plan again → repeat until clean

Common Import IDs by Resource

#
ResourceImport ID FormatExample
aws_instanceInstance IDi-1234567890abcdef0
aws_s3_bucketBucket namemy-bucket
aws_vpcVPC IDvpc-abc123
aws_subnetSubnet IDsubnet-abc123
aws_security_groupSG IDsg-12345678
aws_iam_roleRole namemy-role
aws_iam_policyPolicy ARNarn:aws:iam::123:policy/my-policy
aws_db_instanceDB identifiermy-database
aws_route53_recordZone + name + typeZ1234_example.com_A

Common Pitfalls

#

1. Config Doesn't Match

#
# After import, plan shows changes
# FIX: Update config to match actual resource state
terraform show  # See what was imported

2. Forgetting to Write Config First

#
# Import without config = error
terraform import aws_instance.web i-123
# Error: resource address "aws_instance.web" does not exist in config

3. Import Doesn't Import Relationships

#

Importing an EC2 instance doesn't import its security groups, subnets, or IAM role. Import each resource separately.

4. Sensitive Values Not Imported

#

Passwords, secrets, and some sensitive attributes may not be imported. You'll need to set them manually:

resource "aws_db_instance" "main" {
  # After import, set the password manually
  password = var.db_password  # Import can't read this from AWS
}

Troubleshooting Checklist

#
  1. ✅ Does the resource config exist before importing?
  2. ✅ Is the import ID format correct for this resource type?
  3. ✅ Does terraform plan show zero changes after import?
  4. ✅ Have you imported all dependent resources?
  5. ✅ Are sensitive values set correctly?

Prevention Tips

#
  • Use import blocks over CLI for new projects — they're reviewable and repeatable
  • Use generate-config-out to avoid writing config from scratch
  • Import in batches — group related resources (VPC + subnets + security groups)
  • Run terraform plan after every import — catch drift immediately
  • Document what was imported — helps team understand which resources are now managed
#

Conclusion

#

Terraform import bridges the gap between existing infrastructure and infrastructure as code. Use import blocks (1.5+) with generate-config-out for the easiest workflow, or the CLI for older versions. Always write config first, import, then iterate on plan until you have a clean state with no drift.

#Terraform#Import#Migration#State Management#Cloud

Share this article