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