Hello! Welcome to your next lesson in the "Advanced Infrastructure as Code with Terraform" module.
In our last session, we explored how to use data sources to read information from existing infrastructure, making our configurations more dynamic and adaptable. Today, we'll take the next logical step: learning how to take full control of infrastructure that was created outside of Terraform.
This lesson directly addresses the learning outcome: Use terraform import to bring existing, manually-created infrastructure under Terraform management.
This is a critical skill for any DevOps engineer. You will frequently encounter "brownfield" environments where infrastructure was created manually through a cloud console ("ClickOps") or with scripts before a proper IaC practice was adopted. Your job will be to bring this existing landscape under Terraform's control without destroying and recreating it.
We'll cover:
- The "why" and "when" of importing resources.
- The classic
terraform importcommand-line workflow. - The modern, safer, configuration-driven
importblock (introduced in Terraform 1.5). - The process of generating, reviewing, and applying configurations for imported resources.
1. Why and When to Import Infrastructure
Before diving into the "how," let's establish the "why." Bringing existing resources under Terraform's management is not just about tidiness; it's about establishing a single source of truth for your infrastructure.
A Comprehensive Guide to Importing Existing Infrastructure into ...
The article 'A Comprehensive Guide to Importing Existing Infrastructure into Terraform' provides a great overview of the scenarios and benefits of importing.
Please read the introduction and the section titled 'Why import existing Infrastructure into Terraform and OpenTofu in the first place?'. This will set the context for the practical exercises to follow.
As the article highlights, importing allows you to achieve:
- Unified Management: Your IaC code becomes the definitive source of truth.
- Consistency: You can detect and remediate configuration drift.
- Automation: The imported resources become part of your standard CI/CD workflow for
planandapply. - Reproducibility: You can replicate the configuration in other environments.
Now, let's explore the two primary methods for accomplishing this.

2. The Classic Method: The terraform import Command
For years, the only way to import resources was with the terraform import command. While a newer method now exists, you will still encounter this approach in older projects, documentation, and tutorials, so it's essential to understand it.
The workflow is procedural and requires careful execution:
- Identify the Resource ID: Find the unique identifier for the existing resource in your cloud provider (e.g.,
vpc-0123abcdfor an AWS VPC). - Write the HCL Code: Manually write a
resourceblock in your.tffile that you believe matches the configuration of the existing resource. - Run the Import Command: Execute
terraform import <RESOURCE_ADDRESS> <ID>in your terminal. This command directly modifies the Terraform state file, linking your HCL block to the existing resource. - Verify with
plan: Runterraform plan. If your HCL code perfectly matches the actual resource, Terraform will report "No changes." If not, it will show a plan to modify the resource to match your code.
This video demonstrates this classic workflow by importing a full AWS network stack.
Terraform Import Existing Resources AWS (Import VPC, Route53, EC2, Security Group)
The video 'Terraform Import Existing Resources AWS' by Anton Putra provides a rapid, practical demonstration of the classic terraform import command for several common AWS resources.
Watch the full video (around 4.5 minutes). Notice the repeated pattern: Define the resource block in main.tf. Run terraform import <address> <id>. At the end (04:16), see how terraform plan is used to verify that everything has been imported correctly with no drift.
While effective, this method has significant drawbacks, especially in a collaborative DevOps environment:
- Direct State Manipulation: It modifies the state file without the safety of a
planandapplycycle. This is risky. - Risk of Destruction: If you run
importbut haven't written the corresponding HCL code yet, a teammate runningterraform applycould cause Terraform to see a resource in the state that's not in the code, leading it to destroy the live resource. - One-at-a-Time: You must run the command for every single resource, which is tedious for large environments.
These limitations led HashiCorp to develop a more robust, declarative approach.
3. The Modern Method: The Declarative import Block
Introduced in Terraform 1.5, the import block integrates the import process directly into the standard plan and apply workflow, making it significantly safer and more aligned with IaC principles.

