This is script is the culmination of many hours of work at night and on the weekends. This has been a personal goal of mine was to automate as much of the daily job that I can. And this is where I chose to start. This will allow me to start becoming Proactive instead of reactive.
So here I go! I give you the ability to do full monthly or weekly or daily update cycles of your clone pools from start to finish 100% scripted. Yes, you are reading that right. With this you are able to do software installs based on SCCM and/or Windows updates, Do 3rd Party patching, shut down the VM take a snapshot, clean out the old snaps, power it back on and prepare for the process to start next month all while updating ServiceNow Incident and Change. Yes, that sounds like a ton of stuff and it really is.
Currently, this has been tested and is running on production systems Windows 7, 8, and 10 64bit systems only. I have also tested the code on Horizon 7.4,7.5,7.6, and 7.7. Also tested with vSphere 6.5 and 6.7. Anything outside of this has not been tested.
Time to get down to the features. What is does this script do? Well, here you go:
- Captures Service accounts and passwords for Horizon, and Service Now
- Connects to the Horizon Connection server and pulls an inventory of pools and get the info for the Master Images, Pool Names, vCenter Info.
- Create a ServiceNow Incident and a ServiceNow Change.
- Builds out Installation and Shutdown script on a Share location that distributes the scripts to the VDI Master images.
- Runs the Install Updates function.
- Runs a report of what the current installed 3rd party software is before updating
- Install updates from SCCM
- Install 3rd Party from SCCM (Requires Automatic install of Software)
- Install Windows updates (If not managed by SCCM)
- Install 3rd Party updates (If not managed by SCCM, and requires you to find appropriate scripts to install updates. There are many things on GitHub for the installs.)
- Run a report of what the current installed 3rd party software is after updating.
- Run a report of what the installed updates are since the last recompose
- Create or Update a custom registry key to store the Corporate Build Date.
- Create or Update a custom registry key to store the Last Recompose Date
- Create or Update a custom registry key to store the Last Update Date.
- Create or Update a custom registry key to store the Pool Assignment Type.
- Create or Update a custom registry key to store the Pool Name.
- Create or Update a custom registry key to store the Pool Provision Type
- Create or Update a custom registry key to store the Pool Type.
- Create or Update a custom registry key to store the Pool Name.
- Create or Update a custom registry key to store the Windows Build Version.
- Create or Update a custom registry key to store the Windows Revision.
- Create or Update a custom registry key to store the Windows Version.
- Reboot the master image, it will run the cleanup script. This will clean up the master and make it ready to be cloned.
- Checks the version of windows. (This is used for the optimizer at the end.)
- Create or Update a custom registry key to store the current Windows Core Build Version.
- Runs Disk Cleanup
- Runs Defragment
- Pre-Compile .NET Framework
- Runs SEP update, Scan, Forced Check-in, and Clone Prep.
- Runs VMware Optimizer based on what OS version you are on and what templates you have defined. (Beings its ran via CMD there are no rolling back changes, it’s a bug/Feature in the Optimizer)
- Disables or Stops Services like Windows Updates, SCCM, Adaptiva. AppVolumes
- Clean out Downloads Cache folder
- Clean out Windows Prefetch
- Clear the event logs
- Releases IP Addresses
- Clears DNS
- Shutdown the VM
- Connect to each of the vCenters.
- Create a snapshot of each of the Masters in there Powered Off State.
- Update the VM Notes in vCenter to show Last Recomposed date, Last Update Date, Windows Core Build Version, Pool Type. and Pool Name based off the custom Registry Keys
- Will remove the old snapshots past X number of days old.
- Will Power the Master VMs back on.
- Start the SCCM and Windows Update services.
- Copy’s the log files from each VDI Master back to the central share.
- It will connect to the Horizon View Connection servers.
- Recompose or do image Push to the Pools, Varying the times based on if they are Production or Test.
During this entire process, we will update notes in ServiceNow. All logs generated by this task will be updated in the change.
There a few requirements for this script.
- Must be running Powershell v5.1 or newer on the Scripting Server and the Master VMs
- The account you are running the script as has to have admin rights to the Master Images, Rights to do recompose or image push in Horizon, and appropriate rights in vCenter to take Snapshot, delete snapshots, and write notes.
- Must have a folder share setup with the Read and Write rights to the user you will run the script as. I set up the following directories like this.
- \\ShareLocation\VDI_Tools
- Logs (This is where all the log files will be saved)
- Scripts (Save this script and run it from this location)
- CloneTools (Place your VMware Optimizer and templates in that location.
- \\ShareLocation\VDI_Tools
- The VMware Modules must be installed on the Scripting Server. (Out of laziness I just enable them all.)
- Must install the HV-Helper module on the Scripting Server. The code can be found HERE.
-
- Click the green Clone or download button and then click Download ZIP.
- Extract the zip file and copy the advanced functions Hv.Helper folder to a modules directory.
- Check your PowerShell $env: PSModulePath variable to see which directories are in use:
- User-specific: %UserProfile%\Documents\WindowsPowerShell\Modules
- Systemwide: C:\Program Files\WindowsPowerShell\Modules
- Unblock the advanced functions to allow them to be executed.
- In a PowerShell prompt (as Administrator), run the following command, tailoring the path to where you copied the VMware.Hv.Helper folder:
dir ‘C:\Program Files\WindowsPowerShell\Modules\VMware.HvHelper\’ | Unblock-File
-
- This only works on Horizon 7.0.2 and newer deployments! So if you are not there yet get to updating.
- If you want to run VMware OS Optimization Tool Fling you must have it downloaded and installed in the “CloneTools” share directory, with your custom Templates. For example, this is my configuration.
- All VDI Master Images must be domain joined. It uses domain authentication to do the remote PowerShell.
- Enable-PSRemoting must be enabled on All the VDI Master Images.
User Editable Variables for Primary portion of Script:
$HVServers = @("ServerName.Domain.com","ServerName.Domain.com") #Enter HVServer FQDN in format of ("HVServer1","HVServer2") $SnapDays = "-30" #Number of Days to keep past snapshots on the VDI Master VM must be a negitive number $RCTimeDelay1 = '1' #Recompose Test Pools Delay in hours $RCTimeDelay2 = '48' #Recompose Prod Pools Delay in hours $TestPoolNC = "Test" #Test pool naming convention that diffirentiates them from standard pool. $LogLocation = "C:\VDI_Tools\Logs" #Location to save the logs on the Master VM. They will be copied to share and deleted upon completion of the script. $ScriptLocation = "C:\VDI_Tools\Scripts" #Location for the Scripts on the Master VMs. This folder will remain after script completes $CloneToolsLocation = "C:\VDI_Tools\CloneTools" #Location on the Master VMs where the VMware Optimizer is stored and any other tools. $SleepTimeInS = "180" #Wait timer for windows to install Windows Updates via Windows Update Service. NOT needed if using SCCM to install updates. $AdobeUpdate = "AdobeAcrobatUpdate.ps1" #Name of Adobe Update Script. NOT needed if using SCCM to install updates. $FlashUpdate = "FlashUpdate.ps1" #Name of Flash Update Script. NOT needed if using SCCM to install updates. $FirefoxUpdate = "FireFoxUpdate.ps1" #Name of FireFox Update Script. NOT needed if using SCCM to install updates. $JavaUpdate = "JavaUpdate.ps1" #Name of Java Update Script. NOT needed if using SCCM to install updates. $CustomRegPath = "HKLM:\Software\YourCompanyName\Horizon\" #Custom Registry Path to create a Key that keeps record of when it was last updated. $RunOptimizer = '1' #To run Optimizer enter 1, If you do not want to run enter 0 $RunSEP = '1' #To run Optimizer enter 1, If you do not want to run enter 0 $RunServiceNow = '1' #To Create Service Now Incidents and Change Tickets with details of the Recompose process. $OptimizerTemplateNamingPrefix = "TemplateName" #Template Naming i.e. "CompanyTemplate10" for windows 10 Please make sure to append the namy by 10 for win10, 8 for win8, and 7 for win7 $VMwareOptimizerName = "VMwareOSOptimizationTool.exe" #Name Of the VMware Optimizer Tool $ShareLogLocation = "\\ShareName.domain.com\VDI_Tools\Logs" #Network Share to save the logs. This will be the collection point for all logs as each VDI Master VM will copy there local logs to this location. $ShareScriptLocation = "\\ShareName.domain.com\VDI_Tools\Scripts" #Network Share location where all the scripts are stored and distributed from. $ShareCloneToolsLocation = "\\ShareName.domain.com\VDI_Tools\CloneTools" #Network Share Location where all the Clone Tools are stored and distributed from. $DomainName = "Domain.Name" #The Domain that the Master Images are Joined to. $CorporateBuildRegistryKeyPath = "HKLM:\Software\YourCompanyName\OS\" $CorporateBuildRegistryKeyName = "BuildVersion"
There are 3 variables that you can set to turn off or on portions of the script. By entering a 0 it will skip those portions of the script.
$RunOptimizer = ‘1’ #To run Optimizer enter 1, If you do not want to run enter 0
$RunSEP = ‘1’ #To run Optimizer enter 1, If you do not want to run enter 0
$RunServiceNow = ‘1’ #To Create Service Now Incidents and Change Tickets with details of the Recompose process.
ServiceNow:
Looking at the Service Now bits its a bit overwhelmed on the number of variables you need fill out. But from what I have found its what’s needed for my workflow, you are more than welcome to customize it. The function of the Service Now the piece is that it will open an Incident, and open a change with the Incident as the parent. It will fill out all the change notes with the text from the log files, including what updates were installed, what 3rd party updates were installed and the complete logs of the script.
ServiceNow Variables defined are below. This will require some research on your part. You can follow some of my past posts where I defined how to find the Sys_ID of items in ServiceNow. Link to Building Service Now Functions for adding to existing automation scripts.
#ServiceNow Varibles $SNAddress = "https://YOURSERVICENOW.service-now.com" #______________________________________________________________________________________ #Incident Varribles $SNINCCallerID = "Caller Sys_ID" #Look up the Caller User you want to uses Sys_ID in your Service Now instance $SNINCUrgency = "2" #Look this up in your Service Now instance $SNINCImpact = "3" #Look this up in your Service Now instance $SNINCPriority = "4" #Look this up in your Service Now instance $SNINCContactType = "email" #Look this up in your Service Now instance $SNINCNotify = "2" #Look this up in your Service Now instance $SNINCWatchlist = "Watch List Sys_ID" #Can do comma seperated users Sys_ID's $SNINCServiceOffering = "Service Offering" #Look this up in your Service Now instance $SNINCProductionImpact = "No" #Well I hope its a No. $SNINCCategory = "Your Catagory" #Look this up in your Service Now instance $SNINCSubcategory = "Your SubCat" #Look this up in your Service Now instance $SNINCItem = "request" #Look this up in your Item menu $SNINCAssignmentGroup = "Assignment Group Sys_ID" #Look up the Assignment group you want to uses Sys_ID in your Service Now instance $SNINCAssignedTo = "Assigned To Sys_ID" #Look up the Assignment to user you want to uses Sys_ID in your Service Now instance $SNINCShortDescription = "Weekly Patch Cycle Updates VDI Pools for $SNDate" #Short Descriptiong of the task $SNINCDescription = "Weekly Patch Cycle Update for VDI pools. The following VDI Master Images are being updated with the latest Windows updates and 3rd Party software. $HVPoolMSTR Test Pools will be Refreshed at $SNScriptDate Production Pools will be Refreshed $SNScriptDate2" #Full description of what you are trying to acomplish this is my example. #______________________________________________________________________________________ #Change Varribles $SNCHGRequestedBy = "Requested By Sys_ID" #Look up the Requested by User you want to uses Sys_ID in your Service Now instance, Normaly the same as the Caller for the INC $SNCHGCategory = "Change Catagory" #Look this up in your Service Now instance $SNCHGServiceOffering = "Service Offering" #Look this up in your Service Now instance $SNCHGReason = "Change Reason" #Look this up in your Service Now instance $SNCHGClientImpact = "No" #Look this up in your Service Now instance $SNCHGStartDate = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss') #date in string format. Only way it works. $SNCHGEndDate = (Get-Date).AddHours($RCTimeDelay2+24).ToString('yyyy-MM-dd HH:mm:ss') #24 hour delay of right now (Can Change if needed) Has to be as a string $SNCHGWatchList = "Watch List Sys_ID" #Can do comma seperated users Sys_ID's $SNCHGUrgency = "2" #Look this up in your Service Now instance $SNCHGRisk = "4" #Look this up in your Service Now instance $SNCHGType = "Standard" #Look this up in your Service Now instance $SNCHGState = "1" #Look this up in your Service Now instance $SNCHGAssignmentGroup = "Assignment Group Sys_ID" #Look up the Assignment Group you want to uses Sys_ID in your Service Now instance $SNCHGAssignedTo = "Assigned To Sys_ID" #Look up the Assigned to User you want to uses Sys_ID in your Service Now instance #______________________________________________________________________________________ # Example of what I am using for my change information. $SNCHGJustification = "Weekly Sercurity Patching to install latest Windows updates, 3rd Party updates and Symantec antivirus client update" $SNCHGChangePlan = "Run report on each VDI Master Image for install Software Install Windows Updates from SCCM Install 3rd Party software updates from SCCM Run Report on each VDI Master Image for installed Software after updates have been completed Update Custom Reg key to reflect the last date updated Reboot VDI master VM Run Shutdown Script that will Run Disk Cleanup, Defragment C drive, Pre-Compile .NET Framework, Run SEP Update, Run full system Scan, Force check-in with SEP server, Run VMware Optimization Tool, Clean out DownLoad's Cache Folder, Clear Event Logs, Release IP, Clear DNS, and Shutdown the VM. Create a vCenter Snapshot of each VDI Master Image. Update the vCenter VDI Master Image notes to show current recompose date Remove Old Snapshots from the VDI Master Images Power Back on the VDI Master Image Start all the services Copy Logs from VDI Master Images to remote Share and upload to this Change. Refresh each of the pools based on Prod and Test timelines." $SNCHGTestPlan = "All VDI Clone Pools listed as Test Pools will Be updated 1 hour after script completion, and Production pools will be updated 48 hours later." $SNCHGBackoutPlan = "If there is a error found, will cancel all future Refresh tasks, and revert the ones that have been refreshed already to the previous snapshot."
There are a couple known bugs:
- One is running this more than one time a day will create two snapshots with the same name, and then try to recompose or push the image and get confused as there are two snaps of the same name. So it will fail to update the recompose or push part fo the script. Workaround: Remove the previous snaps for the same day before running the script again. I chose not to put this as the core code as it could delete a running snapshot.
- Running the script late in the day, like the last hour of the day. When it runs the script its date dependent. If the script is kicked off on the 12-4-2018 there is a possibility that the snaps might be taken on 12-5-2018, and when the recompose or Push happens it will be looking for a snap from 12-4-2018. In turn, erroring out and failing the Recompose or Push for those pools. Workaround: Run the script early in the day or morning to avoid running into the next day. On at minimum, the script takes 17 mins to run Per Pool. Please plan accordingly.
- If the credentials that you use to run the script have expiring passwords it will error out and fail the script and not prompt for new creds. (Might be an added feature soon.) Workaround: Use service account with a non-expiring password or delete the password file from the Script Server when you reset your password.
- Multi-Domain issues. If your scripting server is in a different domain than your VDI master images you will run into some DNS lookup issues. As in the script, we are not using FQDN to connect to the master Images we are just using hostname. Sorry if this causes an issue. Workaround: Can be fixed pretty simple by adding a domain variable and appending most of the $VMline variables in the script. (Can not promise when I get to fixing this one.)
Here are a few of before and after Shots of what is going on.
Snapshots:

Snapshots Before

Snapshots After
vCenter Notes:
vCenter Attributes:
Pool Details:

Pool Before

Pool After
Image Push:

Pending Push

Completed Push
Task List:
Custom Registry Entry:
Log files in the Share:
Installed Software Log Example:
VMware Optimizer Report:
Transaction Log Report:
The Code! This has been a long process and a ton of trial and error to get all this to work.
Link to the code on GitHub!
I will continue to update and optimize this code so check back frequently. If There are any issues found please let me know.
What is in the works for this script you ask?
- Breaking the Script into Modules so allow better plug and play and customizations.
- If no ServiceNow building out email functions for reporting. (If someone wants to devote the time to a good HTML body let me know. I just planned on plain text.
- Add Multi-Threading (I have run into some issues trying to deploy in this version and have held until I had more time to test.)
Last but not least. A huge thank you to Michael McDonnell for the help. Your help and guidance and mentorship have been much appreciated. And thanks to the MANY blog posts of other people and the mass amount of TechNet and PowerShell docs I have read of the last few months. And big thanks to Wouter Kursten for creating the HV-Helper, and answering some of my odd questions!