I previously described steps to automate approved updates from a WSUS server with WUInstall for virtual machines managed by VMware vCenter Server (https://deangrant.wordpress.com/2014/01/24/patch-management-for-guest-vms-with-windows-update-server-and-wuinstall-on-vsphere/).
I have recently updated this process to filter virtual machines objects by tagging objects in the new functionality in the vSphere Web Client. Also, the previous process had no concept of task parallelism, which has now also been addressed.
The script requires two mandatory parameters to be specified, we need to specify the vCenter server to which we require to establish a connection and the the category tag that has been assigned to the virtual machine to specify the infrastructure environment.
Param ([Parameter(Mandatory=$true)][string] $vCenter, [Parameter(Mandatory=$true)][string] $Environment)
Now we will register the ‘VMware.VimAutomation.Core’ snap-in and establish the connection to the vCenter server.
If (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) { Add-PSSnapin VMware.VimAutomation.Core | Out-Null } Connect-VIServer $vCenter
Once connected, we will be required to store the current session details of the established connection to pass as a variable later in the script block to create a snapshot for the virtual machine prior to invoking the WUInstall executable.
$Session = ($global:DefaultVIServer).SessionId
In order to filter virtual machines, we will firstly build a collection of virtual machine objects by invoking the Get-View cmdlet to retrieve only the name property values.
$VMs = Get-View -ViewType VirtualMachine -Property Name
For the collection of virtual machine objects we will now perform an operation on each object to filter the virtual machine collection to retreive only virtual machines to which the Tag Assignment ‘Environment’ is equal to the mandatory parameter specified and ‘Updates Enabled’ is equal to ‘Yes’.
ForEach ($VM in $VMS) { If (((Get-TagAssignment -Entity $VM.Name -Category Environment).Tag).Name -eq $Environment -and ((Get-TagAssignment -Entity $VM.Name -Category "Updates Enabled").Tag).Name -eq "Yes") {
Before we invoke the automated process on each virtual machine the Get-Date cmdlet will retrieve the DateTime object to the format ‘yyyy-MM-dd’, to append to the log file generated by the WUInstall executable.
$Date = (get-date).toString('dd-MM-yyyy-HHmm')
The WUInstall executable will be invoked remotely by leveraging the PSExec utility and therefore we will specify the arguments to the executable in a variable to passed in the script block. The WUInstall will be located on the virtual machine in the location C:\Program Files\WUInstall and will create a log file in the directory location.
$WUInstall = "\\$VM -s 'C:\Program Files\WUInstall\WUInstall.exe' /install /autoaccepteula /reboot_if_needed /logfile 'C:\Program Files\WUInstall\wuinstall_$Date.log'"
In order to leverage task parallelism the script block create will be invoked to run each job as a background job on the local computer using the Start-Job cmdlet. For each background job we will require to establish a connection to the vCenter server using the session ID rather than reconnecting.
One of the requirements it to create a virtual machine snapshot prior to the installation of the approved updates, where the name will be ‘Windows Update on yyyy-MM-dd’ and then we will invoke the WUInstall on the virtual machine using the PSExec utility.
$ScriptBlock = [scriptblock]::create("C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -Command { Add-PSSnapin VMware.VimAutomation.Core | Out-Null Connect-VIServer $vCenter -Session $Session New-Snapshot -VM $VM -Name 'Windows Update on $Date' & 'C:\Program Files (x86)\SysinternalsSuite\PsExec.exe ' $WUInstall }")
Now we will invoke the background task to initiate the process.
Start-Job -ScriptBlock $ScriptBlock } }
Depending on the number of virtual machines to which you are invoking the command you may wish to limit the number of background jobs that are invoked in parallel. The below will limit the number of backgorund jobs run to be 10 and once all background jobs have the state of completed deletes the backgound jobs invoked.
While((Get-Job -State 'Running').Count -ge "10") { Start-Sleep -Milliseconds 30 } While (Get-Job -State "Running") { Start-Sleep -Milliseconds 30 } Remove-Job * -Confirm:$False