Importing Resources Into Terraform State
When working with Terraform, you may need to import existing resources into your Terraform state, either to manage them efficiently or to address the unfortunate situation of losing your Terraform state file. In this guide, I will present two essential methods for accomplishing this task, specifically focusing on AWS resources. It’s worth noting that these methods can also be applied to resources from other cloud service providers.
Terraform
Let’s do a quick recap of what Terraform is and its top features so we know why a tfstate file is important. Terraform is an Infrastructure as Code (IaC) tool that allows you to define the desired state of infrastructure resources in code. Terraform leverages software engineering practices like version control, continuous integration, and continuous delivery, among others. These are some benefits of using IaC:
- Consistency and Reproducibility: Ensures infrastructure is set up the same way every time, reducing errors and inconsistencies.
- Version control: Infrastructure configurations can be versioned and tracked like any other piece of code, enabling rollbacks and collaborative development.
- Automation and efficiency: Automates the provisioning and management of infrastructure, reducing manual intervention and speeding up deployments.
- Scalability: Easily scales infrastructure up or down based on configuration changes, making it adaptable to varying workloads.
- Cost management: Helps in identifying and managing unused resources, leading to cost savings.
- Documentation: Acts as documentation for the infrastructure setup, making it easier for teams to understand and manage.
- Testing and validation: Allows for testing infrastructure changes in a controlled environment before applying them to production.
Terraform offers a configuration language and a set of Command-Line Interface (CLI) commands to help us manage and keep track of our infrastructure effectively.
Resources
Terraform uses a configuration language to declare resources representing the infrastructure objects you expect to manage. For example, you can declare resources representing virtual machines, DNS records, etc. Different resource types can be grouped in collections defined by a provider. A provider is a plugin that provides a collection of resource definitions for objects available in an infrastructure platform, such as cloud providers (AWS, Azure, etc.) or on-premises solutions (VMWare vSphere, etc.). To review the basic Terraform syntax, check this example. The Terraform code is stored in files that have a .tf extension, for example: imports.tf
State
The state in Terraform is a file used to keep track of the desired state and metadata of the resources defined with the configuration language. This file is crucial for Terraform operations. By default, Terraform stores the state file locally, but this behavior can be modified using the proper configuration and external third-party tools or services (S3 backend, HTTP backend, HCP Terraform, etc.) to store it in a remote place. There are several advantages of storing the state file in remote locations:
- Collaboration: Allowing multiple users to work together without conflicts, ensuring changes are synchronized, and avoiding discrepancies between team members’ environments.
- State locking: Prevent simultaneous changes, to avoid state corruption by locking the state file when running Terraform. This ensures that only one user or process can modify the state at a time.
- Backup and recovery: Ensure the state file is backed up and recoverable, minimizing the risk of data loss. Remote storage solutions often include automated backups and recovery options.
- Versioning: Enabling rollback to previous state versions to provide a safety net if a deployment goes wrong. This makes it easier to revert to a known good state.
- Security: Enhance security with managed services offering encryption and access controls.
- Centralized management: Facilitate tracking and auditing of changes, providing a single source of truth for the infrastructure state. This centralization helps in maintaining compliance and understanding the history of changes.
- Automation integration: Storing the state file remotely allows seamless integration with CI/CD tools like Azure DevOps or GitHub Actions, enabling automated deployment workflows.
Command-Line Interface
Once the Terraform software package is installed, we can access the Comand-Line Interface or CLI using the Terraform command. This command allows us to execute subcommands, for example: terraform init
, terraform plan
, terraform import
, etc.
By entering the terraform
command followed by various subcommands, you can initiate and manage your infrastructure as code. For example, the terraform init
subcommand prepares your working directory by downloading the necessary provider plugins while terraform plan
generates an execution plan, previewing the changes that will be made to your infrastructure. The terraform import
subcommand allows you to bring existing infrastructure under Terraform management, making it easier to control resources that were created outside of Terraform.
Other subcommands, such as terraform apply
and terraform destroy
, enable you to apply changes or clean up your infrastructure when needed. Each of these commands plays a crucial role in the Terraform workflow, enabling you to efficiently and effectively manage your infrastructure.
Why Import Existing Resources
Before diving into the methods, it’s important to understand why importing resources is necessary. Terraform’s strength lies in its ability to manage infrastructure as code, ensuring consistency and repeatability. Importing existing resources allows you to:
- Ensure consistency: Maintain a single source of truth for your whole infrastructure to ensure that all configurations are managed centrally, reducing discrepancies.
- Improve manageability: Apply Terraform’s powerful orchestration and automation capabilities to existing resources.
- Recover from state file loss: Reconstruct your Terraform state to avoid having to recreate infrastructure or doing manual configuration.
Preparing For Import
Before starting the import process, be sure you meet the following prerequisites:
- Terraform is installed: Make sure you have version 1.5.0 or later of Terraform installed on your system, which is the minimum version that supports import blocks (will be reviewed later). A complete guide to installing Terraform is available here. If you need to manage multiple versions, you can use a Terraform version manager like tfenv.
- AWS CLI is configured: Ensure that your AWS credentials are properly set up and, you have the necessary permissions to access the resources. A complete guide to installing AWS CLI is available here.
- Resource identifiers: Gather the identifiers (e.g., instance IDs, ARN, etc.) of the resources you want to import. In the AWS console, navigate to each resource’s section (e.g., EC2, ECS) to view the ID or ARN of each resource. Here is an example of how to retrieve the ID of an EC2 instance.
Importing Resources
Now we are ready to import the resources. There are two methods: using the import command or using import blocks.
Import command
When using the import command method, it’s important to consider the following steps:
- Define the resources in Terraform files: before you run Terraform import, you must manually write a resource configuration block for the resource being imported, as if you were defining the resource for the first time. The resource block describes where Terraform should map the imported object:
resource “aws_instance” “example” {
name = "hashi"
# (other resource arguments…)
}
2. Know the resource ID: You should have the resource ID in your cloud service provider at hand. For example, in AWS, this could be the security group ID. Some resources may not have an ID in the AWS console, in which case you should use the resource’s name or ARN (for example, a Load Balancer).
3. Know the address of the resources: The resource address specifies where the resource definition resides in the Terraform files. For example, the address for the resource in step one would be aws_instance.example
.
4. Import order: Import resources in a logical order, starting with dependencies and then progressing to the resources that use them. For example, when importing an EC2 instance with a security group, start by importing the security group first and then continue with the EC2 instance. The order is important to ensure that Terraform can manage dependencies correctly.
Here’s an example of how to import a resource into Terraform using the Terraform import command. It requires two parameters: the address and ID of the resource in the cloud provider:
terraform import aws_security_group.example sg-0123456789abcdef0
In this example, we are importing an AWS security group with the ID sg-0123456789abcdef0 using the terraform import
command.
Import blocks
An import block is an object where you define the necessary attributes needed to identify the resources to be imported. Here’s how to go about this process:
- Define resources in the Terraform file: Just like in the previous method, make sure the resources you want to import are defined in your Terraform configuration file.
- Create a Terraform imports file: For example, a file called
imports.tf
, but can be any name. In this file, you will define the import blocks for each resource you intend to import. - Configure the import blocks: Each import block should include the ID and address of the resource you want to import. Terraform will use this information to associate the existing resource with your Terraform configuration.
Here’s an example of Terraform code to import a security group in AWS:
import {
to = aws_security_group.example
id = "sg-0123456789abcdef0"
}
resource "aws_security_group" "example" {
name = "example"
description = "example of terraform import"
vpc_id = "vpc-0123456789123456a"
ingress {
description = "External access"
from_port = 443
to_port = 443
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
"environment" = "sandbox"
}
}
provider "aws" {
region = "us-east-1"
}
After defining the import blocks, we can run the plan to review imports and changes to be made:
terraform plan
We can review the changes before applying them, for example:
aws_security_group.example: Preparing import... [id=sg-0123456789abcdef0]
aws_security_group.example: Refreshing state... [id=sg-0123456789abcdef0]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_security_group.example will be updated in-place
# (imported from "sg-0123456789abcdef0")
~ resource "aws_security_group" "example" {
arn = "arn:aws:ec2:us-east-1:465797644872:security-group/sg-0123456789abcdef0"
description = "example of terraform import"
egress = [
{
cidr_blocks = [
"0.0.0.0/0",
]
description = ""
from_port = 0
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "-1"
security_groups = []
self = false
to_port = 0
},
]
id = "sg-0123456789abcdef0"
~ ingress = [
- {
- cidr_blocks = [
- "0.0.0.0/0",
]
- description = "External access"
- from_port = 443
- ipv6_cidr_blocks = []
- prefix_list_ids = []
- protocol = "tcp"
- security_groups = []
- self = false
- to_port = 443
},
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ description = "External access"
+ from_port = 443
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "-1"
+ security_groups = []
+ self = false
+ to_port = 443
},
]
name = "example"
owner_id = "123456789012"
+ revoke_rules_on_delete = false
tags = {
"environment" = "sandbox"
}
tags_all = {
"environment" = "sandbox"
}
vpc_id = "vpc-0123456789123456a"
}
Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.
In this case, we are defining the rules, and if you agree with the plan, you can apply the changes by running the following command:
terraform apply
One advantage of this method is that you don’t need to define the specific order for importing resources, as Terraform will handle associating resources and its dependencies correctly based on the resources defined in Terraform files.
Another advantage is that you can use this method as part of a CI/CD pipeline. After importing the resources, you can delete the imports.tf file or leave it, as Terraform will recognize that these resources are already imported and part of the state.
Conclusion
Understanding the two methods of importing resources into Terraform: using the import command and import blocks, empowers you to manage your infrastructure more effectively. By leveraging these techniques, you can:
- Ensure consistency by maintaining a single source of truth for your infrastructure.
- Enhance manageability through Terraform’s powerful orchestration and automation capabilities
- Recover from state file loss without the need to recreate the infrastructure manually.
Whether you are integrating with previously manual configurations or addressing state file issues, these guides will be invaluable tools in your Terraform arsenal.