The [.code]terraform import[.code] command and import block enable you to bring existing cloud resources into the Terraform state, allowing them to be managed within the IaC workflow.
In this blog, we’ll explore how and when to use the [.code]terraform import[.code] command and Terraform import block and provide some practical examples for both. We’ll also talk about the challenges and best practices of using Terraform import and much more.
Disclaimer
All use cases of [.code]terraform import[.code] command and Terraform import block discussed here work similarly in OpenTofu, the open-source Terraform alternative. However, to keep it simple and familiar for DevOps engineers, we will refer to these as Terraform [.code]import[.code] throughout this blog post.
What is Terraform Import Command?
The [.code]terraform import[.code] command enables you import your existing resources from the console, bash scripts, or other tools (e.g., Pulumi or CloudFormation) under your Terraform state.
Using the [.code]terraform import[.code] command helps you avoid drifts and inconsistencies in your infrastructure, allowing you to manage your cloud resources through Terraform without recreating them from scratch.
Import Command Syntax
To understand how [.code]terraform import[.code] command is used, let’s take a look at its syntax:
terraform import [options] ADDRESS ID
- ADDRESS: The resource path in your Terraform configuration.
- ID: The unique identifier of the resource in your cloud provider.
To demonstrate how [.code]terraform import[.code] command is executed, you must follow these steps:
- Write the resource block in your Terraform configuration, using [.code]resource "aws_s3_bucket" "bucket_1" {}[.code]
- Import requires resource address and ID. Identify the S3 bucket ID using the cloud provider’s UI or CLI. In this scenario, it is 's3-env0'.
- Run the [.code]terraform import[.code] command with the resource address and resource ID: [.code]terraform import aws_s3_bucket.bucket_1 s3-env0[.code].
- Run the [.code]terraform apply[.code] command to apply the configuration changes.
Use Cases for Terraform Import
- Bringing unmanaged infrastructure under IaC: Organizations might have legacy assets that were manually deployed on cloud providers such as AWS, GCP, and Azure. Using the [.code]terraform import[.code] command, teams can migrate these resources and manage them using Terraform code.
- Breaking down monoliths: Using the [.code]terraform import[.code] command can help split massive Terraform state files into much simpler and easy-to-manage state files.
- Disaster recovery: In case of a state file crashing, getting corrupted, or being wiped clean, [.code]terraform import[.code] could be used to rebuild your infrastructure.
- Reclaiming drifted resources: Manual changes could cause infrastructure drifts. Just like with legacy and idle resources, you can get rid of this drift by using the [.code]terraform import[.code] command.
Terraform Import Block
Version 1.5 of Terraform introduced the import block, which you can use to import an existing resource in your Terraform configuration. This opens the door for a more scalable and structured approach, enabling you to import resources when running [.code]terraform apply[.code].
Import Block Syntax
Let’s look at the syntax for the import block:
import {
id = “resource_id”
to = “resource_address”
}
Using the import block requires you to go through the following steps:
Step 1: Write the resource block in your Terraform configuration.
resource "aws_instance" "env0" {
ami = ""
instance_type = ""
}
Step 2: Identify the resource ID using the cloud provider’s UI or CLI. In this scenario, it is 'i-0ecd5e8ed288048d9'.
Step 3: Write the import block in your Terraform code with the resource ID and the resource address.
import {
id = "i-0ecd5e8ed288048d9"
to = aws_instance.env0
}
Step 4: Run the [.code]terraform apply[.code] command to import the resource and apply the configuration changes.
Import Block vs. Import Command
Deciding which approach to use to import the resource depends on your use case.
In case you want to run more ad-hoc imports using your CLI for individual resources or handle unmanaged assets, the [.code]terraform import[.code] command might be more appropriate.
However, if you prefer a more automated approach with consistent and repeatable imports running in your CI/CD workflows, the declarative import block is the way to go.
Example Scenarios
Let’s take a look at some examples of how to import resources using the [.code]terraform import[.code] command and the Terraform import block.
Import EC2 Instance using Import Block
Let’s assume that there are some EC2 instances in your AWS account that were added by another team to implement a security feature. Now, your team wants to manage these instances using Terraform.
To do so, first, we will write the resource and import blocks for the 'aws_instance.env0' in your main.tf file, like so:
provider "aws" {
region = "eu-central-1"
}
resource "aws_instance" "env0" {
ami = ""
instance_type = ""
}
import {
id = "i-0ecd5e8ed288048d9"
to = aws_instance.env0
}
Next, we will run [.code]terraform init[.code] to install plugins, and apply to import ‘aws_instance’ named ‘env0’ into the Terraform state.
(base) ➜ env0 git:(main) ✗ terraform init
…
Terraform has been successfully initialized!
(base) ➜ env0 git:(main) ✗ terraform apply
aws_instance.env0: Preparing import... [id=i-0ecd5e8ed288048d9]
…
-/+ destroy and then create replacement
…
-/+ resource "aws_instance" "env0" {
…
~ tags = {
- "Name" = "env0" -> null
}
~ tags_all = {
- "Name" = "env0" -> null
}
+ user_data_replace_on_change = false
...
Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.
…
Apply complete! Resources: 1 imported, 0 added, 1 changed, 0 destroyed.
Note that in this example, the ‘env0’ instance is part of the Terraform state file, terraform.tfstate, and the attributes from the AWS cloud provider are being updated since the resource block is not configured properly with any values in the main.tf file.
Hence, [.code]tags[.code], [.code]tags_all[.code], [.code]user_data_replace_on_change[.code] arguments are updated for the 'aws_instance'.
Improve Config to Avoid Replacement
Now, still using the above example, we saw that the attributes for the existing ‘aws_instance’ resource were updated when imported.
You can avoid replacing the attributes for the 'aws_instance' by providing their configuration values in the resource definition of the Terraform code before performing the import action.
In this case, the resource block will change, like so:
resource "aws_instance" "env0" {
ami = ""
instance_type = ""
tags = {
"Name" = "env0"
}
}
Now, run the [.code]terraform apply[.code] command to import the ‘aws_instance’ without any configuration change.
(base) ➜ env0 git:(main) ✗ terraform apply
aws_instance.env0: Preparing import... [id=i-0ecd5e8ed288048d9]
…
~ update in-place
…
# (imported from "i-0ecd5e8ed288048d9")
~ resource "aws_instance" "env0" {
ami = "ami-0ba9883b710b05ac6"
...
aws_instance.env0: Import complete [id=i-06c33d3307252ce19]
Apply complete! Resources: 1 imported, 0 added, 0 changed, 0 destroyed.
Review and Observe the State File
Still using the example above, let’s see how we can compare and verify that ‘aws_instance’ is imported using the [.code]terraform state show aws_instance.env0[.code].
(base) ➜ env0 git:(main) ✗ terraform state show aws_instance.env0
# aws_instance.env0:
resource "aws_instance" "env0" {
ami = "ami-0ba9883b710b05ac6"
associate_public_ip_address = true
availability_zone = "us-east-1e"
cpu_core_count = 1
id = "i-0ecd5e8ed288048d9"
…
The output displays that the EC2 with the id as ‘i-0ecd5e8ed288048d9’ is now managed by your Terraform state file.
Import IAM Roles using Terraform Import Command
In the previous examples we’ve imported EC2 instances. Now, let’s take a look at a scenario where you’ve created a bunch of roles to test access permissions using the AWS console.
If you want to manage these roles using your Terraform code, you can import them into your Terraform state using the bash script.
Define the Terraform configuration for these two roles, ‘env0-1’ and ‘env0-2’, with an assume-role policy. Ensure that the policy matches the roles in your AWS account, like so:
provider "aws" {
region = "eu-west-1"
}
locals {
roles = ["env0-1", "env0-2"]
}
resource "aws_iam_role" "import_roles" {
for_each = toset(local.roles)
name = each.value
assume_role_policy = jsonencode(
…
}
Ensure that neither ‘env0-1’ and ‘env0-2’ roles are not in your state files by running the [.code]terraform plan[.code] command:
(base) ➜ env0 git:(main) ✗ terraform plan
Terraform used the selected providers to generate the following execution plan.
+ create
Terraform will perform the following actions:
# aws_iam_role.import_roles["env0-1"] will be created
+ resource "aws_iam_role" "import_roles" {
...
# aws_iam_role.import_roles["env0-2"] will be created
+ resource "aws_iam_role" "import_roles" {
...
Plan: 2 to add, 0 to change, 0 to destroy.
Now, let's import these two roles in Terraform configuration by running the [.code]terraform import[.code] command inside the bash script, like so:
terraform import "aws_iam_role.import_roles[\"env0-1\"]" env0-1
aws_iam_role.import_roles["env0-1"]: Importing from ID "env0-1"...
…
Import successful!
…
terraform import "aws_iam_role.import_roles[\"env0-2\"]" env0-2
aws_iam_role.import_roles["env0-2"]: Importing from ID "env0-2"...
…
Import successful!
Import Modules using Terraform Import Command
Suppose you have deployed resources using Terraform remote modules and moved your state file somewhere else; your Terraform will become unaware of the existing resources.
Here, you can import these modules similar to the resources with the difference in the address reference as you use the prefix ‘module.module_instance_name’.
Let’s make it clearer by using the AWS VPC module ‘terraform-aws-modules/vpc/aws’ in our main.tf file.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.12.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-1a", "us-west-1b", "us-west-1c"]
…
tags = {
Terraform = "true"
Environment = "production"
}
}
When you run the [.code]terraform plan[.code], it shows that this module will create 20 resources.
(base) ➜ env0 git:(main) ✗ terraform plan
aws_s3_bucket.bucket_1: Refreshing state... [id=s3-env0]
aws_instance.env0: Refreshing state... [id=i-06c33d3307252ce19]
...
+ create
Terraform will perform the following actions:
# aws_iam_user.cross_account_user will be created
+ resource "aws_iam_user" "cross_account_user" {
...
}
Plan: 20 to add, 0 to change, 0 to destroy.
Now, import the existing AWS VPC under the Terraform management by running the [.code]terraform import[.code] command with the VPC ID and target a resource named ‘aws_vpc.this’ within the module:
(base) ➜ test git:(main) ✗ terraform import module.vpc.aws_vpc.this vpc-075501dcf072e7092
…
Import successful!
Note that since Terraform modules are used to deploy a bundle of resources, it is often challenging to import them, as you have to be prepared to look through a large codebase to get the correct resource IDs.
Challenges of Using Terraform Import
Using [.code]terraform import[.code] helps you save time and achieve infrastructure uniformity. However, using the [.code]terraform import[.code] can be challenging in some scenarios, such as the following:
1. Manage Configuration after Import
When you import a resource, Terraform only updates the state file. You need to write the Terraform configuration code to manage that resource yourself.
For example, you must write the Terraform configuration after importing an AWS EC2 instance. This can be time-consuming and might lead to errors if not done properly.
2. Handle Infrastructure Drift
Infrastructure drift occurs when your actual infrastructure differs from what is defined in your desired Terraform configuration.
For example, if you import an S3 bucket without setting all its policies in the .tf file, Terraform may attempt to apply those missing policies, misconfiguring the S3 bucket configuration.
3. Exact Resource IDs Required
The correct resource ID is required to import the resources successfully, which is provider-specific; otherwise, it will fail. You should therefore use CLI or console to check and carefully provide the proper identifiers, which will prove counterproductive.
Best Practices for Using Terraform Import
To deal with the above challenges, here are some best practices that will help avoid issues:
- Define the resource block in your.tf file, with the required configuration, before importing.
- Ensure you either have a backup of your state files or manage it remotely, with the versioning enabled, to easily roll back in case of any accidental state corruption or loss.
- Always run the [.code]terraform plan[.code] command before you apply the changes.
- To avoid issues with dependencies among imported resources, you should plan ahead and - if those dependencies exist - consider importing one resource at a time.
Terraform Import and env0
Organizations struggle with forgotten cloud assets or manually managed resources that carry multiple risks, such as reliability, compliance issues, and security threats.
Meanwhile, manually tracking and closing gaps in IaC coverage is labor-intensive. However, you can solve these problems using env0’s Cloud Compass, which leverages GenAI to create custom import blocks for each asset you want to roll into your IaC. This will help you save time and ensure that resources adhere to current security and compliance policies.
Getting Started
Go to env0, click on the ‘Cloud Compass’ item in the env0 sidebar, and add your cloud account details.
Once you have added a cloud account, it will do an initial scan, and within just a few minutes, you’ll see the dashboard start to populate with data, which will continue to stream in from subsequent future scans.
On the right side of the dashboard, you will see that the Cloud Resources Management categorizes the resources in your cloud account into three categories.
The graph on the left will show the number of resources deployed to cloud infrastructure using IaC over time.
Below, the a detailed table will display additional information for every resource, including a'Severity' score, auto-calculated by env0 unique logic to indicate the risk associated with each resources.
Resource Importing
Follow these steps to use env0’s Cloud Compass feature:
First, create a Terraform configuration file for your existing AWS S3 bucket to import it into your Terraform state.
resource "aws_s3_bucket" "bucket_1" {
bucket = "s3-env0-1"
}
Now, when you click on ‘Generate IaC Code’ in Cloud Compass, you can choose the IaC framework. Here, we’ll use Terraform, which will auto-generate the Terraform import block for the existing S3 bucket.
Copy the generated Terraform code to import the S3 bucket in your Terraform state.
import {
to = aws_s3_bucket.bucket_1
id = "s3-env0-1"
}
Run the [.code]terraform apply[.code] to import the S3 bucket, which will update the Terraform state file.
(base) ➜ test git:(main) ✗ terraform apply
aws_s3_bucket.bucket_1: Preparing import... [id=s3-env0-1]
aws_s3_bucket.bucket_1: Refreshing state... [id=s3-env0-1]
…
# aws_s3_bucket.bucket_1 will be imported
resource "aws_s3_bucket" "bucket_1" {
…
}
Plan: 1 to import, 1 to add, 0 to change, 0 to destroy.
…
aws_s3_bucket.bucket_1: Importing... [id=s3-env0-1]
aws_s3_bucket.bucket_1: Import complete [id=s3-env0-1]
Apply complete! Resources: 1 imported, 1 added, 0 changed, 0 destroyed.
Using the Cloud Compass helps you identify the severity of risks and ensure that your infrastructure is secure and compliant with access settings, policies, and other guardrails you’ve defined within the env0 platform, for all imported resources.
Wrapping up
By now, you know how to import resources in your Terraform configuration using [.code]terraform import[.code] command and import block for managing existing cloud resources and which approach to use when.
We have looked at how the [.code]terraform import[.code] command can be used for infrastructure management using best practices. When performing the import action, you might face challenges such as using exact resource IDs, which helps ensure you have an accurate state file.
You can leverage the Cloud Compass feature in env0, which uses AI to auto-generate custom import blocks helping you save time and simplify your infrastructure management by keeping it secure, consistent, and compliant.
Frequently Asked Questions
Q. How do I import into a Terraform module?
To put import commands into a Terraform module, specify the module path in the import command.
For example:
terraform import module.module_name.resource_type.resource_name resource_id
Q. What do you need to do prior to running Terraform import?
Be sure to do the following before you run the [.code]terraform import[.code] command:
- Define the resource block for what you want to import in your Terraform configuration file.
- Identify the exact resource ID you will use.
- Initialize the working directory with [.code]terraform init[.code] command.
Q. Does Terraform import generate code?
The [.code]terraform import[.code] command does not generate code on its own, but updates the state file to include the existing resource.
Q. What is the difference between Terraform Import vs. Terraform State mv?
The commands are sometimes confused, but they perform two different functions. Let's quickly compare the two:
The [.code]terraform import[.code] command and import block enable you to bring existing cloud resources into the Terraform state, allowing them to be managed within the IaC workflow.
In this blog, we’ll explore how and when to use the [.code]terraform import[.code] command and Terraform import block and provide some practical examples for both. We’ll also talk about the challenges and best practices of using Terraform import and much more.
Disclaimer
All use cases of [.code]terraform import[.code] command and Terraform import block discussed here work similarly in OpenTofu, the open-source Terraform alternative. However, to keep it simple and familiar for DevOps engineers, we will refer to these as Terraform [.code]import[.code] throughout this blog post.
What is Terraform Import Command?
The [.code]terraform import[.code] command enables you import your existing resources from the console, bash scripts, or other tools (e.g., Pulumi or CloudFormation) under your Terraform state.
Using the [.code]terraform import[.code] command helps you avoid drifts and inconsistencies in your infrastructure, allowing you to manage your cloud resources through Terraform without recreating them from scratch.
Import Command Syntax
To understand how [.code]terraform import[.code] command is used, let’s take a look at its syntax:
terraform import [options] ADDRESS ID
- ADDRESS: The resource path in your Terraform configuration.
- ID: The unique identifier of the resource in your cloud provider.
To demonstrate how [.code]terraform import[.code] command is executed, you must follow these steps:
- Write the resource block in your Terraform configuration, using [.code]resource "aws_s3_bucket" "bucket_1" {}[.code]
- Import requires resource address and ID. Identify the S3 bucket ID using the cloud provider’s UI or CLI. In this scenario, it is 's3-env0'.
- Run the [.code]terraform import[.code] command with the resource address and resource ID: [.code]terraform import aws_s3_bucket.bucket_1 s3-env0[.code].
- Run the [.code]terraform apply[.code] command to apply the configuration changes.
Use Cases for Terraform Import
- Bringing unmanaged infrastructure under IaC: Organizations might have legacy assets that were manually deployed on cloud providers such as AWS, GCP, and Azure. Using the [.code]terraform import[.code] command, teams can migrate these resources and manage them using Terraform code.
- Breaking down monoliths: Using the [.code]terraform import[.code] command can help split massive Terraform state files into much simpler and easy-to-manage state files.
- Disaster recovery: In case of a state file crashing, getting corrupted, or being wiped clean, [.code]terraform import[.code] could be used to rebuild your infrastructure.
- Reclaiming drifted resources: Manual changes could cause infrastructure drifts. Just like with legacy and idle resources, you can get rid of this drift by using the [.code]terraform import[.code] command.
Terraform Import Block
Version 1.5 of Terraform introduced the import block, which you can use to import an existing resource in your Terraform configuration. This opens the door for a more scalable and structured approach, enabling you to import resources when running [.code]terraform apply[.code].
Import Block Syntax
Let’s look at the syntax for the import block:
import {
id = “resource_id”
to = “resource_address”
}
Using the import block requires you to go through the following steps:
Step 1: Write the resource block in your Terraform configuration.
resource "aws_instance" "env0" {
ami = ""
instance_type = ""
}
Step 2: Identify the resource ID using the cloud provider’s UI or CLI. In this scenario, it is 'i-0ecd5e8ed288048d9'.
Step 3: Write the import block in your Terraform code with the resource ID and the resource address.
import {
id = "i-0ecd5e8ed288048d9"
to = aws_instance.env0
}
Step 4: Run the [.code]terraform apply[.code] command to import the resource and apply the configuration changes.
Import Block vs. Import Command
Deciding which approach to use to import the resource depends on your use case.
In case you want to run more ad-hoc imports using your CLI for individual resources or handle unmanaged assets, the [.code]terraform import[.code] command might be more appropriate.
However, if you prefer a more automated approach with consistent and repeatable imports running in your CI/CD workflows, the declarative import block is the way to go.
Example Scenarios
Let’s take a look at some examples of how to import resources using the [.code]terraform import[.code] command and the Terraform import block.
Import EC2 Instance using Import Block
Let’s assume that there are some EC2 instances in your AWS account that were added by another team to implement a security feature. Now, your team wants to manage these instances using Terraform.
To do so, first, we will write the resource and import blocks for the 'aws_instance.env0' in your main.tf file, like so:
provider "aws" {
region = "eu-central-1"
}
resource "aws_instance" "env0" {
ami = ""
instance_type = ""
}
import {
id = "i-0ecd5e8ed288048d9"
to = aws_instance.env0
}
Next, we will run [.code]terraform init[.code] to install plugins, and apply to import ‘aws_instance’ named ‘env0’ into the Terraform state.
(base) ➜ env0 git:(main) ✗ terraform init
…
Terraform has been successfully initialized!
(base) ➜ env0 git:(main) ✗ terraform apply
aws_instance.env0: Preparing import... [id=i-0ecd5e8ed288048d9]
…
-/+ destroy and then create replacement
…
-/+ resource "aws_instance" "env0" {
…
~ tags = {
- "Name" = "env0" -> null
}
~ tags_all = {
- "Name" = "env0" -> null
}
+ user_data_replace_on_change = false
...
Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.
…
Apply complete! Resources: 1 imported, 0 added, 1 changed, 0 destroyed.
Note that in this example, the ‘env0’ instance is part of the Terraform state file, terraform.tfstate, and the attributes from the AWS cloud provider are being updated since the resource block is not configured properly with any values in the main.tf file.
Hence, [.code]tags[.code], [.code]tags_all[.code], [.code]user_data_replace_on_change[.code] arguments are updated for the 'aws_instance'.
Improve Config to Avoid Replacement
Now, still using the above example, we saw that the attributes for the existing ‘aws_instance’ resource were updated when imported.
You can avoid replacing the attributes for the 'aws_instance' by providing their configuration values in the resource definition of the Terraform code before performing the import action.
In this case, the resource block will change, like so:
resource "aws_instance" "env0" {
ami = ""
instance_type = ""
tags = {
"Name" = "env0"
}
}
Now, run the [.code]terraform apply[.code] command to import the ‘aws_instance’ without any configuration change.
(base) ➜ env0 git:(main) ✗ terraform apply
aws_instance.env0: Preparing import... [id=i-0ecd5e8ed288048d9]
…
~ update in-place
…
# (imported from "i-0ecd5e8ed288048d9")
~ resource "aws_instance" "env0" {
ami = "ami-0ba9883b710b05ac6"
...
aws_instance.env0: Import complete [id=i-06c33d3307252ce19]
Apply complete! Resources: 1 imported, 0 added, 0 changed, 0 destroyed.
Review and Observe the State File
Still using the example above, let’s see how we can compare and verify that ‘aws_instance’ is imported using the [.code]terraform state show aws_instance.env0[.code].
(base) ➜ env0 git:(main) ✗ terraform state show aws_instance.env0
# aws_instance.env0:
resource "aws_instance" "env0" {
ami = "ami-0ba9883b710b05ac6"
associate_public_ip_address = true
availability_zone = "us-east-1e"
cpu_core_count = 1
id = "i-0ecd5e8ed288048d9"
…
The output displays that the EC2 with the id as ‘i-0ecd5e8ed288048d9’ is now managed by your Terraform state file.
Import IAM Roles using Terraform Import Command
In the previous examples we’ve imported EC2 instances. Now, let’s take a look at a scenario where you’ve created a bunch of roles to test access permissions using the AWS console.
If you want to manage these roles using your Terraform code, you can import them into your Terraform state using the bash script.
Define the Terraform configuration for these two roles, ‘env0-1’ and ‘env0-2’, with an assume-role policy. Ensure that the policy matches the roles in your AWS account, like so:
provider "aws" {
region = "eu-west-1"
}
locals {
roles = ["env0-1", "env0-2"]
}
resource "aws_iam_role" "import_roles" {
for_each = toset(local.roles)
name = each.value
assume_role_policy = jsonencode(
…
}
Ensure that neither ‘env0-1’ and ‘env0-2’ roles are not in your state files by running the [.code]terraform plan[.code] command:
(base) ➜ env0 git:(main) ✗ terraform plan
Terraform used the selected providers to generate the following execution plan.
+ create
Terraform will perform the following actions:
# aws_iam_role.import_roles["env0-1"] will be created
+ resource "aws_iam_role" "import_roles" {
...
# aws_iam_role.import_roles["env0-2"] will be created
+ resource "aws_iam_role" "import_roles" {
...
Plan: 2 to add, 0 to change, 0 to destroy.
Now, let's import these two roles in Terraform configuration by running the [.code]terraform import[.code] command inside the bash script, like so:
terraform import "aws_iam_role.import_roles[\"env0-1\"]" env0-1
aws_iam_role.import_roles["env0-1"]: Importing from ID "env0-1"...
…
Import successful!
…
terraform import "aws_iam_role.import_roles[\"env0-2\"]" env0-2
aws_iam_role.import_roles["env0-2"]: Importing from ID "env0-2"...
…
Import successful!
Import Modules using Terraform Import Command
Suppose you have deployed resources using Terraform remote modules and moved your state file somewhere else; your Terraform will become unaware of the existing resources.
Here, you can import these modules similar to the resources with the difference in the address reference as you use the prefix ‘module.module_instance_name’.
Let’s make it clearer by using the AWS VPC module ‘terraform-aws-modules/vpc/aws’ in our main.tf file.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.12.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-1a", "us-west-1b", "us-west-1c"]
…
tags = {
Terraform = "true"
Environment = "production"
}
}
When you run the [.code]terraform plan[.code], it shows that this module will create 20 resources.
(base) ➜ env0 git:(main) ✗ terraform plan
aws_s3_bucket.bucket_1: Refreshing state... [id=s3-env0]
aws_instance.env0: Refreshing state... [id=i-06c33d3307252ce19]
...
+ create
Terraform will perform the following actions:
# aws_iam_user.cross_account_user will be created
+ resource "aws_iam_user" "cross_account_user" {
...
}
Plan: 20 to add, 0 to change, 0 to destroy.
Now, import the existing AWS VPC under the Terraform management by running the [.code]terraform import[.code] command with the VPC ID and target a resource named ‘aws_vpc.this’ within the module:
(base) ➜ test git:(main) ✗ terraform import module.vpc.aws_vpc.this vpc-075501dcf072e7092
…
Import successful!
Note that since Terraform modules are used to deploy a bundle of resources, it is often challenging to import them, as you have to be prepared to look through a large codebase to get the correct resource IDs.
Challenges of Using Terraform Import
Using [.code]terraform import[.code] helps you save time and achieve infrastructure uniformity. However, using the [.code]terraform import[.code] can be challenging in some scenarios, such as the following:
1. Manage Configuration after Import
When you import a resource, Terraform only updates the state file. You need to write the Terraform configuration code to manage that resource yourself.
For example, you must write the Terraform configuration after importing an AWS EC2 instance. This can be time-consuming and might lead to errors if not done properly.
2. Handle Infrastructure Drift
Infrastructure drift occurs when your actual infrastructure differs from what is defined in your desired Terraform configuration.
For example, if you import an S3 bucket without setting all its policies in the .tf file, Terraform may attempt to apply those missing policies, misconfiguring the S3 bucket configuration.
3. Exact Resource IDs Required
The correct resource ID is required to import the resources successfully, which is provider-specific; otherwise, it will fail. You should therefore use CLI or console to check and carefully provide the proper identifiers, which will prove counterproductive.
Best Practices for Using Terraform Import
To deal with the above challenges, here are some best practices that will help avoid issues:
- Define the resource block in your.tf file, with the required configuration, before importing.
- Ensure you either have a backup of your state files or manage it remotely, with the versioning enabled, to easily roll back in case of any accidental state corruption or loss.
- Always run the [.code]terraform plan[.code] command before you apply the changes.
- To avoid issues with dependencies among imported resources, you should plan ahead and - if those dependencies exist - consider importing one resource at a time.
Terraform Import and env0
Organizations struggle with forgotten cloud assets or manually managed resources that carry multiple risks, such as reliability, compliance issues, and security threats.
Meanwhile, manually tracking and closing gaps in IaC coverage is labor-intensive. However, you can solve these problems using env0’s Cloud Compass, which leverages GenAI to create custom import blocks for each asset you want to roll into your IaC. This will help you save time and ensure that resources adhere to current security and compliance policies.
Getting Started
Go to env0, click on the ‘Cloud Compass’ item in the env0 sidebar, and add your cloud account details.
Once you have added a cloud account, it will do an initial scan, and within just a few minutes, you’ll see the dashboard start to populate with data, which will continue to stream in from subsequent future scans.
On the right side of the dashboard, you will see that the Cloud Resources Management categorizes the resources in your cloud account into three categories.
The graph on the left will show the number of resources deployed to cloud infrastructure using IaC over time.
Below, the a detailed table will display additional information for every resource, including a'Severity' score, auto-calculated by env0 unique logic to indicate the risk associated with each resources.
Resource Importing
Follow these steps to use env0’s Cloud Compass feature:
First, create a Terraform configuration file for your existing AWS S3 bucket to import it into your Terraform state.
resource "aws_s3_bucket" "bucket_1" {
bucket = "s3-env0-1"
}
Now, when you click on ‘Generate IaC Code’ in Cloud Compass, you can choose the IaC framework. Here, we’ll use Terraform, which will auto-generate the Terraform import block for the existing S3 bucket.
Copy the generated Terraform code to import the S3 bucket in your Terraform state.
import {
to = aws_s3_bucket.bucket_1
id = "s3-env0-1"
}
Run the [.code]terraform apply[.code] to import the S3 bucket, which will update the Terraform state file.
(base) ➜ test git:(main) ✗ terraform apply
aws_s3_bucket.bucket_1: Preparing import... [id=s3-env0-1]
aws_s3_bucket.bucket_1: Refreshing state... [id=s3-env0-1]
…
# aws_s3_bucket.bucket_1 will be imported
resource "aws_s3_bucket" "bucket_1" {
…
}
Plan: 1 to import, 1 to add, 0 to change, 0 to destroy.
…
aws_s3_bucket.bucket_1: Importing... [id=s3-env0-1]
aws_s3_bucket.bucket_1: Import complete [id=s3-env0-1]
Apply complete! Resources: 1 imported, 1 added, 0 changed, 0 destroyed.
Using the Cloud Compass helps you identify the severity of risks and ensure that your infrastructure is secure and compliant with access settings, policies, and other guardrails you’ve defined within the env0 platform, for all imported resources.
Wrapping up
By now, you know how to import resources in your Terraform configuration using [.code]terraform import[.code] command and import block for managing existing cloud resources and which approach to use when.
We have looked at how the [.code]terraform import[.code] command can be used for infrastructure management using best practices. When performing the import action, you might face challenges such as using exact resource IDs, which helps ensure you have an accurate state file.
You can leverage the Cloud Compass feature in env0, which uses AI to auto-generate custom import blocks helping you save time and simplify your infrastructure management by keeping it secure, consistent, and compliant.
Frequently Asked Questions
Q. How do I import into a Terraform module?
To put import commands into a Terraform module, specify the module path in the import command.
For example:
terraform import module.module_name.resource_type.resource_name resource_id
Q. What do you need to do prior to running Terraform import?
Be sure to do the following before you run the [.code]terraform import[.code] command:
- Define the resource block for what you want to import in your Terraform configuration file.
- Identify the exact resource ID you will use.
- Initialize the working directory with [.code]terraform init[.code] command.
Q. Does Terraform import generate code?
The [.code]terraform import[.code] command does not generate code on its own, but updates the state file to include the existing resource.
Q. What is the difference between Terraform Import vs. Terraform State mv?
The commands are sometimes confused, but they perform two different functions. Let's quickly compare the two: