Home / Azure

AzureChecklistAdvanced

Moving Azure resources to another resource group or subscription: a pre-move checklist

Validate a resource move before you run it, move dependent resources together, and fix the role assignments, alerts and IaC state that a changed resource ID breaks.

Deepak Bhardwaj7 min read

Applies toAzure Resource Manager moves within one Entra ID tenant, Azure CLI 2.50+

The goal

You need to move resources to a different resource group or subscription: a reorganisation, a new landing zone, or a workload moving to its own subscription. The move itself is one command. What goes wrong is everything around it: a dependent resource left behind, role assignments that no longer apply, alerts pointing at an old ID, Terraform trying to recreate what you moved.

Facts to keep in mind:

  • The resource ID changes. Its region does not. Moving between regions is a different job (Azure Resource Mover or redeploy).
  • Both the source and target resource groups are locked for writes for the whole move, which can take up to a few hours for large moves. Running resources keep running, but you can't create, update or delete in either group until it finishes.
  • Not every resource type can move, and some move only with restrictions. Check the "Move operation support for resources" page in the Azure documentation for each type.

1. Inventory the source

List what is in the resource group and what type each item is:

az resource list --resource-group rg-orders-prod \
  --query "[].{name:name, type:type, id:id}" --output table

For a subscription-wide view, use Resource Graph:

resources
| where resourceGroup =~ "rg-orders-prod"
| summarize count() by type
| order by count_ desc
  • Every resource type checked against the move support page.
  • Dependencies mapped: a VM moves with its disks and NICs; a virtual network moves with everything that depends on it.
  • Peered virtual networks: peering must be removed before the VNet can move, and re-created afterwards.
  • App Service: the app and its plan, and any certificates, follow the App Service move restrictions in the docs.
  • VMs protected by Azure Backup or Site Recovery: follow the VM-specific move guidance (restore point collections and replication have extra steps).

2. Check the target subscription

  • Same Entra ID tenant as the source. Cross-tenant moves are a different, more disruptive process.
  • Target subscription is active.
  • Resource providers registered in the target:
TARGET_SUB=<target-subscription-id>

for ns in Microsoft.Compute Microsoft.Network Microsoft.Storage Microsoft.Web; do
  echo "$ns: $(az provider show --namespace $ns --subscription $TARGET_SUB --query registrationState -o tsv)"
done

az provider register --namespace Microsoft.Web --subscription $TARGET_SUB
  • Quotas in the target cover what you are bringing (vCPUs per family, public IPs):
az vm list-usage --location westeurope --subscription $TARGET_SUB --output table
  • Azure Policy assignments at the target scope reviewed. After the move, the resources are evaluated against the target's assignments, and deployIfNotExists or modify policies may change them.

3. Check permissions and locks

The account that runs the move needs Microsoft.Resources/subscriptions/resourceGroups/moveResources/action on the source resource group and Microsoft.Resources/subscriptions/resourceGroups/write on the target. Contributor on both is the usual way.

az lock list --resource-group rg-orders-prod --output table
az lock list --resource-group rg-orders-shared --subscription $TARGET_SUB --output table
  • Locks that would block the move removed for the window, with a note to put them back.

Warning

Removing a lock removes the protection it was there for. Remove it for the change window only, and re-create it in the same change.

4. Record what the new ID will break

Collect these before the move, because afterwards they point at nothing.

Role assignments scoped to the resources or the source group do not follow the resources:

az role assignment list --resource-group rg-orders-prod --include-inherited false \
  --query "[].{principal:principalName, role:roleDefinitionName, scope:scope}" --output table

Managed identities keep their principal, but their own role assignments are scoped to the old IDs. List them for each identity:

az role assignment list --assignee <principal-id> --all --output table

Alert rules, action groups and workbooks that reference the old IDs:

