Toward the end of the year, one of my customers mentioned that they were reorganizing the resources in their subscriptions. This isn’t unusual for any enterprise that is further along in their cloud adoption journey and is an excellent measure of increased maturity. For those of you reading this with AWS expertise know that this is a effectively an automation exercise of shutting down VMs, copying data, and spinning up new instances in the target region/account. Microsoft recognized this as a problem and announced the Azure Resource Mover at Ignite 2020. In this post, I’ll describe how my customer used the Resource Mover and the edge cases that we encountered along the way.

The subscription and resource group hierarchy that my customer configured initially was sound. They ensured that delegation and segregation of responsibility of resources was inherent in their design. Keyboard jockeys (e.g., developers, cloud engineers, etc.) could spin up virtual machines without having to tinker with the vnet dependencies. Depending on the experience of the user, they could instantiate compute using the portal, ARM template, or Terraform with ease as long as they pointed to the right vnet/subnet to create the associated network interface. Well, no good deed goes unpunished.

At the current time, you “cannot move a virtual machine to a new subscription unless all of its dependent resources are located in the same resource group and are moved together”.1 This was disappointing to read but it made sense. “A subscription represents a grouping of Azure resources”2 and if a running VM had dependencies spread across two different groupings it would cause a myriad of issues. With the hurdle identified, we tinkered with a variety of solutions and eventually landed on one that achieved the intended outcome.

The solution we came up will sound familiar to some of you, and I know you’re already starting to snicker. Yes, we ended up deciding to stop the source VMs, duplicate their data, move them to the new subscription, and then spin up new instances with the copied data. Yes, the script I wrote did use the Resource Mover to move the copied disks.

With that said, the last time I wrote a PowerShell script was in 2016 when I was at CyberArk so the rust was thick. If there are blatant best practice violations in this, please let me know.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# Define variables for script
$SourceSubscription = ""
$SourceResourceGroupName = ""

$TargetSubscriptionId = ""
$TargetResourceGroupName = ""

$ListOfVMs = ""

# Set Source Subscription
Select-AzSubscription -Subscription $SourceSubscription
ForEach( $VMName in $ListOfVMs)
{
    # Get VM Details
    $VM = Get-AzVM -ResourceGroupName $SourceResourceGroupName -Name $VMName -Status
    $VMObject = Get-AzVM -ResourceGroupName $SourceResourceGroupName -Name $VMName
    $Disks = Get-AzVM -ResourceGroupName $SourceResourceGroupName -Name $VMName -Status | Select-Object -ExpandProperty Disks

    # Stop VM in preparation
    Stop-AzVM -Force -ResourceGroupName $SourceResourceGroupName -Name $VMName

    # Get VM Disk objects
    $VMOSDiskName = $Disks | Where-Object Name -NotLike '*Data*'
    $VMDataDisks = $Disks | Where-Object Name -Like '*Data*'

    # Detach data disk from VM
    if( $VMDataDisks.count -gt 0 )
    {
        ForEach($VMDataDisk in $VMDataDisks)
        {
            Remove-AzVMDataDisk -VM $VMObject -DataDiskNames $VMDataDisk.Name
            Update-AzVM -ResourceGroupName $sourceResourceGroupName -VM $VMObject
            # Move the data disk to the new destination
            $DataDiskObject = Get-AzResource -Name $VMDataDisk.Name -ResourceGroupName $SourceResourceGroupName
            Move-AzResource -Force -DestinationResourceGroupName $TargetResourceGroupName -DestinationSubscriptionId $TargetSubscriptionId -ResourceId $DataDiskObject.ResourceId
        }
    }

    # Get OSDisk Object
    $VMOSDisk = Get-AzDisk -ResourceGroupName $SourceResourceGroupName -Name $VMOSDiskName.Name

    # Build snapshot Configuration of OS Disk
    $OSDiskSnapshotConfig = New-AzSnapshotConfig -SkuName $VMOSDisk.Sku.Name -OsType $VMOSDisk.OsType -DiskSizeGB $VMOSDisk.DiskSizeGB -Location $VMOSDisk.location -CreateOption copy -SourceUri $VMOSDisk.Id

    # Define snapshot name
    $OSSnapshotName = "snapshot-"+$VM.Name

    # Create snapshot using the above configuration
    $OSDiskSnapshot = New-AzSnapshot -ResourceGroupName $VMOSDisk.ResourceGroupName -SnapshotName $OSSnapshotName -Snapshot $OSDiskSnapshotConfig

    # Create disk configuration
    $OSDiskConfig = New-AzDiskConfig -SkuName $VMOSDisk.Sku.Name -OsType $VMOSDisk.OsType -DiskSizeGB $VMOSDisk.DiskSizeGB -Location $VMOSDisk.location -CreateOption copy -SourceUri $OSDiskSnapshot.Id

    # Create name for OS disk copy
    $OSDiskCopyName = "osdisk-"+$VMObject.Location+"-"+$VMOSDisk.Name

    # Create copy of OS disk
    $OSDiskCopy = New-AzDisk -ResourceGroupName $VMOSDisk.ResourceGroupName -DiskName $OSDiskCopyName -Disk $OSDiskConfig

    # Move the disk to the new destination
    Move-AzResource -Force -DestinationResourceGroupName $TargetResourceGroupName -DestinationSubscriptionId $TargetSubscriptionId -ResourceId $OSDiskCopy.Id
}

The link to the source is here.

For those of you that read through it, the script is straight forward. The list of VMs variable is intended to be a comma delimited string of VM resource IDs that are looped through starting on line 12. I struggled a bit with pulling the disk data from the PowerShell objects and would love feedback if there is a more elegant solution. As most customers, the VMs they deployed had a varying number of data disks attached and so I needed to make sure that those were handled appropriately which is the reason for the conditional and subsequent loop starting on line 27. Once the data disks were handled, I move on to the OS disk starting on line 39 where I take a snapshot, make a clone from it and then ultimately move the clone to its intended destination.

The automation for instantiating the new VM with the cloned OS disk and the associated data disks is left open so that it can be handled via ARM or Terraform.

Overall, I was pleased that we were able to find a solution that worked and addressed my customer’s needs while being able to use the Resource Mover.