terraform apply -target can still plan to destroy something you never named

Targeting one VM pulled an orphaned image download in through a local value and planned its destroy. A temporary edit keeps it out, and applying a saved, inspected plan beats an auto-approved one, because a saved plan goes stale after any other apply.

I only wanted to change a network tag on three VMs. terraform plan -target=<vm> planned the change I expected and one I didn’t: the destroy of a downloaded image that no VM used any more.

The target pulls in everything the VM’s boot logic refers to, and my boot source is computed in a local that also touches the image downloads. Resources that are orphaned (in state, no longer produced by the configuration) get destroyed along the way. My Terraform version has no -exclude.

Keeping it out

A temporary edit that keeps the orphan’s key in the for_each, then apply, then revert:

needed_images = distinct(concat(
  [{ node = "pve2", image = "debian-13" }],   # temporary: keeps the orphan out of the plan
  [for vm in var.vms : { node = vm.node, image = vm.image } if ...],
))

After the apply, git checkout the file.

Apply a plan you’ve read

Save the plan, inspect it as data, and apply the file:

terraform plan -out=vm.tfplan -target='proxmox_virtual_environment_vm.vms["905"]'
terraform show -json vm.tfplan | python3 -c '...'     # assert: one update, only the field I meant
terraform apply vm.tfplan

I asserted in the script that the plan had exactly one resource change and that only the intended attribute differed. Two practical notes: the resources here are keyed by VM id, so the address is vms["905"] and not the VM’s name; and a saved plan goes stale after any other apply to the same state (“Saved plan is stale”), so with several VMs, plan again before each one. A long apply can also be cut off by a tool’s timeout in the middle: run it in the background and plan again to see where it stopped.