Powershell - Move Azure resources between resource groups in bulk
function moveResources { [CmdletBinding()] param( [bool]$onlyValidate, [string]$sourceSub, [string]$targetSub, [array]$rgNames, [array]$excludedResourceTypes ) begin { $startDate = Get-Date #Setup block Write-Host "Starting script...$($startDate)" # Connect to your Azure account Connect-AzAccount Set-AzContext -Subscription $targetSub # Create or update resource groups in the target subscription $rgNames | ForEach-Object { New-AzResourceGroup -Name $_ -Location "westeurope" -Force } # Initialize an empty array for storing resource group information $rgs = @() # Build an array of objects containing resource group details foreach ($rg in $rgNames) { $rgs += [pscustomobject]@{ rgName = $rg oldId = "/subscriptions/$sourceSub/resourceGroups/$rg" newId = "/subscriptions/$targetSub/resourceGroups/$rg" } } } process { Set-AzContext -Subscription $sourceSub # Process each resource group in parallel with a throttle limit of 5 $rgs | Foreach-Object -ThrottleLimit 5 -Parallel { # Retrieve resources from the source resource group $rgResources = (Get-AzResource -ResourceGroupName $_.rgName) | Where-Object { ($_.ParentResource -eq $null -and $_.ResourceType -notin $using:excludedResourceTypes) } # Get unique resource provider namespaces $uniqueResourceProvider = $rgResources.Type | Get-Unique $formattedUniqueResourceProviders = $uniqueResourceProvider | Split-Path -Parent # Set the Azure context to the target subscription Set-AzContext -Subscription $using:targetSub # Register resource providers $formattedUniqueResourceProviders | ForEach-Object { Register-AzResourceProvider -ProviderNamespace $_ -ErrorAction SilentlyContinue } # Create an array of resource IDs $resourceIds = @( $rgResources.ResourceId ) # Output resource details $rgResources | Format-Table # Invoke resource action to validate resource move, stop thread if it throws exception. Invoke-AzResourceAction -Action validateMoveResources -ResourceId $_.oldId -Parameters @{ resources = $resourceIds targetResourceGroup = $_.newId } -Force -ErrorAction Stop # Output a message indicating successful validation Write-Host "$($_.rgName) Was validated ok by validateMoveResources!" if ($using:onlyValidate -ne $true) { Write-Host "Moving resources in $($_.rgName)!" Read-Host "Press Enter to continue..." # Move the resources to the target resource group Move-AzResource -DestinationResourceGroupName $_.rgName -DestinationSubscriptionId $using:targetSub -ResourceId $rgResources.ResourceId -Force } } } end { $endDate = Get-Date Write-Host "Action complete..." Write-Host "Started $($startDate)" Write-Host "Ended $($endDate)" $delta = New-TimeSpan -Start $startDate -End $endDate Write-Host "Total time: $($delta.Minutes)" } } # Define source and target subscription ID $sourceSub = "" $targetSub = "" # Create an array of resource group names $rgNames = ( "rprg01", "rprg02", "rprg03", "rprg04" ) $excludedResourceTypes = @( 'microsoft.insights/metricalerts' ) moveResources -sourceSub $sourceSub -targetSub $targetSub -rgNames $rgNames -excludedResourceTypes $excludedResourceTypes -onlyValidate $true