Migrate to VMC using HCX and Powershell

HCX is an application mobility platform that is available at no extra cost for VMC customers. It can perform a number of functions, most notably VM migrations, Disaster Recovery and L2 Network Extensions between sites that are paired within HCX. I’ve deployed VMC/HCX a number of times as a POC for customers that are evaluating the service. It is very powerful to show customers how you can easily migrate VMs or networks into VMC with just a few mouse clicks. For larger migration projects though, automation and scripting can make a huge difference when you need to churn through hundreds of VMs.

Every migration project is different, and customer expectations have an effect on how to best use the tools at your disposal. Using one large migration project as a proving ground, I was able to build some automation using Powershell that greatly enhanced my ability to leverage HCX at scale.

Not all migrations are the same

HCX comes with a service simply called Migration, but for this project it wasn’t necessarily the best tool for the job. I was tasked with migrating hundreds of VMs into VMC split up into a number of migration waves that were carved out ahead of time. That meant I had a need to make sure all VMs were synced into VMC prior to each migration wave. With a lot of other moving parts, there was also the risk that scheduled waves could get pushed back due to other factors. Therefore it wasn’t easy enough to just use bulk migration and set a number of maintenance windows, since our windows weren’t 100% set in stone.

Another factor that meant not relying on maintenance windows was application testing. These migrations would happen with multiple app owners on the call and a requirement to turn down apps and databases prior to migrations, which usually varied in time each wave. That meant I needed to be a bit more manual when it came to which VMs could migrate and when. HCX Migration does have some nice automation features in shutting down the source VM for you and spinning up into VMC without manual intervention, but the business requirements for this project forced me to be a bit more agile.

That led me to utilizing the Disaster Recovery service within HCX for this project. There are pros and cons to every decision, and certainly there are some cons that came with HCX DR. One big con was that it doesn’t automate the shutdown of source VMs after they recover into VMC (what I considered a migration), but there can be a script for that! What it does do is allow you to sync a large number of VMs ahead of time, manage VMs very granularly within the service, and provide flexibility as to when you kick off the migrations. In this case, the pros outweighed the cons for me as I prepared for the project.

Diving into HCX Powershell

After I settled on the big picture, I dug a bit into what Powershell capabilities were available for HCX. A VMware blog post on Getting Started with the HCX Module was a great jumping off point. I was able to hop onto a jump box and upgrade to the latest Powershell. Then I simply installed PowerCLI: install-module VMware.PowerCLI

Once that completed, I was able to run get-command -module VMware.vimautomation.HCX to see that the HCX module was available and view the commands that I could work with. I did a lot of get-help to check out what the commands did, but you can also find all of these commands in the VMware PowerCLI Cmdlets Reference.

Learning the Process

Once I had access to HCX Powershell, I needed to understand the process I wanted to implement. Like many other processes that can be automated, I started with manual testing and worked my way up. I knew what it took to use the GUI and select one VM to replicate into VMC by protecting it with HCX DR. I then used HCX Powershell to run individual commands and make sure that they functionally worked the same as the manual process. Most of the time this was the case, but I certainly learned a few minor quirks along the way.

Then I sketched out what my workflow would be for the end-to-end process. One of the big things I began to tackle was on the front end. The list of VMs in scope to migrate all came from the customer’s CMDB and was gathered into a spreadsheet that grouped them by migration wave, server type, etc. I knew that I wanted to sync batches of VMs at a time as to not overwhelm the system with every VM at once. I also wanted to run some test cases to make sure my script was solid.

In the testing process, I learned that mistakes do happen. The list had some duplicate names and HCX would error out if it tried to add a VM that was already in there. Also, as the project went along, VMs would get pushed back to later waves or even pulled forward. I wanted to be able to easily make adjustments on the fly, and factored that into the creation of a script used to replicate VMs.

Turning it into Code

This process led me to developing a script that I used to sync all of the VMs into VMC. While this was just one step in the overall migration procedure, it did save a ton of manual effort in digging through the HCX console for hundreds of VMs. I removed comments for the sake of space (I did learn something in college all those years ago!), but at a high level this script:

– Requires parameters for the HCXserver, vCenter server, creds to connect to each, a CSV file list of VMs that will be synced (changes with each run) and a static CSV file that contained network mappings. The network mappings lists the port group name for an on-prem network and its corresponding network in VMC (in this case HCX extended networks).

– Sets variables that define all of the target resources to be used. This environment contained three different VMC clusters, so as one cluster would fill up I was able to switch the destination to another cluster.

