Indeed, the solution which works with today’s Terraform (v1.0 and earlier) is to explicitly update the state with the new addresses before you create a new plan with your updated configuration:
terraform state mv "example.first" "module.a.example.first"
The above assumes that you have a
module "a" block calling your new module and that the
resource "example" "first" declaration inside the module represents the same desired object that you originally had in the root module.
If you use Terraform v1.1 or later (Terraform v1.1.0 is currently in beta as I’m writing this) then if your shared modules belong to the same codebase as the root module (that is, if you are calling them using relative paths starting with
./, so Terraform will treat them as part of the same “package”) then you could get a similar effect by writing an equivalent
moved block in your root module:
from = example.first
to = module.a.example.first
With this approach, you can just run a normal
terraform apply operation and you should see Terraform report as part of the plan that the existing object moved to the new address, and so Terraform won’t plan to recreate it.