Cluster Aware Updating (CAU) introduced in Server 2012 allows you to update your Fail-Over cluster by draining all the Highly Available (HA) roles from the node, performing the updates and then restoring the roles.
While this works great for Clustered HA VMs on a Hyper-V cluster, it appears CUA completely ignores any unclustered VMs on the Nodes. We have non-HA VMs on our Hyper-V Hosts that act as nodes in a File server Fail-Over Cluster and Exchange DAG groups. When CUA runs it ignores the shutdown settings of these VMs and just powers them off, this can causes loss of data in nodes and causes the cluster to have to bring up a down resource rather than just transfer the role.
To fix this we had to create scripts that shut down all the local VMs and then start them back up. These can then be set in the PreUpdateScript and PostUpdateScript profile settings for CUA. I stored the scripts on the CSV volume in C:\ClusterStorage\Volume1\Hyper-V\CAU\. These could then be added as a local file in CAU configuration wizard.
PreUpdateScript = ShutdownLocal.ps1
# VM Delay $CheckDelay = 30 # Local VMs $LocalVMs = Get-VM | Where-Object { ($_.IsClustered -eq $false) -and ($_.State -ne "Off")} $Node = $env:computername $LogFile = "C:\ClusterStorage\Volume1\Hyper-V\CAU\Log\$(get-date -f yyyy-MM-dd)-$Node.log" (Get-Date -Format s) + " CAU Shutdown Script" >> $LogFile #Shutdown Each VM ForEach ($VM in $LocalVMs) { $VMName = $VM.Name # Check State If ($VM.State -ne "Off") { If ($VM.AutomaticStopAction -eq "ShutDown") { # Change State (Get-Date -Format s) + " Shutting Down $VMName" >> $LogFile Stop-VM -AsJob -VM $VM #-WhatIf } ElseIf ($VM.AutomaticStopAction -eq "Save") { # Change State (Get-Date -Format s) + " Saving $VMName" >> $LogFile Stop-VM -AsJob -VM $VM -Save #-WhatIf } } } # Wait for all VMs to Shutdown $i = 0 Do { $VMon = 0 $i++ $TimeWait = $CheckDelay * $i Start-Sleep -Seconds $CheckDelay (Get-Date -Format s) + " Waited $TimeWait seconds for:" >> $LogFile ForEach ($VM in $LocalVMs) { $VMName = $VM.Name # Check State If ((Get-VM -Name $VMName).State -ne "Off" -and (Get-VM -Name $VMName).State -ne "Saved") { # Report State (Get-Date -Format s) + " -$VMName" >> $LogFile $VMon++ } } } Until ( ($i -gt 10) -or ($VMon -eq 0) ) (Get-Date -Format s) + " CAU Shutdown Script Completed" >> $LogFile
PostUpdateScript = StartLocal.ps1
$Node = $env:computername $LogFile = "C:\ClusterStorage\Volume1\Hyper-V\CAU\Log\$(get-date -f yyyy-MM-dd)-$Node.log" $PauseDelay = 300 # Local VMs $LocalVMs = Get-VM | Where-Object { ($_.IsClustered -eq $false) -and ($_.AutomaticStartAction -like 'Start*') -and ($_.State -ne "Running")} | Sort-Object AutomaticStartDelay (Get-Date -Format s) + " CAU Startup Script" >> $LogFile $DelayElapsed = 0 #Start Each VM ForEach ($VM in $LocalVMs) { $VMName = $VM.Name $VMDelay = $VM.AutomaticStartDelay # Check State If ($VM.State -ne "Running") { $VMDiff = $VMDelay - $DelayElapsed If ($VMDiff -gt 0) { Start-Sleep -Seconds $VMDiff } $DelayElapsed += $VMDiff # Change State (Get-Date -Format s) + " Starting $VMName" >> $LogFile Start-VM -AsJob -VM $VM #-WhatIf } } # Delay as will immediately start shutting down next node (Get-Date -Format s) + " VMs Started waiting $PauseDelay seconds for clusters to stabilise" >> $LogFile Start-Sleep -Seconds $PauseDelay (Get-Date -Format s) + " CAU Startup Script Completed" >> $LogFile