– Grabs a list of all the VMs available to HCX as well as all of the existing replications within HCX DR for comparison against the inputted CSV file.

– For every VM in the CSV file, first check to see if it is already replicating to VMC. If so, log the VM name and mark it as replicating. If not, check to see if the VM actually exists in the source vCenter. If it doesn’t exist, log the VM name and mark it as missing.

– If all conditions are met, grab the on-prem network port group name from the vSphere VM object (the HCX VM object doesn’t contain network names). For every vnic attached to the VM, set the source and destination network names. Save the vnic(s) into a network mapping variable.

– Set a new replication variable for the VM being processed and all of the corresponding values required by New-HCXReplication, then start the replication.

– Output the list of VMs processed by the script to a TXT file that shows if the VM was successful, already replicating in HCX, or missing. This allowed me to go back and spot check errors, and if need be re-run the script against a CSV file of VMs that may have somehow been missed the first time.

param(
    [Parameter(Mandatory=$true)] [string] $HCXserver,
    [Parameter(Mandatory=$true)] [string] $vCenter,
    [Parameter(Mandatory=$true)] [string] $user,
    [Parameter(Mandatory=$true)] [string] $pass,
    [Parameter(Mandatory=$true)] [string] $csvVM,
    [Parameter(Mandatory=$true)] [string] $csvNetwork
)

Connect-HCXServer -Server $HCXserver -User $user -Password $pass
Connect-VIServer -Server $vCenter -User $user -Password $pass

$SourceSite = Get-HCXSite -server $HCXserver
$DestSite = Get-HCXSite -destination

$RPOIntervalMinutes = 15
$SnapshotIntervalMinutes = 60
$SnapshotNumber = 1

$TargetDatastore = Get-HCXDatastore -site $DestSite -name 'WorkloadDatastore (2)'
$TargetCompute = Get-HCXContainer -site $DestSite -Name 'Compute-ResourcePool-3'
$TargetFolder = Get-HCXContainer -site $DestSite -name 'SDDC-Datacenter'

$vmlist = Import-Csv -Header 'Name' -Path $csvVM
$netmap = Import-Csv -Path $csvNetwork

$vms = Get-HCXVM -site $SourceSite
$vSphere = Get-VM
$destnetworks = Get-HCXNetwork -site $DestSite
$currentreps = Get-HCXReplication

$reps_out = @()

foreach($row in $vmlist){
    $vm = $vms | where-object {$_.Name -eq $row.Name}
    $isreplicating = $currentreps | where-object {$_.VM -match $row.Name}
	
	if ($isreplicating -eq $null){
	    
	    if ($vm -ne $null){
	       $vSphereVM = $vSphere | where-object {$_.Name -eq $vm.Name}
		   $sourceref = Get-VirtualPortGroup -vm $vSphereVM
		   $SourceNetwork = Get-HCXNetwork -Name $sourceref.name -site $SourceSite
	       $NetworkMapping = @()
   
           foreach ($network in $SourceNetwork){
               $netmapref = $netmap | where-object {$_.Source -eq $network.Name}
			   $DestNetwork = $destnetworks | where-object {$_.Name -eq $netmapref.Dest}
		       $NetworkMapping += New-HCXNetworkMapping -SourceNetwork $network -DestinationNetwork $DestNetwork
		    }
			       
	        $rep = New-HCXReplication -DestinationSite $DestSite `
             -NetworkMapping $NetworkMapping `
             -RPOIntervalMinutes $RPOIntervalMinutes `
             -SnapshotIntervalMinutes $SnapshotIntervalMinutes `
             -SnapshotNumber $SnapshotNumber `
             -SourceSite $SourceSite `
             -TargetDatastore $TargetDatastore `
             -TargetComputeContainer $TargetCompute `
             -TargetDataCenter $TargetFolder `
             -NetworkCompressionEnabled $true `
             -VM $vm
       
	       Start-HCXReplication -Replication $rep -Confirm:$false
	       $reps_out += $rep
	    }
	    else{
	       $reps_out += $row.Name + ' is missing'
		}
	}
	else{
	   $reps_out += $row.Name + ' is already syncing'
	}
}

$file = '.\vms-added.txt'
$reps_out | Add-Content -Path $file

This was just one aspect of the overall migration process, but it saved me A TON of time and effort. There are many other tedious aspects of a cloud migration, so I tried to eliminate as many manual steps like this as I could with automation.

As I continue to find other uses for HCX Powershell and roll them out for future projects, I will be sure to update this blog, in addition to a GitHub repo I plan to make available in the near future. Happy automating…

Leave a Reply

Your email address will not be published. Required fields are marked *