resources
| where type in~ ("microsoft.insights/metricalerts", "microsoft.insights/scheduledqueryrules")
| mv-expand scope = properties.scopes
| where tostring(scope) contains "/resourcegroups/rg-orders-prod"
| project name, resourceGroup, scope
  • Role assignments exported.
  • Alert rules, dashboards and workbooks listed.
  • Scripts, pipeline variables, Key Vault references and connection strings that contain resource IDs found (search your repos for the resource group name).
  • IaC owners told: Terraform state and Bicep parameter files will be wrong after the move.

5. Validate without moving

The validateMoveResources action runs every check without moving anything. Put the source resource group in --ids, and the resources plus the target group's full ID in the body:

SRC_SUB=<source-subscription-id>

az resource invoke-action --action validateMoveResources \
  --ids "/subscriptions/$SRC_SUB/resourceGroups/rg-orders-prod" \
  --request-body "{
    \"resources\": [
      \"/subscriptions/$SRC_SUB/resourceGroups/rg-orders-prod/providers/Microsoft.Compute/virtualMachines/vm-orders-01\",
      \"/subscriptions/$SRC_SUB/resourceGroups/rg-orders-prod/providers/Microsoft.Compute/disks/vm-orders-01-osdisk\",
      \"/subscriptions/$SRC_SUB/resourceGroups/rg-orders-prod/providers/Microsoft.Network/networkInterfaces/vm-orders-01-nic\"
    ],
    \"targetResourceGroup\": \"/subscriptions/$TARGET_SUB/resourceGroups/rg-orders-shared\"
  }"

List only top-level resources; child resources move with their parent. No output means validation passed. A failure returns an error that names the resource and the reason.

Validation message points to Likely cause Fix
MissingSubscriptionRegistration Provider not registered in target az provider register in the target subscription
A resource "must be moved together" with another Dependent resource left out of the list Add the dependent resources and validate again
Resource type not supported Type can't move Redeploy it in the target instead
Authorization failure Missing move or write permission Grant the actions from step 3
Lock or scope errors Lock on source or target group Remove for the window, re-add after

6. Run the move

Run it in a change window, because both groups are locked for writes until it finishes:

az resource move \
  --destination-group rg-orders-shared \
  --destination-subscription-id $TARGET_SUB \
  --ids \
    "/subscriptions/$SRC_SUB/resourceGroups/rg-orders-prod/providers/Microsoft.Compute/virtualMachines/vm-orders-01" \
    "/subscriptions/$SRC_SUB/resourceGroups/rg-orders-prod/providers/Microsoft.Compute/disks/vm-orders-01-osdisk" \
    "/subscriptions/$SRC_SUB/resourceGroups/rg-orders-prod/providers/Microsoft.Network/networkInterfaces/vm-orders-01-nic"

Leave out --destination-subscription-id for a move within the same subscription. Confirm the result in the target:

az resource list --resource-group rg-orders-shared --subscription $TARGET_SUB --output table

Caution

Don't cancel or retry a move from a second session while it is running. If the command loses its connection, check the Activity Log on the source resource group for the move operation's status before you do anything else.

7. After the move

  • Role assignments re-created at the new scope, including those for managed identities.
  • Locks re-applied on both groups.
  • Alert rules and dashboards re-pointed at the new IDs, and one test alert fired.
  • Diagnostic settings checked on the moved resources; confirm logs still arrive in Log Analytics.
  • Terraform state fixed: terraform state rm for the old address and terraform import with the new ID, then terraform plan shows no changes.
  • Bicep and pipeline parameters updated to the new resource group and subscription.
  • Backup protection confirmed: az backup item list --vault-name <vault> --resource-group <vault-rg> --output table.
  • Cost reports and budgets checked; cost moves to the target subscription from the move date.

Prevent it next time

  • Put each workload in its own resource group from day one, with everything it depends on, so a later move is one validated list.
  • Keep shared resources (VNets, Key Vaults, Log Analytics) in separate groups so workload moves don't drag them along.
  • Refer to resources by name and resource group in IaC and scripts, not hard-coded IDs.
  • Keep this checklist with the change template, and always run validateMoveResources before the window.