The modern workflow is:
- Identify Resource ID: Same as before.
- Define
importBlock: Add animportblock to your configuration. It requires two arguments: the resourceidand thetoaddress where it will be managed. - Generate Configuration (Optional but Recommended): Run
terraform plan -generate-config-out=generated.tf. Terraform will inspect the live resource and automatically write a correspondingresourceblock for you in a new file. - Review and Refine: The generated code is a starting point. You must review it, remove unnecessary arguments, and fix any issues.
- Plan and Apply: Run
terraform planto see the import action. Then runterraform applyto execute it. - Cleanup: Once the import is complete, remove the
importblock from your configuration.
This video from Ned in the Cloud provides an excellent deep dive into this modern workflow and its advantages.
Exploring the Import Block in Terraform 1.5
In 'Exploring the Import Block in Terraform 1.5', Ned explains the problems with the old command and provides a fantastic demonstration of the new, safer workflow.
Please watch these key segments: Shortcomings of import command (00:48 - 04:19): This clearly explains the risks I mentioned earlier. Understanding these problems highlights why the new method is superior. The New import Block Workflow (04:19 - 06:14): Pay close attention to the new, safer process: add block -> plan -> apply. Generating Configuration (06:14 - 07:22): This introduces the powerful -generate-config-out flag. Practical Demos (07:22 - 20:45): You can watch the full demo, but the most crucial part is understanding the process shown in the first Azure subnet example (07:22 - 12:37). The second example (12:37 onwards) is a great illustration of how you need to clean up the auto-generated code, which is a key real-world step.
The key advantages of the import block are clear:
- Declarative: The intent to import is captured in code.
- Safe: It's part of the standard
plan/applycycle. You can review the import plan before any state is modified. - CI/CD Friendly: It can be run in automated pipelines without manual CLI commands.
- Code Generation: The
-generate-config-outflag saves a significant amount of manual effort, though it's still experimental and requires human oversight.
4. Step-by-Step Guide and Best Practices
Let's solidify this with a practical guide. While the videos used Docker and Azure, the process is identical for AWS. The official HashiCorp tutorial provides another excellent walkthrough that also covers important details about refining the generated code.
Import Terraform configuration - HashiCorp Developer
The official HashiCorp tutorial 'Import Terraform configuration' walks through the entire configuration-driven import process, including the crucial step of refining the auto-generated code.
Read through this tutorial from 'Prerequisites' to 'Limitations and other considerations'. The example uses Docker, but the principles are universal. Focus on these key sections: Define import block (Section 2): Note the simple syntax using id and to. Generate configuration (Section 3): This is the core of the workflow. Pay special attention to how the tutorial identifies an issue in the generated plan (forces replacement) and then corrects the generated HCL code to achieve a clean import. Import and update the resource (Section 4): This shows how you can even modify the resource in the same apply that imports it. Limitations and other considerations (Section 6): This is a critical read. It highlights that import doesn't detect relationships or know which default attributes can be ignored. You, the engineer, must provide that context.
Conclusion
You now have the knowledge to confidently bring existing, manually-created infrastructure under Terraform's management. This is a day-one skill for a DevOps engineer joining a team that is in the process of adopting IaC.
Key Takeaways:
- Two Methods: The classic
terraform importcommand (procedural, risky) and the modernimportblock (declarative, safe, recommended). - The Modern Workflow: Define an
importblock, useterraform plan -generate-config-outto create a starting configuration, refine the generated code, and then useplan/applyas usual. - Refinement is Key: Auto-generated code is not perfect. It's your responsibility to prune it, add necessary resource relationships (like referencing a
docker_imagefrom adocker_container), and ensure the plan is non-destructive (unless intended). - Always Verify: After any import operation, run
terraform planone last time to ensure your state and configuration are perfectly synchronized with reality, resulting in a "No changes" message.
In our next and final lesson of this module, we will put everything together. We'll learn how to integrate Terraform into a CI/CD pipeline to automate infrastructure validation (plan) and deployment (apply). This will automate the workflows we've been practicing manually, which is the ultimate goal in a modern DevOps environment.