How to use Terraform Import
Terraform import brings existing infrastructure under state file management
Introduction to Terraform Import
Terraform is a powerful Infrastructure as Code (IaC) tool, but what happens when you have existing infrastructure that wasn't created with Terraform? That's where terraform import
comes in. This command allows you to bring existing cloud resources under Terraform's management, avoiding the need to recreate them from scratch. Terraform version 1.5 and later introduced the import
block, offering a more streamlined and declarative approach to importing resources.
Why Terraform Import is Used
- Manage existing resources: Integrate infrastructure created outside of Terraform (e.g., manually via the cloud console, using scripts, or other IaC tools) into your Terraform workflow.
- Avoid recreation: Prevent the need to delete and recreate existing resources, which can be disruptive and time-consuming.
- Consistency and control: Manage all your infrastructure through Terraform, ensuring a consistent and controlled environment.
- Migration: Gradually adopt Terraform for existing resources, minimizing disruption.
- Disaster Recovery: If your Terraform state file is lost or corrupted, you can use
terraform import
to rebuild it.
Examples of Using the Command
Here are some basic examples of using the terraform import
command:
- This command imports an existing S3 bucket named
my-existing-bucket
and associates it with the resource blockaws_s3_bucket.example
in your Terraform configuration. You'll need to have a corresponding resource block defined in your.tf
file. - This command imports an EC2 instance with the ID
i-abcd1234
and associates it with the resource blockaws_instance.example
. - Azure resource IDs are typically full paths.
Importing an Azure Virtual Machine
terraform import azurerm_virtual_machine.example /subscriptions/xxx-xxx-xxx-xxx-xxx/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/myVM
Importing an AWS EC2 Instance
terraform import aws_instance.example i-abcd1234
Importing an AWS S3 Bucket
terraform import aws_s3_bucket.example my-existing-bucket
Tips and Tricks
Write the resource block first: Before running the terraform import
command, define the corresponding resource block in your Terraform configuration file.
Identify the resource ID: You'll need the unique ID of the resource you're importing (e.g., the S3 bucket name, EC2 instance ID, or Azure resource path). Refer to the provider documentation for the correct ID format.
Use terraform plan
: After importing, always run terraform plan
to see the differences between the imported resource's actual state and your configuration. Adjust your configuration as needed.
Consider using terraform plan -generate-config-out
: Terraform can generate the configuration for you, which can be a helpful starting point, especially for complex resources (Terraform 1.5+).
Be aware of dependencies: Import resources in the correct order, considering dependencies between them.
Back up your state file: It's always a good practice to back up your Terraform state file before performing import operations.
Use import blocks for CI/CD: The import
block integrates well with CI/CD pipelines.
Remove import blocks: After successfully importing a resource, you can remove the import
block from your configuration.
When Not to Use the Import Command
When you can easily recreate the resource: If the resource is simple to recreate from scratch using Terraform, it might be safer and cleaner to do so.
For core infrastructure with high risk of disruption: Importing fundamental resources like networking (VPCs, subnets) or IAM can be risky, as misconfigurations can lead to widespread outages. Consider creating parallel resources and migrating gradually.
When you are unsure about the existing configuration: If you don't fully understand the configuration of the existing resource, importing it can lead to unexpected changes.
Terraform Import Command vs. The Import Block
Before Terraform 1.5, importing existing infrastructure was exclusively done via the terraform import
command. This method is an imperative operation, meaning you directly instruct Terraform to perform an action at a specific time. You would run the command, provide the resource address and the external resource ID, and Terraform would then add that resource to its state file. The crucial next step was for the user to manually write the corresponding Terraform configuration block (HCL) that matched the imported resource's actual state. This process was often iterative, involving terraform plan
to identify configuration drift after the import.
With Terraform 1.5, HashiCorp introduced the import
block, a declarative alternative integrated directly into the Terraform configuration language (HCL). This block allows you to define which resources should be imported as part of your normal terraform plan
and apply
workflow. Instead of a separate command, you specify an import
block in your .tf
files, referencing the target resource in your configuration (to = <resource_address>
) and providing the id
of the existing resource you want to import. When you run terraform plan
and terraform apply
with an import
block present, Terraform performs the import operation, and importantly, it can also generate a starting configuration for the imported resource (using terraform plan -generate-config-out
), streamlining the post-import cleanup.
The key differences boil down to automation and safety:
- terraform import (command): This is a good choice for one-off, interactive imports where you might be bringing a single resource under management. It's less ideal for large-scale migrations or CI/CD pipelines because it requires manual post-import configuration writing and is an external, imperative step.
- import block: This is the recommended approach for most modern use cases, especially for automating imports, large-scale migrations, and integrating imports into CI/CD pipelines. Its declarative nature allows the import process to be part of the regular plan/apply cycle, providing better visibility and control, including the ability to import multiple resources simultaneously using
for_each
, and the helpful configuration generation feature. While the command still exists and has its place for quick fixes, theimport
block offers a more robust and integrated solution for managing existing infrastructure.
Import Block Examples
This example uses the for_each
argument within an import
block to import multiple S3 buckets. This shows how to import resources into specific module instances.
Importing Resources Across Module Instances
import {
for_each = { for b in local.buckets : "${b.group}.${b.key}" => b }
id = each.value.id
to = module.group.aws_s3_bucket.this
}
Importing Multiple Resources with for_each
locals {
buckets = {
"staging" = "staging-bucket"
"uat" = "uat-bucket"
"prod" = "production-bucket"
}
}
import {
for_each = local.buckets
to = aws_s3_bucket.app_data[each.key]
id = each.value
}
resource "aws_s3_bucket" "app_data" {
for_each = local.buckets
# ... bucket configuration ...
}
Using Terraform Import in a Remote Backend
If Terraform automation and collaboration software, such as Scalr or Terraform Cloud, are used, there is one extra step to consider before doing an import. Since the remote backend is managing the state, the backend must be specified in the code to ensure that the resources are added to the correct state file. The backend code looks like:
terraform {
backend "remote" {
hostname = "my-account.scalr.io"
organization = "<ID of environment>"
workspaces {
name = "<workspace-name>"
}
}
}
Once the code is added and the import command is executed, the imported resources will be added to the state in the backend.
Summary
Terraform import
is a vital command for bringing existing, unmanaged infrastructure under Terraform's Infrastructure as Code (IaC) control. It prevents the disruptive recreation of resources, enabling seamless migration, consistent management, and enhanced control over your cloud environment. While the traditional terraform import
command is an imperative, one-off operation requiring manual HCL configuration, Terraform 1.5 introduced the import
block. This declarative alternative integrates imports directly into your HCL, allowing for automated, multi-resource imports, and even configuration generation, making it ideal for CI/CD pipelines and large-scale adoption. When importing, it's crucial to write the resource block first, obtain the correct resource ID, and always run terraform plan
afterward to reconcile differences. A strict import order (parent before child) is necessary for resources with dependencies. The import
process updates your Terraform state directly, including when using remote backends like Scalr or Terraform Cloud, ensuring centralized and synchronized state management. While powerful, terraform import
should be used judiciously, avoiding unnecessary imports for easily recreatable resources or high-risk core infrastructure.