Import Resources into Terraform
Terraform import brings existing infrastructure under state file management. Learn what import options you have.
Why Import Resources into Terraform
Importing resources into Terraform is the process of bringing existing, manually provisioned infrastructure under Terraform's management. This is a crucial step when you have infrastructure that was created outside of your Terraform configuration, such as resources provisioned directly through a cloud provider's console. The import operation doesn't create the resource; instead, it matches an existing cloud resource to a corresponding resource
block in your Terraform code and records its state in your Terraform state file. Once imported, that resource is fully managed by Terraform, allowing you to use terraform plan
and terraform apply
to track and manage its configuration, ensuring your state file accurately reflects your live infrastructure.
There are two ways to import resources: The Terraform import block and the terraform import
command.
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 ...
}
Terraform Import Command Basics
The import
command allows you to link a remote, pre-existing resource to a corresponding resource block in your Terraform configuration and record it in the Terraform state file. This doesn't modify the actual infrastructure; it merely updates Terraform's state so that future terraform plan
and terraform apply
operations can manage that resource alongside others defined in your code. The basic syntax involves specifying the Terraform resource address (e.g., aws_instance.example
) and the unique ID of the existing remote resource (e.g., i-1234567899abcdef0
for an AWS EC2 instance).
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:
Importing an AWS EC2 Instance
This command imports an EC2 instance with the ID i-abcd1234
and associates it with the resource block aws_instance.example
.
terraform import aws_instance.example i-abcd1234
Importing an AWS S3 Bucket
This command imports an existing S3 bucket named my-existing-bucket
and associates it with the resource block aws_s3_bucket.example
in your Terraform configuration. You'll need to have a corresponding resource block defined in your .tf
file.
terraform import aws_s3_bucket.example my-existing-bucket
Importing an Azure Virtual Machine
Azure resource IDs are typically full paths.
terraform import azurerm_virtual_machine.example /subscriptions/xxx-xxx-xxx-xxx-xxx/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/myVM
Terraform Import Flags
Here are the most common and useful flags you can use with the terraform import
command:
-config=path
: Specifies the path to the directory containing your Terraform configuration files. By default, Terraform looks in the current working directory.-input=true/false
: Determines whether Terraform should ask for interactive input for provider configuration or variable values. Setting it tofalse
is useful for automation and scripting.-lock=false
: Disables holding a state lock during the operation. Caution: This is generally not recommended in a collaborative environment as it can lead to state corruption if other commands are run concurrently.-lock-timeout=0s
: Sets a duration to retry acquiring a state lock before failing.-no-color
: Disables colorized output in the console.-parallelism=n
: Limits the number of concurrent operations Terraform performs when walking the graph. The default is typically 10.-provider=provider
(Deprecated): Overrides the provider configuration to use when importing an object. It's generally best practice to let Terraform use the provider specified in the configuration for the target resource.-var 'foo=bar'
: Sets a variable in the Terraform configuration directly from the command line. This flag can be used multiple times for different variables. Variable values are interpreted as literal Terraform expressions.
When using flags, they typically need to appear before the positional arguments (ADDRESS and ID) in the command. For example: terraform import -var 'env=prod' aws_instance.my_instance i-1234567890abcdef0
.
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.
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.
Verifying the Import in State
After executing the terraform import
command, it's important to verify that the resource has been successfully brought under Terraform's management. The primary way to confirm this is by running terraform plan
. A successful import will result in a plan that shows "No changes. Your infrastructure matches the configuration." or, if you've imported a resource and then immediately added a new attribute or dependency in your HCL, it might show only the planned changes for those new additions, but not a plan to create or destroy the imported resource itself. If terraform plan
shows that the imported resource would be created or destroyed, it indicates the import failed or that the resource block in your configuration does not perfectly match the imported resource.
Terraform Show
Additionally, you can inspect the state file directly or use terraform show
to see if the imported resource now appears within Terraform's recorded state.
terraform show
aws_instance.example:
resource "aws_instance" "example" {
ami = "ami-09bd3f9be7c1b8217"
arn = "arn:aws:ec2:us-east-1:123456:instance/i-0354906e43123456"
associate_public_ip_address = true
availability_zone = "us-east-1d"
cpu_core_count = 1
...
..
.
Terraform State List
Running terraform state list
is another excellent way to verify that an import was successful. After running terraform import
, if the operation was successful, the imported resource's address will appear in the output when you execute terraform state list
. This command directly queries the current state file and lists all resources that Terraform is currently managing, providing a quick and clear confirmation that your new resource has been added to the state.
terraform state list
aws_instance.example
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.