Terraform Import Block — Deep Dive for Real-World Enterprise Scenarios

 

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-out flag

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:

    1. Check Azure Portal/CLI → Note principalId, scope, roleDefinitionId.
    2. Use terraform state list to find existing addresses.
    3. Use terraform state show to 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, or change before import apply.
      • For random keys, use terraform state list and terraform state show → match carefully.
      • Keep import.tf under version control.

📌 Step-by-Step Import Process (Final Flow)

      1. Make a local copy of the backend state (terraform state pull).
      2. Prepare import.tf file (in same directory as tfvars and state file).
      3. Run terraform plan (with local state) to see import plan (should be 0 to add, 0 to change, 0 to destroy, x to import)
      4. Run terraform apply with local state
      5. Run terraform plan again (with local state) → Should show “Your Infrastructure is up to date”. (0 to add, 0 to change, 0 to destroy)
      6. Make sure a backup exists of the remote state file.
      7. Push local state file to remote backend → terraform state push terraform.tfstate.
      8. Run terraform plan with default remote backend → Should show “Your Infrastructure is up to date”.
      9. Delete local state file
      10. Delete import.tf file.

 

Leave a comment