Montag, 15. Oktober 2018

Powershell und Windows Update

Der Hyper-V Server hat ein Management Interface welches aus mehreren Scripts besteht:
Auszug

  • sconfig.cmd
    • sconfig.vbs
    • WUA_SearchDownloadInstall.vbs
    • ...
Die VBS-Scripts liegen %systemroot%\system32\en-us\ (bzw. anderen Sprachen).
Gesteuert wird das Windows Update über die Windows Update API. Die Doku ist schwerer Stoff, ich habe mir von verschiedenen Seiten und aus dem oben erwähnten Script ein paar Dinge zusammen gelesen und will es hier kurz notieren.
Meine Scripts in dem Artikel sind alle nicht perfekt, sondern eher als Lösungsansatz gedacht. Damit kann man z.B. beim Hyper-V Server etwas mehr tun als nur mit der sconfig "Oberfläche".

Die Schritte etwas im Detail

Zuerst muss man festlegen welche Updates man suchen will (ein paar Details):
$Criteria = "IsInstalled=0 and Type='Software'"
$Criteria = "IsInstalled=0 and Type='Driver'"
Dann werden verschiedene Com Objecte eingerichtet und z.B. die Titel der verfügbaren Updates angezeigt.
$Searcher = New-Object -ComObject Microsoft.Update.Searcher
$SearchResult = $Searcher.Search($Criteria).Updates
# Titel anzeigen
$SearchResult|select Title
Powershell bietet an der Stelle etwas mehr Komfort zur Suche und Auswahl als die eigentliche API. Deshalb füge ich hier bewusst den Neubau der Update Collection ein. Eigentlich ist diese jetzt schon in $SearchResult enthalten.
$updatesToDownload = New-Object -ComObject Microsoft.Update.UpdateColl
$SearchResult|%{ if($_.Title -match "KB2267602") {$updatesToDownload.Add($_)}}
$updatesToDownload|select Title
Danach werden die gewünschten Updates heruntergeladen.
$Session = New-Object -ComObject Microsoft.Update.Session
$Downloader = $Session.CreateUpdateDownloader()
$Downloader.Updates = $updatesToDownload
$Downloader.Download()
Um es ganz korrekt zu machen, kann man noch eine Collection der wirklich heruntergeladenen Updates erstellen ...
$updatesToInstall = New-Object -ComObject Microsoft.Update.UpdateColl
$SearchResult|%{ if($_.isDownloaded) {$updatesToInstall.Add($_)}}
... um dann die Updates zu installieren.
$Installer = New-Object -ComObject Microsoft.Update.Installer
$Installer.Updates = $updatesToInstall
$Result = $Installer.Install()
Als Abschluss wird bei Bedarf ein Neustart ausgeführt.
If ($Result.rebootRequired) { shutdown.exe /t 0 /r }

Komplett in einem Script

Das Ganze als ein Script mit zwei Parametern am Anfang:
Den Type entweder weglassen (Alle suchen) oder auf Software oder Driver setzen.
Bei $compare kann man entweder nur einen "*" für "Alles" setzen oder wie im Beispiel den Begriff mit Wildcards für ein bestimmtes Update.
# Parameter
$Criteria = "IsInstalled=0 and Type='Software'"
$compare= "*" # "*KB2267602*"
# Suche notwendige Updates 
$Searcher = New-Object -ComObject Microsoft.Update.Searcher
$SearchResult = $Searcher.Search($Criteria).Updates
# Zusammenstellung Download Collection
$updatesToDownload = New-Object -ComObject Microsoft.Update.UpdateColl
$SearchResult|%{ if( $_.Title -like $compare ) {$updatesToDownload.Add($_)}}
#$updatesToDownload|select Title
#$updatesToDownload.Count
# Download Updates 
$Session = New-Object -ComObject Microsoft.Update.Session
$Downloader = $Session.CreateUpdateDownloader()
$Downloader.Updates = $updatesToDownload
if ($updatesToDownload.Count -gt 0) {$Downloader.Download()}
# Zusammenstellung Install Collection
$updatesToInstall = New-Object -ComObject Microsoft.Update.UpdateColl
$SearchResult|%{ if($_.isDownloaded) {$updatesToInstall.Add($_)}}
# Install Updates 
$Installer = New-Object -ComObject Microsoft.Update.Installer
$Installer.Updates = $updatesToInstall
$Result = $Installer.Install()
# Neustart wenn gefordert
If ($Result.rebootRequired) { shutdown.exe /t 0 /r }

Was lief bisher?

Man kann sich auch die gesamte Historie anzeigen lassen:
Wann wurde welches Update installiert? Die Zeiten werden in UTC angezeigt!
# Historie abfragen
$session = new-object -comobject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()
$history = $searcher.QueryHistory(0, $searcher.GetTotalHistoryCount())
$history |select Date,Title |more
Leider liefert das Ergebnis am Ende immer viele Leerzeilen.

Mehr Komfort

Die Powershell Gallery halt ein umfangreiches Scriptmodul "PSWindowsUpdate" bereit mit dem der Windows Update Service wohl sehr komfortabel behandelt werden kann. Ich habe mir das zunächst nur kurz angeschaut. Um das Script aus der PSGallery zuinstallieren muss man ein paar Vorbereitungen treffen, das ist hier ganz gut beschrieben.

Noch ein Special

In $SearchResult stehen auf den zweiten Blick noch mehr Informationen!
So erzeugt man eine Liste mit Detailinformationen, z.B. mit der UpdateID oder dem Download Link.
#$Criteria = "IsInstalled=0 and Type='Software'"
$Searcher = New-Object -ComObject Microsoft.Update.Searcher
$SearchResult = $Searcher.Search($Criteria).Updates

foreach ($update in $SearchResult) {
  $title = $update.Title
  $guid = $update.Identity.UpdateId
  $title
  $guid
  $bundles = $update.BundledUpdates
     foreach($bundledUpdate in $bundles) {
        foreach($content in $bundledUpdate.DownloadContents) {
           if (!$content.IsDeltaCompressedContent) {
              $url = $content.DownloadUrl
              $url
            }
        }
    }
}

Mit der so ermittelten UpdateID wäre es auch möglich die Suche nach speziellen Paketen auszuführen:
$Criteria = "IsInstalled=0 and UpdateID='a3fafd03-b687-49e6-9df9-3963057ce376'"

Es gibt mit CIM und "PowerShell remoting session" auch einen Ansatz die VMs zentral zu patchen.
Ich habe hier ein Infos z.B. zu den resultcodes gefunden.

Keine Kommentare:

Kommentar veröffentlichen