?Migrating All Azure Resources Between Subscriptions with Azure CLI & PowerShell?

Migrating resources between Azure subscriptions is a common but risky task. Whether you’re reorganizing tenants, separating billing, or preparing for a handover, doing this manually is slow and error-prone.
The issue here is that Azure Portal does not provide an option to migrate all resources at once.

For this reason, I created a PowerShell + Azure CLI script that automatically migrates all resource groups and their resources from one subscription to another.

? What This Script Does

The script performs the following steps:

  1. Connects to a source Azure subscription
  2. Retrieves all resource groups
  3. Switches to a target subscription
  4. Creates missing resource groups
  5. Moves all supported resources to the target subscription
  6. Logs success and failure clearly

It uses native Azure Resource Manager (ARM) move operations, meaning:

  • Resource IDs remain intact
  • No redeployment is required
  • Downtime is minimized (but not zero)

? Prerequisites

Before running the script, ensure:

  • Azure CLI is installed (az version)
  • You are logged in (az login)
  • You have Owner or Contributor permissions on both subscriptions
  • Resources are in a movable state

⚠️ Not all Azure resources support cross-subscription moves (e.g., classic resources, some networking dependencies).


⚙️ Script Configuration

Update the following values before running:

$sourceSubId = "***"     # Source subscription ID
$targetSubId = "***"     # Target subscription ID
$location = "westeurope" # Location for new resource groups

The $location value is only used when creating missing resource groups in the target subscription.


PowerShell automation terminal


? Script Walkthrough

1️⃣ Switch to Source Subscription

az account set --subscription $sourceSubId

This ensures all resource discovery happens in the correct source context.


2️⃣ Retrieve All Resource Groups

$resourceGroups = az group list --query "[].name" -o tsv

This fetches every resource group name in the source subscription.


3️⃣ Ensure Resource Groups Exist in Target

az group exists --name $rg

If the resource group doesn’t exist in the target subscription, it is automatically created:

az group create --name $rg --location $location

✔ Prevents failures during resource moves
✔ Keeps naming consistent


4️⃣ Collect Resource IDs

$ids = az resource list --resource-group $rg --query "[].id" -o tsv

Azure requires resource IDs for move operations, not names.


5️⃣ Move Resources Across Subscriptions

az resource move `
  --destination-group $rg `
  --destination-subscription-id $targetSubId `
  --ids $ids

This is the core operation:

  • Moves resources
  • Keeps them in the same resource group name
  • Preserves configuration and metadata

6️⃣ Error Handling & Logging

if ($LASTEXITCODE -eq 0) {
    Write-Host "SUCCESS"
} else {
    Write-Host "FAILED - see error above"
}

Failures usually occur due to:

  • Unsupported resource types
  • Dependency constraints
  • Resources spanning multiple resource groups

Azure error diagnostics dashboard


⚠️ Important Limitations

Be aware of these Azure constraints:

❌ Some resources cannot be moved

  • Classic resources
  • Certain App Service plans
  • Managed identities with dependencies

❌ Resources must move together

  • VNets + subnets
  • NICs + VMs
  • Disks + VMs

✔ Azure will block the move if dependencies are violated


✅ Best Practices Before Running

✔ Test on a single resource group first
✔ Export ARM templates as a backup
✔ Run during a maintenance window
✔ Validate networking dependencies
✔ Monitor activity logs during execution


? When Should You Use This Script?

This approach is ideal for:

  • Subscription consolidation
  • Tenant separation
  • Environment restructuring (Dev → Prod)
  • M&A cloud migrations
  • Billing realignment

? Final Thoughts

This script provides a clean, repeatable, and safe way to migrate Azure resources at scale using native tooling from Microsoft Azure.

It’s not magic—but with proper preparation, it can save hours or days of manual work.

If you found this useful, feel free to:
? Like
? Repost
? Share your migration war stories

Happy migrating ☁️?

----- SCRIPT ------

Write-Host "======================================"
$sourceSubId = "***"
$targetSubId = "***"
$location = "westeurope"               

Write-Host $sourceSubId
Write-Host $targetSubId

# Switch to source subscription
Write-Host Setting + $targetSubId
az account set --subscription $sourceSubId

# Get list of all resource group names
$resourceGroups = az group list --query "[].name" -o tsv

foreach ($rg in $resourceGroups) {
    Write-Host "======================================" -ForegroundColor Cyan
    Write-Host "Processing source RG: $rg" -ForegroundColor Yellow

    # Check if same-name RG already exists in TARGET subscription
    az account set --subscription $targetSubId

    $exists = az group exists --name $rg --output tsv

    if ($exists -eq "false") {
        Write-Host "  Creating target RG '$rg' in location $location ..." -ForegroundColor Green
        az group create --name $rg --location $location --subscription $targetSubId
    } else {
        Write-Host "  Target RG '$rg' already exists - skipping creation" -ForegroundColor Green
    }

    # Switch back to source to list resources
    az account set --subscription $sourceSubId

    # Get resource IDs from source RG
    $ids = az resource list --resource-group $rg --query "[].id" -o tsv

    if ($ids) {
        Write-Host "  Moving resources from '$rg' ..." -ForegroundColor Yellow
        az resource move `
            --destination-group $rg `
            --destination-subscription-id $targetSubId `
            --ids $ids

        if ($LASTEXITCODE -eq 0) {
            Write-Host "  SUCCESS" -ForegroundColor Green
        } else {
            Write-Host "  FAILED - see error above. Often due to dependencies or unsupported resource types." -ForegroundColor Red
        }
    } else {
        Write-Host "  No resources found in '$rg' - skipping move" -ForegroundColor Gray
    }
}

Write-Host "======================================" -ForegroundColor Cyan
Write-Host "All resource groups processed." -ForegroundColor White

comments powered by Disqus