Terraform refactor to modules deletes resources

I’ve finally got a use-case for using Modules in Terraform, and so I’m beginning to dip into testing with them. I’ve got a bunch of Azure resources (VNET, subnet, NSG, etc) that already exist in my Terraform config, and I’m looking to basically duplicate them for a disaster recovery purpose.

In reality, I don’t want to duplicate the Terraform config, because if it ever changes or improves, it would not be efficient to track those changes in multiple spots. So instead I can move the resources that build my VNET into a module, add some variables, and then call the module twice from my main Terraform config – just need to pass in different values for the variables.

module "vnet" {
  source = "./network"
  env = "test1"
  location = "CanadaCentral"

module "dr-vnet" {
  source = "./network"
  env = "test2"
  location = "CanadaEast"

While reading up on modules though, I noticed a note in the docs:

When refactoring an existing configuration to introduce modules, moving resource blocks between modules causes Terraform to see the new location as an entirely separate resource to the old. Always check the execution plan after performing such actions to ensure that no resources are surprisingly deleted.

Yikes! Even for a simple implementation of a VNET, deleting it means deleting a bunch of dependent resources like virtual machines.

However, there does appear to be a (very manual) way to work around this problem when refactoring into Modules: use the terraform mv command to move location of a resource in the state file.

This relies upon the syntax of referencing resources beginning with module.”module name”. For example, lets say I have this original resource:

resource "azurerm_resource_group" "test-rg" {
  name     = "test-rg"
  location = "CanadaCentral"

Assuming I call the module just like my example above, I would use the following terraform mv command:

terraform state mv azurerm_resource_group.test-rg module.vnet.azurerm_resource_group.test-rg

Note the sytax of my destination, being module.”module name”.resourcetype.”resource name”

The downside is that I would need to do this for each original resource that is moving into a Module – perhaps a small price to pay in order to have better managed code, and at least I don’t need to go and find the Azure resource ID like you do with the terraform import command.

This resource group is actually a bad example, because terraform apply will fail when it tries to create two different resource groups with the same name in the same subscription. This could be avoided if you pass in an alternate AzureRM Provider to your Module, using a different Azure subscription.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.