Back to all blog posts

A Comprehensive Guide to Importing Existing Infrastructure into Terraform and OpenTofu

Picture of Soren Martius
Sören Martius Chief Product Officer
Photo of Annu Singh
Annu Singh Technical Content Writer
selina nazareth
Selina Nazareth Developer Relations Manager
Reading Time:5 min read

Learn how to seamlessly import existing infrastructure into Terraform and OpenTofu using the terraform import command and the declarative import block. This step-by-step guide covers best practices, real-world examples, and key differences between import methods to help you efficiently manage cloud resources with Infrastructure as Code (IaC).

Importing Existing Infrastructure into Terraform and OpenTofu Cover

When working with Terraform and OpenTofu, you might encounter situations where you must import existing infrastructure into Terraform and OpenTofu. To mention some of the possible scenarios:

  • Infrastructure that has been created via “ClickOps” (a common term for using the Cloud UI), bash scripts, etc., without using IaC in the first place.
  • Infrastructure created with a different IaC tool (e.g., Cloud Formation, Pulumi, or Crossplane).

This tutorial explores available approaches for bringing existing resources under Terraform.

Note: While this article is also valid for OpenTofu, for simplicity, all examples will focus exclusively on Terraform.

Why import existing Infrastructure into Terraform and OpenTofu in the first place?

Bringing existing infrastructure under Terraform has several benefits:

  • Unified Management: Infrastructure as Code becomes your infrastructure's single source of truth.
  • Consistency: Avoid configuration drift and misconfigurations by managing all resources declaratively.
  • Automation: Accelerate your developers’ velocity and improve collaboration and quality by automating commands such as terraform plan and terraform apply and allowing changes to be reviewed before triggering a deployment.
  • Improved Governance: Terraform’s declarative approach allows for better resource tracking, change management, and compliance auditing.
  • Reproducibility: If you provision and manage your infrastructure with Infrastructure as Code, you can deploy the same configurations infinitely to multiple environments.

Bringing resources under Terraform or OpenTofu transforms your cloud infrastructure into a well-organized and automated system, making it easier to scale, manage, and adapt to changes.

Two Approaches: terraform import vs. import Block

Two different approaches exist.

1. Using the terraform import Command

The terraform import (or tofu import ) command is used to import a single resource at a time using a one-off command in a terminal of your choice.

To understand how the  terraform import  command is used, let’s look at its syntax first:

terraform import [options] ADDRESS_ID

This command can only import resources into the state and expects the relevant resource configuration to be in code.

Follow these steps to import your resources to Terraform:

  1. Write the resource block in the config file ( .tf file ) and configure it according to how the existing resource is configured.
  2. Run terraform import , passing the resource address (e.g., aws_s3_bucket.example ) and the unique identifier (e.g., the bucket name or resource ID) as arguments. Terraform queries the cloud provider’s API for the resource’s details.
  3. Terraform stores the live resource’s attributes in the state file and associates them with the resource block in the config file (which was also passed as an argument in the import command ).

Now, you can manage the lifecycle of this cloud resource with Terraform just like other configured resources.

Examples:

Importing an AWS S3 Bucket Suppose you have an S3 bucket named my-existing-bucket. To manage it with Terraform:

resource "aws_s3_bucket" "example" {
  bucket = "my-existing-bucket"
}

Run terraform import aws_s3_bucket.example my-existing-bucket . Terraform maps the S3 bucket’s state to the configuration.

💡 After importing, run terraform plan to verify that no unintended changes will be applied.

Importing an AWS Security Group Suppose you have an existing security group with the ID sg-0123456789abcdef0 that allows SSH access. To manage it with Terraform:

