As you may be aware, VMware’s Update Manager (VUM) does a great job of remediating vSphere hosts when they’re in DRS-enabled clusters. You select the cluster you want to remediate, verify the correct baselines are attached, and click Remediate. VUM will automatically evacuate each host in turn, then patch, reboot, repeat as necessary until the entire cluster is remediated.
What you may not be aware of is that VUM doesn’t work as smoothly on clusters that aren’t DRS-enabled, as the automated host evacuation doesn’t work. This puts you, the VMware administrator, in a boring spot as you now have to manually evacuate each host. If you have many clusters that aren’t DRS enabled (vSphere Enterprise licensing required), then you are in for a tedious task.
That tedium is no longer necessary, thanks to a combination of VMware’s vSphere PowerCLI, PowerCLI for VUM, and this script. This script will open a connection to vCenter, present a menu of non-“out of the box” VUM baselines to choose from, present a list of clusters to choose from, then automatically evacuate each host with vMotion, and remediate as needed. This script is not designed to replace any functionality that already exists in a vSphere client with the VUM plugin enabled, but rather, offers an easy way for you, the admin, to remediate your clusters and do something else productive with the time saved.
There are a couple requirements to successfully run this script. The first one, of course, is PowerCLI 4.1U1 for vSphere. The second is PowerCLI 4.1 for Update Manager. I made a couple assumptions while writing this script. One is that you’ve created a one or more custom baselines that you’re using to remediate your hosts. For example, I’ve got three: ESXi Critical Updates, ESXi Non-critical updates, and an extension baseline for host hardware specific vendor software. Second, I’ve assumed that you have a common portgroup that you use for vMotions, as this script will look for that portgroup name and verify vMotion is enabled on that VMkernel port.
Note: The code for the do_snapin_check function was inspired by a post by Sean Kearney (@energizedtech). Sean is a longtime contributor to the PowerShell community, Microsoft MVP, and PowerShell evangelist in Canada. And if you’ve ever heard him speak, you’ll agree with me that he puts the “yell” in PowerShell. 🙂
Update: I put a loop in that will prompt for another cluster to be remediated using the previous baseline choices.
A word of caution: Be sure to check the distribution of VMs across the cluster when this process is complete. Without DRS, it is entirely possible that all of the VMs could be sitting on one host.
[powershell]
function do_clear_screen(){
clear
Write-Host "#### VMware Cluster Remediation ####"
Write-Host
Write-Host
}
function do_snapin_check() {
$statusloaded=$false
$snapins=("VMware.VimAutomation.Core","http://www.vmware.com/support/developer/PowerCLI/index.html"),("VMware.VumAutomation","http://www.vmware.com/support/developer/ps-libs/vumps/")
foreach ($snapin in $snapins) {
Add-PSSnapin $snapin[0]
if((Get-PSSnapin $snapin[0]) –eq $null){
Write-Host "This script requires" $snapin[0]
Write-Host "Which can be downloaded free from" $snapin[1]
} else {
$statusloaded=$true
}
}
return $statusloaded
}
function do_login(){
do {
$login = $false
$vcenter = Read-Host "vCenter server name or IP"
$credential = $host.ui.PromptForCredential("Credentials required", "Please enter your user name and password.", "", "")
Write-Host "Attempting to login…"
Connect-VIServer -Server $vcenter -Credential $credential -Protocol https | Out-Null
if (-not $?) {
do_clear_screen
Write-Host "Unable to login. Please try again."
} else {
$login = $true
}
} until ($login -eq $true)
}
function do_baseline_menu(){
$available = @{}
$selected = @()
$i = 0
do {
$available[$i] = $baselines.SyncRoot[$i].Name
$i++
} until ($i -ge $baselines.Count)
do {
do_clear_screen
$i = 0
do {
if($available[$i]) {
Write-Host $i":" $available[$i]
}
$i++
} until ($i -eq ($baselines.Count))
do {
$sel = Read-Host "Please choose the baseline you’d like to apply"
} until ( ($sel -ge "0") -and ($sel -lt $baselines.Count))
[int]$sel = $sel
foreach ($baseline in $baselines) {
if ($baseline.Name -eq $available[$sel]) {
$selected += $baseline
}
}
$available.Remove($sel)
if ($selected.Count -ne $baselines.Count) {
do {
$continue = Read-Host "Would you like to choose another baseline to be applied? [y/n]"
} until ( ($continue.ToLower() -eq "y") -or ($continue.ToLower() -eq "n") )
if ($continue.ToLower() -eq "y") {
do_clear_screen
}
} else {
$continue = "n"
}
} until ($continue.ToLower() -eq "n")
return $selected
}
function do_cluster_menu() {
do_clear_screen
$i = 0
do {
Write-Host $i":" $clusters.Syncroot[$i].Name
$i++
} until ($i -eq $clusters.Count)
do {
[int]$sel = Read-Host "Please choose the cluster you’d like to apply patches to"
} until ( ($sel -ge "0") -and ($sel -lt ($clusters.Count)) )
[string]$cluster_choices = $clusters.SyncRoot[$sel].Name
return $cluster_choices
}
function do_cluster_remediation() {
do_clear_screen
Write-Host "Please wait, retrieving cluster…"
$cluster = Get-Cluster $cluster_choices
if(!$cluster) {
Write-Host "Unable to retrieve cluster details. Exiting."
break;
}
$vmhosts = Get-VMHost -Location $cluster | where { ($_.ConnectionState -eq "Connected") -and ($_.Version -eq "4.1.0") }
if (!$vmhosts) {
Write-Host "No ESX(i) 4.1 hosts found in cluster. Exiting."
break;
} else {
$err = $null
# Verifying vMotion is enabled on a specific portgroup
foreach ($vmhost in $vmhosts) {
Write-Host "Verifying vMotion requirements on host $vmhost"
$vmknic = Get-VMHostNetworkAdapter -VMHost $vmhost | where {$_.PortGroupName -like "*VMK_CIFS*"}
if ($vmknic.VMotionEnabled.Equals($false)) {
Write-Host "vMotion requirement failed on host $vmhost. Exiting."
$err = "1"
break;
}
}
}
if(!$err) {
Write-Host "vMotion requirements passed."
foreach ($vmhost in $vmhosts) {
$clusterpartners = Get-VMHost -Location $cluster | where { ($_.ConnectionState -eq "Connected") -and ($_.Name -ne $vmhost.Name) }
$vms = Get-VMHost $vmhost | Get-VM
# vMotion all of the VMs from the current host to random partners in the cluster
if($vms) {
foreach ($vm in $vms) {
$dsthost = $clusterpartners | Get-Random
Write-Host "vMotioning VM $vm to $dsthost"
Move-VM -VM $vm -Destination $dsthost -Confirm:$false | Out-Null
}
}
Write-Host "Remediating $vmhost"
Remediate-Inventory -Baseline $baseline_choices -Entity $vmhost -Confirm:$false -ClusterDisableHighAvailability:$true
}
}
}
$ErrorActionPreference = "SilentlyContinue"
do_clear_screen
$statusloaded = do_snapin_check
if ($statusloaded) {
do_login
# —– BASELINE —– #
do_clear_screen
Write-Host "Please wait, retrieving baselines…"
$baselines = Get-Baseline | where {$_.IsSystemDefined -eq $false}
if ($baselines) {
$baseline_choices = do_baseline_menu
} else {
Write-Host "An error occurred while retrieving baselines. Exiting."
}
# —– BASELINE —– #
do {
# —– CLUSTERS —– #
do_clear_screen
Write-Host "Please wait, retrieving clusters…"
$clusters = Get-Cluster | Sort-Object -Property Name
if ($clusters) {
$cluster_choices = do_cluster_menu
} else {
Write-Host "An error occurred while retrieving clusters. Exiting."
}
# —– CLUSTERS —– #
# —– REMEDIATION —– #
do_clear_screen
Write-Host "You chose the following baselines to be applied:"
Write-Host
foreach ($baseline in $baseline_choices) {
$baseline.Name
}
Write-Host
Write-Host "You chose the following cluster to be updated:"
Write-Host
$cluster_choices
Write-Host
do {
$continue = Read-Host "Would you like to continue? [y/n]"
} until ( ($continue.ToLower() -eq "y") -or ($continue.ToLower() -eq "n") )
if ($continue.ToLower() -eq "n") {
Write-Host "Exiting."
# break;
} else {
do_cluster_remediation
}
# —– REMEDIATION —– #
$continue = Read-Host "Would you like to apply the same baselines to another cluster? [y/n]"
} until ($continue.ToLower() -eq "n")
}
[/powershell]
Thanks for sharing this code, it has been very useful. I’ve made some improvements to it to try and balance the RAM used on each host as guests are vmotioned. I’ve published it at http://communities.vmware.com/docs/DOC-16824