blog.atwork.at

news and know-how about microsoft, technology, cloud and more.

Hyper-V Cluster updaten

Hallo in die Runde. Schon mal gefragt wie man einen 4 Node Hyper-V Cluster patched? Klar.. mit Cluster Aware Updating. Kannten Sie nicht? Schlecht, gutes Tool. Was tuts? Naja, wie der Name schon sagt. ”Cluster Aware” bedeutet, dass man eine Liste an Hotfixes angibt, die auf einen Cluster installiert werden sollen ohne dass der Cluster offline geht.

Das Tool verschiebt alle Resourcen setzt den Node in Maintenance Mode, patched reihum die Knoten und rebootet den Node ohne dass auch nur eine VM offline geht (bzw. jede andere Resource natürlich). Vorzugsweise wirds halt bei einem Hyper-V Cluster eingesetzt weil die Resourcen (die VMs) per Live Migration herumgeschoben werden und damit gar nicht offline gehen.

Hab ich schon öfter verwendet. Aber, einziges Problem. Der CAU kann natürlich nur updaten was er auch kennt.

image

Dafür gibts jetzt an Button genannt “Preview updates for this cluster” – dort kann man dann zwei Plugins auswählen:

image

Das Microsoft.WindowsUpdatePlugin schaut einfach auf der Windows Update Seite nach und überprüft welche Updates auf den Clusternodes drauf sind und schon sagt er was so fehlt. Sehr schnelles durchpatchen von ganzen Clustern.

Wenn man jetzt aber gewissen Empfohlenen folgt und nicht notwendigen Patches wie auf diesem Technet Blog auch installieren will, dann sind das teilweise schon echt viele Patches die man da runterladen, einzeln applyen muss und dgl.

Jetzt hat der erste kluge Kopf gemeint, das muss doch einfacher gehen und ein kleines Powershell Script geschrieben das diesen Job vereinfacht. Ein weiterer kluger Kopf hat sich gedacht, das geht doch besser und dann kam ich.

Hmm.. nein ich bin nicht der klügste Kopf aber Perfektionist. Warum nicht beides kombinieren? Dem CUA kann ich per Microsoft.HotfixPlugin klar machen das er gefällligst einen Ordner mit fertigen Updates gefälligst einspielen soll.

Nur ... wie krieg ich jetzt alle fehlenden Updates?
Man erweitert das Script vom zweiten klugen Kopf und baut noch einen DownloadUpdates Parameter ein der per BITS alle Updates runterladet. Entpackt diese und kopiert sich laut Anleitung des HotfixPlugins einen Ordner mit entsprechenden Rechten und dem config file um dem CUA Plugin alles zu geben damit es zügig unsere Updates einspielen kann.

image

Jetzt noch das Script mit allen Parametern und ein paar wenigen Spielerein. Noch nicht perfekt, aber eigentlich recht gut (finde ich).

param
(
    [parameter(mandatory=$True)]$ClusterName,
    [parameter(mandatory=$false)][switch]$OutputGridView,
    [parameter(mandatory=$false)][switch]$OutputCSVFile,
    [parameter(mandatory=$false)][switch]$CheckClusterOnly,
    [parameter(mandatory=$false)][switch]$CheckHyperVOnly,
    [parameter(mandatory=$false)][switch]$CheckAllHotfixes=$true,
    [parameter(mandatory=$false)][switch]$DownloadUpdates
)
#Getting current execution path
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$DownloadPath = $dir

#Loading list of updates from XML files
[xml]$SourceFileHyperV = Get-Content $dir\UpdatesListHyperV.xml
[xml]$SourceFileCluster = Get-Content $dir\UpdatesListCluster.xml
$HyperVHotfixes = $SourceFileHyperV.Updates.Update
$ClusterHotfixes = $SourceFileCluster.Updates.update

#Getting installed Hotfixes from all nodes of the Cluster
$ClusterNodes = Get-Cluster $ClusterName | Get-ClusterNode
#Create array for outputtable
$ObjectOutputTable = New-Object System.Collections.ArrayList

