I’m now in charge of roughly 30 Hyper-V servers, half in an HA cluster, and the rest are standalone. Rather than doing updating each server manually I decided to take it upon myself to automate it via PowerShell. In my environment, we use SCVMM and SCOM, so doing this is rather easy, we also have WSUS for filtering updates as we have run into a few issues before.
There are 2 pieces of software used in this as a workaround for a known issue with remotely calling the Microsoft Update Session API, WUInstall and PSexec.
Here is the code below:
Add-PSSnapin Microsoft.SystemCenter.VirtualMachineManager
Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client
$VMMServer = Get-VMMServer "SCVMM IP Adress" #<- Had issues with authentication while using the FQDN
$SCOMServer = "SCOM Server FQDN"
$VMHosts = Get-VMHost -VMMServer $VMMServer | Where-Object {$_.name -ge "Hostname"}
New-PSDrive -Name:Monitoring -PSProvider:OperationsManagerMonitoring -Root:\ | Out-Null
New-ManagementGroupConnection -ConnectionString:$SCOMServer | Out-Null
Set-Location "Monitoring:\$SCOMServer"
function StartMaintenance {
$SCOMAgent = Get-Agent | Where-object {$_.Name –match “$VMHost”}
Write-Host "Placing host $VMHost into maintenance mode."
$SCOMAgent.HostComputer | New-MaintenanceWindow -StartTime (Get-Date) -EndTime ([DateTime]::Now).AddMinutes(60) -Comment "Running Windows Updates"
Disable-VMHost $VMHost | Out-Null
}
function EndMaintenance {
$SCOMAgent = Get-Agent | Where-object {$_.Name –match “$VMHost”}
Write-Host "Placing host $VMHost back into service."
Enable-VMHost $VMHost | Out-Null
$SCOMAgent.HostComputer | Set-MaintenanceWindow -EndTime ([DateTime]::Now).AddSeconds(5) -Comment "Finished Windows Updates"
}
function InstallUpdates {
psexec \\$VMHost -s -c \\shareserver\share\wuinstall.exe /install /accepteula > "C:\Users\logon\Desktop\Scripts\Logs\$VMHost.log"
}
function CheckForReboot {
$baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$VMhost)
$key = $baseKey.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\")
$subkeys = $key.GetSubKeyNames()
$key.Close()
$baseKey.Close()
If ($subkeys | Where {$_ -eq "RebootPending"}) {
Write-Host "Reboot is required: Restarting $VMHost"
Restart-Computer -ComputerName $VMHost -Force
sleep 120
}
Else {
Write-Host "No Reboot required."
}
}
function CheckVMStatus {
$VMStatus = Get-VM -VMMServer $VMMServer -VMHost $VMHost | Select Status
if ($VMStatus -match "HostNotResponding") {
Write-Host "SCVMM has not updated the status of the Virtual Machines yet"
sleep 120
CheckVMStatus
}
Else {
Write-Host "Powering on Virtual Machines"
Get-VM -VMMServer $VMMServer -VMHost $VMHost | Start-VM | Out-Null
}
}
function CheckHostStatus {
$Status = Get-Service -Computername $VMHost VMMS -erroraction silentlycontinue | select status
if ($Status -match "Running") {
Write-Host "Host is Up"
Write-Host "Waiting for SCVMM to populate VM state"
CheckVMStatus
}
Else {
Write-Host "Host is still rebooting"
sleep 120
CheckHostStatus
}
}
Foreach ($VMHost in $VMHosts) {
StartMaintenance
InstallUpdates
CheckForReboot
CheckHostStatus
EndMaintenance
}