resource "aws_security_group" "ssh_access" {
  name        = "ssh-access-group"
  description = "Allow SSH access"
  vpc_id      = "vpc-0abc12345"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Run terraform import aws_security_group.ssh_access sg-0123456789abcdef0 . Terraform will now manage the security group under this configuration.

Importing a Google Cloud Storage Bucket Suppose you have a GCS bucket named my-gcs-bucket . To import it:

resource "google_storage_bucket" "example" {
  name     = "my-gcs-bucket"
  location = "US"
}

Run terraform import google_storage_bucket.example my-gcs-bucket . This ensures Terraform tracks and manages the bucket.

2. Using the import Block

The import block, introduced in Terraform 1.5, simplifies the process by embedding import statements directly into your configuration using a declarative approach. Unlike the import command, configuration-driven import using the import blocks allows for importing multiple resources at once, it’s predictable, can be reviewed by peers by looking at previews inside of Pull Request and works well with CI/CD pipelines. It eliminates the need for manual terraform import commands.

Examples

Importing an S3 Bucket Here’s how the same S3 bucket can be imported using an import block:

resource "aws_s3_bucket" "example" {
  bucket = "my-existing-bucket"
}

import {
  to = aws_s3_bucket.example
  id = "my-existing-bucket"
}

When you run terraform plan or terraform apply , Terraform automatically imports the resource and ensures the configuration matches its current state.

Import Block vs. Import Command

The choice between import approaches depends on your specific use case. If you need to perform ad-hoc imports of individual resources or manage unmanaged assets through the command line interface (CLI), the terraform import command may be more suitable. However, if you prefer an automated approach with consistent and repeatable imports integrated into your CI/CD workflows, the declarative import block is the better option.

Common Questions About Importing Terraform Resources

Several important questions often arise when importing existing infrastructure into Terraform and OpenTofu. Let's explore the key aspects of this functionality.

Understanding the Impact of Terraform Import

Running terraform import doesn't modify your infrastructure directly - it simply adds the existing infrastructure resource to your Terraform state. However, be cautious: if you subsequently run a apply command without defining the configuration for the imported resource, Terraform will attempt to destroy that resource since it lacks the necessary configuration.

Code Generation and Import Process

It's important to note that terraform import doesn't automatically generate code. This responsibility falls to the engineer managing the infrastructure. While the import command is powerful for managing manually created infrastructure, it does come with certain limitations:

  1. Manual code writing is required after importing resources
  2. Infrastructure drift can occur if the resource configuration doesn't exactly match the manual setup
  3. Earlier Terraform versions required importing resources individually (though this limitation has been addressed with the import block feature)

Comparing Import Options

Data Sources vs. Import

While Terraform data sources allow you to query details about existing resources, they don't provide management capabilities. Importing a resource, on the other hand, gives you full lifecycle control over that resource.

Terraform Import vs. Terraformer

The key distinction lies in scope: terraform import handles single resources that must be explicitly specified, while Terraformer can import multiple resource types simultaneously and generate their corresponding code.

Import Limitations

The import functionality is specifically designed for pre-existing objects - you cannot import resources that don't yet exist in your infrastructure.

Best Practices for Terraform Import

To ensure successful resource imports, consider these essential practices:

First, thoroughly understand your existing infrastructure and how it aligns with your Terraform configuration. Before beginning the import process, create your HashiCorp Configuration Language (HCL) code to accurately reflect your infrastructure resource settings.

Version control is crucial - ensure your state file is managed remotely and has versioning enabled. This practice should extend to your code configuration as well.

Finally, always run a plan after importing resources. This step is vital for verifying that the import was successful and that your code configuration accurately represents the imported resource.

Summary

In this article, you learned how to import existing cloud infrastructure into Terraform and OpenTofu using either the terraform import command or the declarative approach using the import block, which is often preferred.

Terramate is an orchestration, observability and visibility platform for Terraform, OpenTofu and Terragrunt that significantly simplifies importing existing infrastructure by generating the required import statements and importing existing infrastructure into smaller units of execution called stacks. You can test it yourself by creating a free account or scheduling a demo with our solutions engineering team.

Ready to supercharge your IaC?

Explore how Terramate can uplift your IaC projects with a free trial or personalized demo.