When an existing Terraform resource is moved into a module, Terraform sees a new resource address. If state is not moved first, the plan may try to create the module resource and destroy the old root resource.

Symptom

After converting a root resource to a module call, the plan shows addresses like this:

# module.vm_group.vsphere_virtual_machine.vm["lb1"] will be created

while the existing state still has:

vsphere_virtual_machine.vm["lb1"]

Check Current State

Run from the environment root:

terraform state list

For vSphere VMs, filter the list:

terraform state list | grep vsphere_virtual_machine

Move State

Move the existing resource address to the module address:

terraform state mv \
  'vsphere_virtual_machine.vm["lb1"]' \
  'module.vm_group.vsphere_virtual_machine.vm["lb1"]'

Repeat for each for_each key:

terraform state mv \
  'vsphere_virtual_machine.vm["wrkr1"]' \
  'module.vm_group.vsphere_virtual_machine.vm["wrkr1"]'

Validate

Run:

terraform validate
terraform plan

The target result is no unexpected replacement. If the plan still shows replacement, inspect the forced replacement field before applying:

terraform plan -out=tfplan
terraform show -no-color tfplan > plan.txt
grep -n "must be replaced\|forces replacement" plan.txt

Notes

  • Do not use terraform state rm for this case. The resource still exists; only its Terraform address changed.
  • Move state before applying the module conversion.
  • Preserve behavior first. Improve module defaults after the plan is stable.
  • If the environment uses for_each, the keys must stay stable or Terraform will treat the instances as different resources.