Terraform import is essential when real-world infrastructure needs to be brought under Terraform management without recreating or destroying resources. But with custom modules and enterprise scenarios, importing becomes tricky. This article explains everything in detail, including the confusing parts.
Terraform Import Methods Comparison
1. The terraform import Command
This was the traditional approach of importing resources to Terraform, before terraform 1.5.
terraform import azurerm_virtual_network.example /subscriptions/.../resourceGroups/rg-example/providers/Microsoft.Network/virtualNetworks/vnet-example
What it promises:
-
-
- Imports resource into state file
- Can generate configuration with
-generate-config-outflag
-
Enterprise limitations:
-
-
- You are directly importing resources to state file, without seeing the plan – risky for an Enterprise
- Fails with module-based architectures – cannot properly generate module references
- No support for
for_each/count– generates static configurations even for dynamic resources - Lacks resource dependencies – imports resources in isolation without reference to related resources
-
2. Import Blocks (Terraform 1.5+)
Version 1.5 onward, Terraform has launched a new approach to import resources, which is import block.
In this article, we will cover how to import Azure services to terraform state using import block feature.
import {
to = azurerm_virtual_network.example
id = "/subscriptions/.../virtualNetworks/vnet-example"
}
Important: If you’re using Terraform version 1.5 or above, import blocks are the preferred option. Their biggest advantage is the ability to see the plan before actual import, which isn’t possible with the terraform import command.
Understanding the Import Block
import {
to = <terraform state address>
id = <real-world resource ID>
}
to: Where the resource should be imported in Terraform state.
id: Real resource ID in Azure
You can store this block in a file (say import.tf) within the Terraform working directory, where you have the tfvars, state and from where you will run terraform plan and apply.
Likewise, you can create many import blocks one after another, one import block for each Azure resource that you want to import.
So if you want to import 4 Azure resources, the structure of import.tf file will be as follows :
import.tf
import {
to = <terraform state address>
id = <real-world resource ID>
}
import {
to = <terraform state address>
id = <real-world resource ID>
}
import {
to = <terraform state address>
id = <real-world resource ID>
}
import {
to = <terraform state address>
id = <real-world resource ID>
}
🎯 Two Practical Scenarios
✅ Scenario 1: Easy-to-Identify Resources (Modules with Keys)
When modules support keys, Terraform plan will clearly show the address.
Example from terraform plan:
module.network.azurerm_subnet.subnet["web"]
Azure Resource ID:
/subscriptions/xxx/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-01/subnets/web
Import block:
import {
to = module.network.azurerm_subnet.subnet["web"]
id = "/subscriptions/xxx/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-01/subnets/web"
}
Direct copy from plan + ID = Simple!
❗ Scenario 2: Auto-generated Keys (Role Assignments etc)
When no keys are used in modules → Terraform uses random index/GUIDs. Matching becomes tricky.
Terraform plan shows:
module.managed_id.azurerm_role_assignment.this["1e71f885-b502-49b4-8d7d-6ea99381234a"]
But which Azure resource does this belong to?
Solution:
-
- Check Azure Portal/CLI → Note principalId, scope, roleDefinitionId.
- Use
terraform state listto find existing addresses. - Use
terraform state showto inspect and match principalId/scope → Identify correct mapping.
Once matched → prepare import block accordingly.
📦 Working with Remote State (Backend)
Typically, in a enterprise environment, state files are stored in remote backend. However, for this import purpose it is convenient to work on local state file.
Therefore, if our state file is stored in remote backend, we will create a local copy from it by running terraform state pull command. Once the import process is done, we will push the local state file to remote backend by terraform state push command.
✅ Downloading state (Recommended → terraform pull)
terraform state pull > terraform.tfstate
Working with local state:
Remember, when you run terraform plan or terraform apply, by default it will consider remote backend, if it is configured.
Therefore, if you want to refer local state file, you have to specify it by using the -state parameter. Failure to do so will point it to the remote backend.
terraform plan -state=terraform.tfstate terraform apply -state=terraform.tfstate
✅ Updating remote backend (Recommended → terraform push)
Important: Always take a backup of remote state before overwriting!
Once import is done and terraform plan shows no changes, it’s time to update the remote backend.
terraform state push terraform.tfstate # Push local to remote backend
Validate:
terraform plan
Now, this one is for the remote state file, as you did not specify the state file location.
✅ Should show → Infrastructure up to date.
Bingo ! The import operation is fully successful !
📌 Best Practices
-
-
- Take backup of remote state file before the activity.
- Ensure no
create,delete, orchangebefore import apply. - For random keys, use
terraform state listandterraform state show→ match carefully. - Keep import.tf under version control.
-
📌 Step-by-Step Import Process (Final Flow)
-
-
- Make a local copy of the backend state (
terraform state pull). - Prepare
import.tffile (in same directory as tfvars and state file). - Run
terraform plan(with local state) to see import plan (should be 0 to add, 0 to change, 0 to destroy, x to import) - Run
terraform applywith local state - Run
terraform planagain (with local state) → Should show “Your Infrastructure is up to date”. (0 to add, 0 to change, 0 to destroy) - Make sure a backup exists of the remote state file.
- Push local state file to remote backend →
terraform state push terraform.tfstate. - Run
terraform planwith default remote backend → Should show “Your Infrastructure is up to date”. - Delete local state file
- Delete import.tf file.
- Make a local copy of the backend state (
-