# decide what to show
if ($CheckAllHotfixes) {
    $CheckClusterOnly = $false
    $CheckHyperVOnly = $false
}
foreach($Node in $ClusterNodes)
{
    # using hashtable for the hotfixes as it's easier to find an object in the collection
    $Hotfixes = New-Object System.Collections.Hashtable
    Get-HotFix -ComputerName $Node | Select-Object hotfixID,description | % { $Hotfixes.Add($_.hotfixid,$_.description) }
   
    #check if we should check cluster hotfixes
    if (!($CheckHyperVOnly)) {
        Write-Host "Listing Cluster Hotfixes on Node: $Node" -ForegroundColor Yellow
        #Write-Host "Listing Hyper-V 2012 Hotfixes" -ForegroundColor Yellow
        foreach($RecomendedHotfix in $HyperVHotfixes)
        {
            if ($Hotfixes.ContainsKey($RecomendedHotfix.id)) {
                if ($OutputGridView -or $OutputCSVFile) {
                    $ObjectOutputTable.Add( [PSCustomObject] @{
                        "HotfixID" = $($RecomendedHotfix.Id)
                        "Status" = "Installed"
                        "Host" = $Node
                        "Description" = $($RecomendedHotfix.Description)
                        "Link" = $("<a href="http://support.microsoft.com/KB/">http://support.microsoft.com/KB/</a>$($RecomendedHotfix.Id.ToString().split("B")[1])")
                    } ) | Out-Null
                } else {
                    Write-Host $RecomendedHotfix.id "installed" -ForegroundColor Green
                }
            } else {
           
                if ($OutputGridView -or $OutputCSVFile) {
                    $ObjectOutputTable.Add( [PSCustomObject] @{
                        "HotfixID" = $($RecomendedHotfix.Id)
                        "Status" = "Not Installed"
                        "Host" = $Node
                        "Description" = $($RecomendedHotfix.Description)
                        "Link" = $("<a href="http://support.microsoft.com/KB/">http://support.microsoft.com/KB/</a>$($RecomendedHotfix.Id.ToString().split("B")[1])")
                    } ) | Out-Null
                } else {
                    Write-host $RecomendedHotfix.id "not installed" -ForegroundColor Red
                }
            }
        }
    }
    if (!($CheckClusterOnly)) {
        Write-Host "Listing HyperV Hotfixes on Node: $Node" -ForegroundColor Yellow
        foreach($RecomendedClusterHotfix in $ClusterHotfixes)
        {
            if ($Hotfixes.ContainsKey($RecomendedClusterHotfix.id)) {
                if ($OutputGridView -or $OutputCSVFile) {
                    $ObjectOutputTable.Add( [PSCustomObject] @{
                        "HotfixID" = $($RecomendedClusterHotfix.Id)
                        "Status" = "Installed"
                        "Host" = $Node
                        "Description" = $($RecomendedClusterHotfix.Description)
                        "Link" = $("<a href="http://support.microsoft.com/KB/">http://support.microsoft.com/KB/</a>$($RecomendedClusterHotfix.Id.ToString().split("B")[1])")
                    } ) | Out-Null
                } else {
                    Write-Host $RecomendedClusterHotfix.id "installed" -ForegroundColor Green
                }
            } else {
                if ($OutputGridView -or $OutputCSVFile) {
                    $ObjectOutputTable.Add( [PSCustomObject] @{
                        "HotfixID" = $($RecomendedClusterHotfix.Id)
                        "Status" = "Not Installed"
                        "Host" = $Node
                        "Description" = $($RecomendedClusterHotfix.Description)
                        "Link" = $("<a href="http://support.microsoft.com/KB/">http://support.microsoft.com/KB/</a>$($RecomendedClusterHotfix.Id.ToString().split("B")[1])")
                    } ) | Out-Null
                } else {
                    Write-Host $RecomendedClusterHotfix.id "not installed" -ForegroundColor Red
                }
            }
        }
    }
}
#output to GridView?
if ($OutputGridView) { $objectOutputTable | Out-GridView }
#output to CSV File?
if ($OutputCSVFile) { $ObjectOutputTable | Export-Csv -Path $dir\Hotfixes.csv -NoClobber -NoTypeInformation -Encoding "UTF8" -Force -UseCulture }

#Download the Updates?
if ($DownloadUpdates){
    Write-Host "Downloading AllUpdates.." -ForegroundColor Yellow
    foreach($RecomendedHotfix in $HyperVHotfixes){
        if ($RecomendedHotfix.DownloadURL -ne ""){
            Start-BitsTransfer -Source $RecomendedHotfix.DownloadURL -Destination $DownloadPath
        }
    }
    foreach($RecomendedClusterHotfix in $ClusterHotfixes){
        if ($RecomendedClusterHotfix.DownloadURL -ne ""){
            Start-BitsTransfer -Source $RecomendedClusterHotfix.DownloadURL -Destination $DownloadPath
        }
    }
}

Wie immer gilt – wenn das Script was kaputt macht oder der CUA oder jegliches Folgen dieser Anleitung den Cluster explodieren lässt... ich bin nicht schuld!

LG Christoph

Comments (4) -

  • Chad Rogers

    9/4/2013 9:33:48 PM |

    Hi,

    Sorry I only speak English..

    I tried your script, but all the "Link =" lines give errors.  I don't know PowerShell to figure out whats wrong. :-(

    Closest I could come up with is:

    "Link" = $("<a href=""http://support.microsoft.com/KB/$($RecomendedHotfix.Id.ToString().split("B")[1])"">http://support.microsoft.com/KB/$($RecomendedHotfix.Id.ToString().split("B")[1])</a>")

    Which produces a string that produces correct HTML string for a link, but it's being displayed "as-is", rather that as an HTML link in the Grid.

    As I said, I don't know PS so not sure what you (and I) did wrong.

  • Christoph

    9/5/2013 9:50:24 AM |

    Hi Chad,

    no problem @ english ;)

    I made the mistake to think outgridview can view a html link which it doesn't. Will update the script with the changes in a few days but in the meantime you can use the download option without gridview output to get all updates automatically.

    BG Christoph

  • Chad Rogers

    9/5/2013 5:02:45 PM |

    Hi Christoph,

    One suggestion for a future version:

    The option to only download updates that are not installed already?

  • Christoph

    9/5/2013 10:13:48 PM |

    Hi Chad,

    yes - thought about that already. Just didn't have time,.. Frown

Loading