Update : updated script to support Azure Az powershell module.
You may have come across situations where you want to move an Azure VM from one Resource Group to another. Sounds familiar, right? Luckily, Microsoft has provided a very cool and easy wizard based tool through the Azure Portal. Refer here for more information on that. However, is that so easy? Not really? During the move, it checks several dependencies and decides if the move can be initialized.
Now, think about a situation, where you need to move Azure VMs from one Subscription to another. Getting complex, uh! As you are moving the VM to a different subscription and in turn moving to a different Virtual Network/ Subnet, it is never going to pass the checks before move. What will you do now?
I came across this situation while working on a Project to move a list of Azure VMs to a different Subscription. These VMs were created in wrong Subscription first time. However, we only realized, when we already deployed and configured applications on them and don’t really like to reinstall and reconfigure on new VMs in the correct Subscription.
Here is my solution to smoothly move those VMs to the correct Subscription. I am not going to talk about pre-working the Virtual Network, Subnet and Network Security Group to meet Network requirements. Make sure you took care of those before starting the actual move. In addition, I wanted to take highest caution while moving those VMs. So, in my script, I will not delete those Source VMs. rather, keep those in deallocated state. We can decide fate of those once Application Team confirms new VMs working perfectly as expected.
So, just to summarize the steps this script will perform :
- Collect all details of the source VM including Disks, Size, Tags etc.
- Check if the VM is already deallocated, if not, deallocate it.
- Copy all Disks associated with the VM to the Target Subscription Resource Group.
- Check if the provided Availability Set is already created in Target subscription. If not, create a new Availability Set with provided Name.
- Deploy new Azure VM with same configuration of the source VM.
- Attach all copied Data disks to the new VM.
This script is currently set for one VM at a time. However, feel free to modify with a loop and reading VM names from a source file or any other enhancement you would like to make. Hope it will help you with your migration challenge and don’t forget to share your comments and suggestions. Oh yes! Don’t forget to delete those deallocated VMs 🙂 .
Make sure you have Azure Az Module installed and imported on the system before running this script. For more information on how to install and configure Az module refer following article : https://docs.microsoft.com/en-us/powershell/azure/new-azureps-module-az?view=azps-5.5.0
Param(
[Parameter(Mandatory=$true)]
[String]$sourceSubscriptionId,
[Parameter(Mandatory=$true)]
[String]$targetSubscriptionId,
[Parameter(Mandatory=$true)]
[String]$sourceResourceGroupName,
[Parameter(Mandatory=$true)]
[String]$targetResourceGroupName,
[Parameter(Mandatory=$true)]
[String]$targetVnetName,
[Parameter(Mandatory=$true)]
[String]$targetVnetResourceGroupName,
[Parameter(Mandatory=$true)]
[String]$targetSubnetName,
[Parameter(Mandatory=$true)]
[String]$sourceVMName,
[Parameter(Mandatory=$true)]
[String]$asName
)
try{stop-transcript|out-null}
catch [System.InvalidOperationException]{}
#region - set all required variables with values
$allDataDisks = @()
$date = Get-Date -UFormat "%m-%d-%Y"
$currentDir = $(Get-Location).Path
$tFile = "$($currentDir)\Migrate_$($sourceVMName)_Transcript_$($date).txt"
$dFormat = "%m-%d-%Y %H:%M:%S"
#end-region
#region - Capture all required information from the VM Details to be used while deploying new VM"
Start-Transcript -Path $tFile -Append -NoClobber
$selectSubscription = Select-AzSubscription -SubscriptionId $sourceSubscriptionId
$vmDetails = Get-AzVM -Name $sourceVMName -ResourceGroupName $sourceResourceGroupName
$vmTags = $vmDetails.Tags
$vmSize = $vmDetails.HardwareProfile.VmSize
$osDiskName = $vmDetails.StorageProfile.OsDisk.Name
$dataDisks = $vmDetails.StorageProfile.DataDisks
if([string]::IsNullOrEmpty($dataDisks)){
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: No Data disk attached to VM $sourceVMName" -ForegroundColor Green
}else{
ForEach($dataDisk in $dataDisks){
$allDataDisks += $dataDisk.Name
}
}
#end-region
#region - Stop and deallocate the VM before changing the size
$vmPowerStatus = (Get-AzVM -Name $sourceVMName -ResourceGroupName $sourceResourceGroupName -Status).Statuses.code
if($vmPowerStatus -contains "PowerState/running"){
$vmRunning = $true
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: VM is running, Stopping and Deallocating VM $sourceVMName" -ForegroundColor Green
$stopVm = Stop-AzVM -Name $sourceVMName -ResourceGroupName $sourceResourceGroupName -Force -Confirm:$false
do
{
Start-Sleep -Seconds 5
$vmPowerStatus = (Get-AzVM -Name $sourceVMName -ResourceGroupName $sourceResourceGroupName -Status).Statuses.code
}while($vmPowerStatus -contains "PowerState/running")
}else{
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: VM $sourceVMName is already stopped and deallocated" -ForegroundColor Green
}
#end-region
#region - copying OS Disk
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: Copying VM Disks from source subscription to destination subscription" -ForegroundColor Green
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: Copying OS Disk from source subscription to destination subscription" -ForegroundColor Green
$managedDisk= Get-AzDisk -ResourceGroupName $sourceResourceGroupName -DiskName $osDiskName
$targetLocation = $managedDisk.Location
$osType = $managedDisk.OsType
$selectSubscription = Select-AzSubscription -SubscriptionId $targetSubscriptionId
$diskConfig = New-AzDiskConfig -SourceResourceId $managedDisk.Id -Location $targetLocation -CreateOption Copy -Tag $vmTags
$newVMDisk = New-AzDisk -Disk $diskConfig -DiskName $osDiskName -ResourceGroupName $targetResourceGroupName
#end-region
#region - copying Data Disk(s)
if(!([string]::IsNullOrEmpty($allDataDisks))){
foreach($disk in $allDataDisks){
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: Copying VM Disk $disk from source subscription $sourceSubscriptionId to destination subscription $targetSubscriptionId" -ForegroundColor Green
$selectSubscription = Select-AzSubscription -SubscriptionId $sourceSubscriptionId
$managedDisk= Get-AzDisk -ResourceGroupName $sourceResourceGroupName -DiskName $disk
$selectSubscription = Select-AzSubscription -SubscriptionId $targetSubscriptionId
$diskConfig = New-AzDiskConfig -SourceResourceId $managedDisk.Id -Location $targetLocation -CreateOption Copy -Tag $vmTags
$newVMDisk = New-AzDisk -Disk $diskConfig -DiskName $disk -ResourceGroupName $targetResourceGroupName
}
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: All Disks copied" -ForegroundColor Green
}
#end-region
#region - create availability set
$selectSubscription = Select-AzSubscription -SubscriptionId $targetSubscriptionId
$asDetails = Get-AzAvailabilitySet -Name $asName -ResourceGroupName $targetResourceGroupName -ErrorAction SilentlyContinue
if([string]::IsNullOrEmpty($asDetails)){
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: Deploying new Availablity Set $asName" -ForegroundColor Green
$asDetails = New-AzAvailabilitySet -ResourceGroupName $targetResourceGroupName -Name $asName -Location $targetLocation -Sku 'Aligned' -PlatformUpdateDomainCount 5 -PlatformFaultDomainCount 3 -Tag $vmTags
}
#end-region
#region - create a new VM in target subscription with disk copied from source subscription
$selectSubscription = Select-AzSubscription -SubscriptionId $targetSubscriptionId
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: Creating a new Virtual Machine in subscription $targetSubscriptionId using those copied Disk(s)" -ForegroundColor Green
$targetVM = New-AzVMConfig -VMName $sourceVMName -VMSize $vmSize -AvailabilitySetId $asDetails.Id
if($osType -eq 'Windows'){
$managedOSDisk= Get-AzDisk -ResourceGroupName $targetResourceGroupName -DiskName $osDiskName
$targetVM = Set-AzVMOSDisk -VM $targetVM -ManagedDiskId $managedOSDisk.Id -CreateOption Attach -Windows
$vnet = Get-AzVirtualNetwork -Name $targetVnetName -ResourceGroupName $targetVnetResourceGroupName
$snet = Get-AzVirtualNetworkSubnetConfig -Name $targetSubnetName -VirtualNetwork $vnet
Write-Host $snet
$nicName = "$($sourceVMName)_nic01"
Write-Host $nicName
$nic = New-AzNetworkInterface -Name $nicName -ResourceGroupName $targetResourceGroupName -Location $targetLocation -Subnet $snet -Tag $vmTags -Force -Confirm:$false
$targetVM = Add-AzVMNetworkInterface -VM $targetVM -Id $nic.Id
$targetVM = Set-AzVMBootDiagnostics -VM $targetVM -Disable
$newVM = New-AzVM -VM $targetVM -ResourceGroupName $targetResourceGroupName -Location $targetLocation -Tag $vmTags
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: Virtual Machine $sourceVMName in subscription $targetSubscriptionId has been deployed" -ForegroundColor Green
}
#end-region
#region - attach data disks to new VM
if(!([string]::IsNullOrEmpty($allDataDisks))){
$lun = 1
foreach($disk in $allDataDisks){
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: Attaching Data Disk $disk" -ForegroundColor Green
$selectSubscription = Select-AzSubscription -SubscriptionId $targetSubscriptionId
$dataDiskInfo = Get-AzDisk -DiskName $disk -ResourceGroupName $targetResourceGroupName
$targetVMDetails = Get-AzVM -ResourceGroupName $targetResourceGroupName -Name $sourceVMName
$targetVMDetails = Add-AzVMDataDisk -VM $targetVMDetails -Name $disk -CreateOption Attach -ManagedDiskId $dataDiskInfo.Id -Lun $lun
$updateVM = Update-AzVM -ResourceGroupName $targetResourceGroupName -VM $targetVMDetails
$lun++
}
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: All Disks attached" -ForegroundColor Green
}
#end-region
Write-Host "[$(Get-Date -UFormat $dFormat)] INFORMATION: Virtual Machine $sourceVMName has been successfully moved from $sourceSubscription to target subscription $targetSubscriptionId" -ForegroundColor Green
Write-Host (("=") * 100)
Stop-Transcript
Download above script and save it with a .ps1 file extension. After that, open powershell console and login to your Azure account using – Login-AzAccount. It will prompt you to enter your Azure credentials.
After you login to Azure, run the powershell script saved in previous step.