LAB 07 – Import, drift and removed¶
Story: GreenGrid still has a legacy resource group that someone created in the portal years ago, a colleague "quickly" changes tags in the portal, and the data team takes over the README blob. Time to bring reality and code back together.
| Duration | ~40 min |
| You create | legacy.tf; you edit main.tf |
| Solution | solutions/LAB07/ (or ./bin/catchup 7) |
| Situation | Tool |
|---|---|
| a resource exists in Azure, not in Terraform | import block (+ -generate-config-out) |
| many existing resources | terraform query (list resources) or aztfexport |
| someone changed Azure, not the code | drift: terraform plan, then decide |
| Terraform should forget a resource but not delete it | removed block |
Step 1 – Something created by hand¶
P=jk # <- your prefix
az group create -n rg-$P-legacy -l westeurope --tags owner=someone-who-left -o none
Step 2 – import block with generated configuration¶
legacy.tf:
import {
to = azurerm_resource_group.legacy
id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/rg-${var.prefix}-legacy"
}
Let Terraform write the resource block for you:
✅ Checkpoint: generated.tf contains an azurerm_resource_group "legacy" block with the values read from Azure; the plan says 1 to import.
Generated code is a starting point, not a result. Move the block into legacy.tf, delete generated.tf, and bring it to the GreenGrid standard:
resource "azurerm_resource_group" "legacy" {
name = "rg-${var.prefix}-legacy"
location = var.location
tags = merge(local.common_tags, { legacy = "true" })
}
✅ Checkpoint: the plan shows import + update in place (the tags change from owner=someone-who-left to the standard tags) – reviewed before it happens. After apply, terraform state show azurerm_resource_group.legacy works.
The
importblock may stay in the code (it is a no-op once imported) – or be deleted after every environment applied it. The oldterraform importcommand still works but changes the state immediately, without a plan.
Step 3 – Drift¶
Play the colleague in the portal:
az group update -n rg-$P-greengrid-dev --set tags.environment=production -o none
az network nsg rule create -g rg-$P-greengrid-dev --nsg-name nsg-app-$P-greengrid-dev \
-n allow-rdp --priority 200 --destination-port-ranges 3389 --access Allow -o none
terraform plan -var-file=env/dev.tfvars
✅ Checkpoint: Terraform reports the tag change and the unknown RDP rule and plans to revert both.
Decide per change: revert (apply), or adopt (change the code), or tolerate (ignore_changes). Here: apply – an RDP port open to the world is not a matter of taste.
terraform plan -refresh-onlyshows drift without proposing changes;terraform apply -refresh-onlywrites the real values into the state only.
Step 4 – removed: hand over a resource¶
The data team now manages the README blob. Delete the azurerm_storage_blob.readme resource from main.tf and add to legacy.tf:
✅ Checkpoint: azurerm_storage_blob.readme will no longer be managed by Terraform – not destroyed. Apply; the blob is still in Azure (az storage blob list ... -c raw --auth-mode login).
Step 5 – Bulk import (demo)¶
For a whole resource group, write configuration with aztfexport:
Or, since Terraform 1.14, query existing resources with list blocks in a *.tfquery.hcl file and terraform query -generate-config-out=... (for providers that support list resources).
Troubleshooting¶
| Symptom | Likely cause |
|---|---|
Cannot import non-existent remote object |
the resource group name/ID in the import block does not match Azure |
Resource already managed by Terraform |
already imported – remove the import block or ignore |
-generate-config-out refuses: file exists |
delete generated.tf first |
| removed block plans a destroy | destroy = false missing in the lifecycle block |
Files for this lab: Think-Cube/terraform-azure-labs/labs/LAB07-import-drift