Skip to main content

Command Palette

Search for a command to run...

Day 22: Terraform Import & Moving Resources

Updated
4 min read
Day 22: Terraform Import & Moving Resources
S

I'm a cloud-native enthusiast and tech blogger, sharing insights on Kubernetes, AWS, CI/CD, and Linux across my blog and Facebook page. Passionate about modern infrastructure and microservices, I aim to help others understand and leverage cloud-native technologies for scalable, efficient solutions.

Welcome to Day 22! Today you’ll learn how to bring existing infrastructure under Terraform management and how to refactor your Terraform code without destroying resources.

🎯 Today’s Goals

  • Import existing resources into Terraform

  • Move resources within the state.

  • Refactor configurations safely

  • Handle resource migrations

📥 Terraform Import

Import adds existing infrastructure to Terraform state without recreating it.

Basic Import Syntax

terraform import <resource_address> <resource_id>

Import Process

  1. Write resource block

  2. Import into state

  3. Run plan to verify

  4. Adjust configuration if needed

Example: Import EC2 Instance

# Step 1: Write resource block
resource "aws_instance" "imported" {
  # Required arguments only initially
  ami           = "ami-12345"
  instance_type = "t2.micro"
}
# Step 2: Import
terraform import aws_instance.imported i-1234567890abcdef0
# Step 3: Plan to see differences
terraform plan
# Step 4: Update configuration to match
# Add missing arguments from plan output

Common Resource Imports

# EC2 Instance
terraform import aws_instance.web i-1234567890abcdef0

# VPC
terraform import aws_vpc.main vpc-12345678

# S3 Bucket
terraform import aws_s3_bucket.data my-bucket-name

# Security Group
terraform import aws_security_group.web sg-12345678

# IAM Role
terraform import aws_iam_role.app my-app-role

# RDS Instance
terraform import aws_db_instance.database my-database

🔄 Moving Resources

Within Same State

Use terraform state mv:

# Rename resource
terraform state mv aws_instance.old aws_instance.new

# Move to module
terraform state mv aws_instance.web module.web.aws_instance.server

# Move from module
terraform state mv module.old.aws_instance.web aws_instance.web

# Move with count/index
terraform state mv 'aws_instance.web[0]' 'aws_instance.primary'

Between Different States

# Pull source statet
erraform state pull > source.tfstate

# Pull destination state
terraform state pull > dest.tfstate

🧪 Hands-On Lab

Lab 1: Import Existing Resources

main.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# We'll import this later
resource "aws_s3_bucket" "imported" {
  bucket = "BUCKET_NAME"  # Replace after import

  tags = {
    Imported = "true"
  }
}
# Create bucket manually first
BUCKET_NAME="import-lab-$(date +%s)"
aws s3 mb s3://$BUCKET_NAME

# Add bucket name to terraform
sed -i "s/BUCKET_NAME/$BUCKET_NAME/" main.tf

# Import the bucket
terraform import aws_s3_bucket.imported $BUCKET_NAME

# Verify
terraform plan  

# Update tags
terraform apply

# Clean up
terraform destroy

Lab 2: Move Resources

main.tf:

resource "aws_instance" "old_name" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"

  tags = {
    Name = "test-instance"
  }
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}
# Apply
terraform apply -auto-approve

# Rename in code
mv old_name to new_name in main.tf

# Move in state
terraform state mv aws_instance.old_name aws_instance.new_name

# Verify
terraform plan  

# Should show no changes
# Clean up
terraform destroy

🔧 Import with For_Each

resource "aws_s3_bucket" "imported" {
  for_each = toset([
    "bucket-1",
    "bucket-2",
    "bucket-3"
  ])

  bucket = each.value
}
# Import each
terraform import 'aws_s3_bucket.imported["bucket-1"]' bucket-1
terraform import 'aws_s3_bucket.imported["bucket-2"]' bucket-2
terraform import 'aws_s3_bucket.imported["bucket-3"]' bucket-3

📋 Best Practices

DO:

  1. Always back up the state before importing

     terraform state pull > backup.tfstate
    
  2. Import in stages

    • Import critical resources first

    • Test thoroughly

    • Document what was imported

  3. Use terraform plan to verify

     terraform import <resource> <id>terraform plan  # Should show minimal changes
    
  4. Keep import scripts

     # import.sh
     #!/bin/bash
     terraform import aws_vpc.main vpc-12345
     terraform import aws_subnet.public[0] subnet-12345
     terraform import aws_subnet.public[1] subnet-67890
    

DON’T:

  1. Don’t import without backing up

  2. Don’t modify state files manually

  3. Don’t import into production without testing

  4. Don’t forget to update configuration after import

🔍 Troubleshooting

Import Fails

# Error: resource already in state
terraform state rm aws_instance.web
terraform import aws_instance.web i-12345
# Error: resource not found
# Check resource ID is correct
aws ec2 describe-instances --instance-ids i-12345

Plan Shows Changes After Import

# Get actual resource configuration
terraform show
# Update your .tf file to match

📝 Summary

Today you learned:

  • ✅ Importing existing resources

  • ✅ Moving resources in state

  • ✅ Refactoring without destruction

  • ✅ Import blocks (Terraform 1.5+)

  • ✅ Import best practices

🚀 Tomorrow: Provisioners & Null Resources


← Day 21: Workspaces | Day 23: Provisioners →

More from this blog

S

StackOps - Diary

33 posts

Welcome to the StackOps - Diary. We’re dedicated to empowering the tech community. We delve into cloud-native and microservices technologies, sharing knowledge to build modern, scalable solutions.