VMware Horizon – Automating Template Creation & Maintenance

Release date: September 20th 2021

Welcome to my VMware Horizon series. In this session I will describe how I’ve automated the VMware Horizon Template creation and maintenance process. In my previous post: VMware Horizon – Windows 10 Desktop – From design to desktop pool, I described the manual procedure for doing this, which I find both time-consuming and tedious. Therefore, I was pleasantly surprised when I saw that VMware Digital Workspace Tech Zone had done the job and created a blog about this here: Using Automation to Create Optimized Windows Images for VMware Horizon VMs They’ve done a very thorough job and I used this as a guide when I did the set up myself. VMware have provided two scripts that helps automating VMware Horizon operations:

Using the Create Or Reset VMs script VMware has provided will work well enough, although, as my goal was to automate the complete process, I had to extend their script by adding the following features:

  • When the template vm is created, the VM was quite large in size. I needed to make a routine that would export this to an OVA-file, delete the original template vm, import OVA in Thin-mode to vSphere and clean up and delete the OVA-file on local storage.
  • Adjust Portgroup-setting on the imported vm, take a snapshot and push this to VMware Horizon

Before I got to the point of actual scripting, I set up the MS WDS/MDT infrastructure as follows:

I also had to setup VMware PowerCLI to be able to Automate VMware Horizon.

Once I had this in place, I could start creating my PowerCLI routines, but first I created the Credential Store XML-files to automate login to vSphere and Horizon, read more about the cmdlet here: New-VICredentialStoreItem

New-VICredentialStoreItem -User <user> -Password <user> -Host <server> -File C:\<your location.xml>

Export vm to OVA delete original VM:

Once the Create Or Reset VMs script has run, the VM is created and shut down. As show below the disk size is almost 40 GB.

Therefore I will have my script do the following procedures first.

  • Connect to my vCenter server using my predefined Credential Store Item
  • Remove any snapshots
  • Disconnect any ISO-files
  • Export the vm to an OVA file in the D:\Images folder on the Windows machine running the script. (Be sure to have enough disk space available!)
  • Remove the VM from vSphere
$ExportDir = "D:\Images\"
$viserver = "<FQDN vCenter Server>"
$viuser = Get-VICredentialStoreItem -File "D:\scripts\vc-01_sso.xml" -host $viserver
Connect-viserver -Server $viserver -User $viuser.user -Password $viuser.password
Get-Snapshot $vm | Remove-Snapshot -confirm:$false
Get-VM -Name $vm | Get-CDDrive | Set-CDDrive -NoMedia -confirm:$false
Get-VM -Name $vm | Export-VApp -Destination $ExportDir -Format OVA -Force
Get-VM -Name $vm | Remove-VM -DeletePermanently

PowerCLI Reference: Get-VICredentialStoreItem & Export-VApp

Import from OVA-File and change Portgroup

As with the export routine above, the routine below also demand that we are logged in to the vCenter server. This routine will import from the exported OVA file in thin format to vSphere. It will also make sure the VM gets connected to the correct port group.

$VMCluster = Get-Cluster -name "<vSphere Cluster Name>"
$VMCluster | Get-VMHost
$VMHOST = $VMCluster | Get-VMHost -Name <FQDN ESXi-host>
$ovaFile = "D:\Images\ovafile.OVA"
$FolderContainer = "<vSphere VM Folder>"
$vmDatastore = Get-Datastore -Name "<Datastore Name>"
# If using a DataStore Cluster:
# $VMDataStoreCluster = Get-DatastoreCluster -Name "DatastoreCluster Name"
$StorageFormat = "Thin"
$vmDestination = "<Name of destination VM>"

$VMHOST | Import-VApp -Source $ovaFile -Location $ClusterName  -InventoryLocation $FolderContainer -Datastore $VMDatastore -DiskStorageFormat $StorageFormat -Name $VMDestination -Force

$VMPortgroup = "<Portgroup Name>"
Get-VM -Name $VMDestination | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $VMPortgroup -Confirm:$false

PowerCLI Reference: Get-VMHost, Get-NetworkAdapter & Get-Datastore

After the shrinking-operation, the VM have a much smaller footprint, only 12,8 GB, which is quite acceptable.

Delete OVA file from local storage, take snapshot and Push to VMware Horizon

The final part of my script do the following:

  • Remove the OVA file I created above after successful import
  • Creates a snapshot
  • Connects to to my Horizon Connection server using my predefined Credential Store Item
  • Push the new VM and snapshot to my Desktop Pool
Remove-item $ovaFile -Force
$SnapshotName = "<Snapshot Name>"
New-Snapshot -VM $vmDestination -Name $SnapshotName
$HVServer = "<FQDN Horizon Connection Server>"
$HVUser = Get-VICredentialStoreItem -File "D:\scripts\admin_hz.xml" -host $HVServer
Connect-HVServer -Server $HVServer -User $HVUser.user -Password $HVUser.password
$PoolName = "<VMware Horizon Desktop Pool Name>"
Start-HVPool -SchedulePushImage -Pool $PoolName -LogoffSetting WAIT_FOR_LOGOFF -ParentVM $vmDestination -SnapshotVM $SnapshotName

PowerCLI Reference: New-Snapshot & Start-HVPool

As I’m in no sense an expert in PowerCLI-scripting or programming, the above scripting most probably lack somewhat in finesse, but it gets the job done, and that is good enough for me. If it is helpful for anyone else, excellent, because that is why i published this. Using this automated setup, it is quite easy to do maintenance tasks, as it is only a matter of updating the task sequence with new versions etc.

  • Day 2 Maintenance:
    • Update VMware Tools
    • Change Operating System
    • Add new VMware Agents

VMware Digital Workspace Tech Zone: Using Automation to Create Optimized Windows Images for VMware Horizon VMs

VMware Horizon planning, deployment etc.

Disclaimer: Every tips/tricks/posting I have published here, is tried and tested in different it-solutions. It is not guaranteed to work everywhere, but is meant as a tip for other users out there. Remember, Google is your friend and don’t be afraid to steal with pride! Feel free to comment below as needed.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: