Terraform: Move entire resource group to new Azure subscription

Viewed 926

A historically grown project with it's associated infrastructure on Azure must be split up. Luckily, it is well structured and as a result it is pretty clear that we need to move two resource groups and it's affiliated resources to a new Azure subscription.

  • I have checked, most of the resource can be moved, thus don't have to be created anew and data can be preserved.

  • For those resources that cannot be moved, luckily we are using terraform, which can be run to re-create those resources.

  • The resources that need to be moved is an entire terraform-project, so there is no need to remove resources from the current configuration files. All of it needs to go to a new subscription.

So far so good, but now I need to understand how to practically proceed, so that terraform is aware of the moved resources. The only option I currently see is to manually move the resources on the Azure portal, change subscription on the CLI/for terraform and then run an terraform state rm; terraform import or a terraform mv for each of the resources.

Isn't there an easier way to achieve this? It seems overly cumbersome and basically would make it easier to just delete the entire infrastructure in the current subscription and recreate it via terraform in the new subscription (not an option!)

1 Answers

It is NOT possible to move resources from one resource group to another simply via changing resource_group_name parameter in *tf file. Terraform 1.0.2 will attempt to destroy such a resource, therefore trying to setup a multi-subscription terraform workspace is futile. Also as docs say:

resource_group_name - (Required) The name of the Resource Group in which the Linux Virtual Machine should be exist. Changing this forces a new resource to be created.

TL;DR - there is no native mechanism for migrating Azure resources between resource groups or subscriptions via terraform

As you point out, moving resources manually and retaining them in Terraform would require some state file manipulation. Fortunately Azure resource IDs are pretty straightforward, so a simple string processing in bash can do the trick.

Quick and dirty bash script could just replace old subscription UUID with new and be similar to this (WARNING - Correct implementation depends on your setup - do some testing before running all the statements):

# Extract the identifying URLs using terraform plan
terraform  plan | grep "Refreshing state" > terraform-current-states.txt
# Extract the states we know from the configuration
terraform state list > terraform-state-list.txt

# Subscription strings
OLD_SUBSCRIPTION=11111111-1111-1111-1111-111111111111
NEW_SUBSCRIPTION=00000000-0000-0000-0000-000000000000
# Resource group strings (other name)
OLD_RG='SOME_NAME'
NEW_RG='ANOTHER_NAME'

# Iterate over state list
cat terraform-state-list.txt | while read RESOURCE; do
  AZURE_ID=$(grep "${RESOURCE}"  terraform-current-states.txt | cut -d"=" -f2 | tr -d ']')

  # null resources etc can be filtered out - no AZURE_ID
  if [ -z "$AZURE_ID" ]; then
    continue
  fi

  # print out the current resource and azure id mapping as a comment
  echo "# ${RESOURCE} ${AZURE_ID}"

  # replace the subscription and resource group strings (and remove weird characters from terraform
  NEW_AZURE_ID=$(echo ${AZURE_ID} | sed "s/${OLD_SUBSCRIPTION}/${NEW_SUBSCRIPTION}/" | sed "s/${OLD_RG}/${NEW_RG}/g" | sed "s/\x1b\[[0-9;]*m//g")

  # replace the current terraform state with a "state rm" and then "import" command
  echo "terraform state rm ${RESOURCE}; terraform import ${RESOURCE} ${NEW_AZURE_ID}
  "
done > terraform-migration-generated-script.sh

The above produces a ready-to-use terraform-migration-generated-script.sh script with terraform rm and terraform import commands.

Don`t forget about state backup or even terraform state pull and push to different location before you start.

Related