Using CloudClient to bulk change vRA Reservations

What happens when you have ~500 VMs in vRealize Automation that need to have their reservations changed? You figure out a way to script that tedious process. (This will provide some background and more details on a recent VMware Communities post.)

I first started looking at what I was most familiar with - PowervRA - a great community supported, PowerShell wrapper for the vRA API stack. Unfortunately, PowervRA does not offer reservation manipulation - only reporting.

CloudClient is a Java application wrapper for the vRA API. Bulk changing reservations is one use case, but several other uses exist in the documentation from that download site.

To use CloudClient in a wrapper script, we must first save our password by interactively launching CloudClient and executing the login keyfile command. This command specifies an output location for the encrypted file and prompts for a password. Most CloudClient activities require authentication with both vRA and vRA’s IaaS component. CloudClient

With the files saved, I can then create a CloudConfig.properties file

# CloudClient.Properties File used to automatically login to CloudClient and it should be located in the same directory as lib and bin folders.
# Passwords can be provided or simply encrypted to a file, use command:
# login keyfile --file mypass.txt --password mypaasword
#vra_server : vRealize Automation Virtual Appliance
vra_server=vra-server.mycorp.local
#vra_tenant : Tenant Name, defaults to vsphere.local
vra_tenant=mytenant
#vra_username : Username
vra_username=pezhore
#vra_password : Password
#vra_password=
#vra_keyfile : Encrypted file location of password
vra_keyfile=vra_login.enc
# Some Administrator level API calls require Windows Authentication to communicate to the vCAC Infrastructure Service Components
#vra_iaas_server : Infrastructure Server (Windows Authentication) where IaaS Web Server node (Model Manager Web) is running, defaults to finding the name dynamically from vCAC
vra_iaas_server=vra-web.mycorp.local
#vra_iaas_username : Windows username in the form user@domain
vra_iaas_username=vra_iaas@mycorp.local
#vra_iaas_password : Windows password
#vra_iaas_password=
#vra_iaas_keyfile : Encrypted file location of Windows password
vra_iaas_keyfile=iaas_login.enc

With the CloudConfig.properties file in place at the same level as the ./bin folder, I can execute my commands directly from PowerShell

.\bin\cloudclient.sh vra machines change reservation --ids "restest01" --reservationName "Windows\ -\ 02"

This works great for single VMs, but for migrating several hundred - and ensuring the VMs you’re requesting are indeed on the wrong reservation takes a bit more effort. If I connect to vRA with PowervRA, I can gather a list of VMs on the “old” reservation and pass that list of applicable servers to the CloudClient command line. Alternatively, if I provide a list of VMs to PowerShell, I can validate which are on the old reservation and update only those.

The final source can be found below:

<#
.SYNOPSIS
Script that changes vRA managed machine reservations.
.DESCRIPTION
Leveraging an existing connection to vRA and a pre-configured cloudclient installation,
find all managed machines on a given OldReservation and attempt to change them to a
given NewReservation.
.EXAMPLE
PS C:\> Set-vRAMachineReservation.ps1
Uses defaults to change all machines on the "Windows - 01" reservation
to the "Windows - 02" reservation
.EXAMPLE
PS C:\> Set-vRAMachineReservation.ps1 -OldReservation "Res - 01" -NewReservation "Res - 02"
Finds all vRA machines on the "Res - 01" reservation and changes them to the
"Res - 02" Reservation
.EXAMPLE
PS C:\> Set-vRAMachineReservation.ps1 -TargetVMs "Test01","Test02","Test03"
Attempts to find the three test VMs in vRA, and if they are a member of the default OldReservation,
"Windows - 01", will attempt to change their reservation to the default NewReservation,
"Windows - 02"
.EXAMPLE
PS C:\> Set-vRAMachineReservation.ps1 -TargetVMs "Test01" -OldReservation "Res - 01"
Attempts to find the Test01 VM in vRA, and if it is a member of the "Res - 01" reservation, will
attempt to change its reservation to the default NewReservation, "Windows - 02"
.PARAMETER OldReservation
The existing reservation that should be changed
.PARAMETER NewReservation
The new reservation for all machines found on existing
.PARAMETER TargetVMs
An array of VM names to target for reservation changes
#>
[CmdletBinding()]
param(
[System.String] $OldReservation = "Windows - 01",
[System.String] $NewReservation = "Windows - 02",
[ValidateScript( {
Test-Path -Path $_ -Type Container
})]
[System.String] $CloudClientPath = "C:\temp\cloudclient\4.4.0\VMware_vRealize_CloudClient-4.4.0-5511232",
[System.String[]] $TargetVMs
)
process {
# CloudClient doesn't like spaces. We need to escape them
$NewReservation = $NewReservation -replace " ", "\ "
# Initialize Values
$AllResources = @()
$Page = 1
# If we're not asking for a specific set of VMs, grab everything.
if (! $TargetVMs) {
<#
vRA uses pagnation to restrict how many results are returned. It will return *at most* 100
results for a given "page". To fully get all resources, we must leverage a $Count variable
to track how many results we have and increment the page for every time we get back 100
results.
#>
do {
# Write out some progress
$ProgressSplat = @{
Activity = "Analyzing Resources"
Status = "Current page $Page"
}
Write-Progress @ProgressSplat
# Get the vRA Resources (e.g. Machines) for this page. WithExtendedData includes the current
# reservation
$Group = Get-vRAResource -Page $Page -WithExtendedData
# Grab the count of how many objects were retrieved
$Count = $Group.count
# Increment our page counter
$Page ++
# Add this group to the larger AllResources array
$AllResources += $Group
} while ($Count -ge 100)
}
else {
# Loop through every individual vm in the TargetVMs list
foreach ($TargetVM in $TargetVMs) {
$AllResources += Get-vRAResource -Name $TargetVM
}
}
# Initialize our list of machines
$List = @()
# Parse every resource we retrieved above
foreach ($Resource in $AllResources) {
# If this resource's reservation matches the old reservation, add it to the list
if ($Resource.data.machinereservationname -match $OldReservation) {
Write-Information -MessageData "$OldReservation participant: $($Resource.name)" -InformationAction Continue
$List += $Resource.name
}
}
# CloudClient needs the MachineIDs as a comma separated list
$MachineIDs = $List -join ","
# Call the cloudclient executable, passing in the MachineIDs and New Reservation
$CloudClientBat = Join-Path -Path $CloudClientPath -ChildPath "\bin\cloudclient.bat"
& $CloudClientBat vra machines change reservation --ids $MachineIDs --reservationName $NewReservation
}