Windows PowerShellErstellen eines eigenen Softwareinventurprogramms

Don Jones

Inhalt

Finden von Informationen
Erstellen von Prototypen
Lesen von Computernamen
Modularisieren
Pipelinefunktionen

In dieser Ausgabe von Windows PowerShell wird eine sehr praktische Verwendung der PowerShell vorgeführt: Es wird ein Tool erstellt, das die Buildnummer des Betriebssystems (eine der besten Möglichkeiten, die Betriebssystemversion zu bestimmen) und die Service Pack-Versionsnummer aus einer Liste von Computern inventarisiert. Die Lösung wird Ihnen aber nicht gleich am Anfang präsentiert. Sie werden in diesem Artikel durch den Prozess geführt, den ich zum Entwickeln eines Skripts wie diesem verwende.

Dies ist offensichtlich ein nützliches Tool, doch ich glaube, dass der Prozess der Toolerstellung sogar noch wichtiger ist. Wenn Sie diesen Entwicklungsprozess verstanden haben und für Ihre eigenen Aufgaben wiederverwenden können, werden Sie nahezu alle administrativen Probleme mithilfe von Windows PowerShell gut lösen können. (Wie heißt es in einem bekannten Sprichwort so schön: Zeige jemandem, wie er ein Tool erstellt und ...) Lassen Sie uns also anfangen.

Finden von Informationen

Die erste und oft schwierigste Aufgabe besteht darin herauszufinden, wo Sie eigentlich die Versionsnummer des Betriebssystems und des Service Packs finden können. Sie könnten versucht sein, in der Registrierung zu stöbern. Die Registrierung ist manchmal die Antwort bei der Suche nach Informationen wie diesen, aber für mich ist sie meist die letzte Zuflucht, weil das Arbeiten darin etwas kompliziert sein kann.

Die Worte „abrufen“ und „Informationen“ lösen bei mir sofort eine mögliche Antwort aus: Windows-Verwaltungsinstrumentation (Windows Management Instrumentation, WMI). Ein anderes Schlüsselwort, das mich an WMI erinnert, ist „remote“, da in Version 1 von Windows PowerShell die WMI Ihre einzige Option zum Durchführen eines Remoteinformationsinventars oder Verwaltungsvorgangs ist.

Leider gibt es in den meisten Windows-Systemen Zehntausende von WMI-Klassen, sodass es schwierig sein kann, die eine zu finden, die die benötigten Informationen enthält. Ich fange meistens mit einer Websuchmaschine an und gebe einen Ausdruck wie „WMI Service Pack Versionsnummer“ ein. Beachten Sie, dass ein relativ langer Suchbegriff wie dieser erforderlich ist, um genauere Ergebnisse zu generieren.

Es kann auch sein, dass Sie unterschiedliche Suchbegriffe ausprobieren müssen, erliegen Sie jedoch nicht der Versuchung abzukürzen („WMI SP Version“ wird keine allzu nützlichen Ergebnisse zurückgeben). Ziehen Sie alternative Begriffe in Betracht. Was Sie zum Beispiel „Patches“ nennen, bezeichnen andere vielleicht als „Hotfixes“ oder „Quick Fix Engineering“ oder „QFE-Patches“. Es kann sein, dass sie all diese Begriffe ausprobieren müssen, um die richtigen Ergebnisse zu finden.

Wenn Sie nach etwas suchen, das sich auf Computerhardware oder das zentrale Windows-Betriebssystem bezieht, kann es nützlich sein, den Suchbegriff um „Win32“ zu erweitern, da die meisten relevanten WMI-Klassen mit dem Präfix „Win32_“ anfangen. Das Hinzufügen von „Win32“ zur aktuellen Suche erzielt auf alle Fälle die besten Ergebnisse. Sie sehen mehrere Ergebnisse mit „Win32_OperatingSystem“ im Titel. Das ist ein WMI-Klassenname.

Der wichtige Trick hierbei ist, auf keines der Suchergebnisse zu klicken. (Das führt nur zum Wahnsinn.) Sie möchten zunächst die eigentlichen Dokumentationsseiten für die Klasse finden. Führen Sie daher eine neue Suche aus, bei der Sie nur den gefundenen Klassennamen verwenden. In der Regel ergibt sich dadurch in den ersten aufgelisteten Ergebnissen ein Link zur Website msdn.microsoft.com, und so gelangen Sie einigermaßen direkt zur Klassendokumentation.

Abbildung 1 zeigt einen Teil dieser Seite. Ich habe einen Bildlauf zu einer besonders wichtigen Tabelle auf dieser Seite durchgeführt, in der die Betriebssystemversionen aufgelistet sind, unter denen die Klasse funktioniert. Wie oft habe ich schon über einer Lösung gebrütet, nur um dann festzustellen, dass der Lösungsversuch in meiner Version von Windows gar nicht existierte. Daher habe ich mir angewöhnt, zuerst diese Tabelle zu prüfen.

fig01.gif

Abbildung 1 Nachschlagen von Informationen zur WMI-Klasse (zum Vergrößern auf das Bild klicken)

Beim Bildlauf nach oben auf derselben Seite sehen Sie zwei Eigenschaften, die von Interesse sind: BuildNumber und ServicePackMajorVersion. ServicePackMinorVersion dürfte ebenfalls nützlich sein, obwohl mir bei Microsoft noch nie eine 2.1-Service Pack-Zahl untergekommen ist. Trotzdem sollte diese Eigenschaft geprüft werden, wenn auch nur, um gründlich zu sein.

Cmdlet des Monats: Export-CliXML und Import-CliXML

Windows PowerShell hat die Fähigkeit, einen statischen Snapshot der Objekte in einem besonderen XML-Format zu speichern, wodurch die Informationen in diesen Objekten permanent in einer Datei aufbewahrt und für eine spätere Untersuchung in den Speicher geladen werden können. Sie können Objekte einfach zu Export-CliXML leiten, um diese Objekte in einer Datei zu speichern:

Get-Process | Export-CliXML c:\processes.xml

Wenn Sie diese Objekte später in die Shell importieren, können Sie sie so wie jedes andere Objekt untersuchen. Sie sind nicht „wirkliche“ Objekte, ermöglichen aber weitere Berichtsoptionen. Angenommen, Sie planen ein Skript, das alle Prozesse auf einem bestimmten Server um 3 Uhr morgens exportiert, während eine Wartungsaufgabe ausgeführt wird. Zu Beginn Ihres Arbeitstags können Sie diese Prozesse laden und prüfen, sie vielleicht nach ihrem virtuellen Speicherverbrauch sortieren, etwa so:

Import-CliXML c:\processes.xml | Sort VM -descending

Erstellen von Prototypen

Fahren Sie erst fort, wenn Sie sichergestellt haben, dass sich diese Eigenschaften wie erwartet verhalten. Windows PowerShell erleichtert dies sehr. Zu Beginn prüfen Sie diese Informationen auf dem lokalen Computer:

Get-WmiObject Win32_OperatingSystem | Select
BuildNumber,ServicePackMajorVersion,ServicePack­MinorVersion

Die kleinere Version war null. Das haben Sie erwartet und fahren deshalb fort, ohne dies zu beachten. Die anderen Informationen entsprechen ebenfalls den Erwartungen: eine Buildnummer für meinen Windows Server 2008-Computer, die 6001 lautet, und eine Service Pack-Version, die 1 lautet.

Als Nächstes führen Sie einen ähnlichen Test auf einem Remotecomputer durch, auf dem Sie Administrator sind:

Get-WmiObject Win32_OperatingSystem –computer Server2 | 
Select BuildNumber,ServicePackMajorVersion

Wenn dieser Test nicht funktioniert, müssen Sie vor dem Fortfahren zunächst den Grund dafür herausfinden. Alle Probleme hängen wahrscheinlich mit Konnektivität, Firewalls oder Berechtigungen zusammen und liegen damit außerhalb des Bereichs von Windows PowerShell. Wenn alles korrekt funktioniert, können Sie mit dem nächsten Schritt in der Problemstellung fortfahren: Wie rufen Sie eine Reihe von Computernamen aus einer Datei ab?

Lesen von Computernamen

Unter der Voraussetzung, dass es sich bei der Liste von Computernamen um eine Textdatei handelt und in jeder Zeile ein Computername aufgelistet ist, kann dies am einfachsten mit dem Cmdlet Get-Content bewerkstelligt werden. (Keine Sorge, falls Ihre Computernamen nicht in einer Textdatei oder zeilenweise aufgelistet sind. Die Methoden, die unter anderen Umständen zur Anwendung kommen, werden in einem künftigen Artikel erörtert.)

Jeder Name wird als unabhängiges Zeichenfolgeobjekt zurückgegeben. Ein nützliches Feature des Cmdlet Get-WmiObject ist, dass sein Parameter –computerName eine Sammlung von Computernamen akzeptiert. Daher müsste dies funktionieren:

$names = Get-Content c:\computernames.txt
Get-WmiObject Win32_OperatingSystem –comp $names | Select
BuildNumber,ServicePackMajorVersion

Das Problem ist jetzt, dass die Ausgabe eine Liste von Zahlen darstellt, ohne anzugeben, welche Zahl zu welchem Computer gehört. Glücklicherweise hat die Win32_OperatingSystem-Klasse zufällig eine andere Eigenschaft, CSName, die den Computernamen enthält. So können Sie Ihrer Ausgabe diese Eigenschaft hinzufügen und erhalten eine ansehnliche Inventur:

$names = Get-Content c:\computernames.txt
Get-WmiObject Win32_OperatingSystem –comp $names | Select
CSName,BuildNumber,ServicePackMajorVersion

Modularisieren

Die gesamte Vorgehensweise könnte einen weniger erfahrenen Techniker etwas überfordern. Deshalb besteht der letzte Schritt darin, all dies in eine Funktion zu modularisieren. Eine Möglichkeit ist das Erstellen einer Funktion, die einen Dateinamen akzeptiert, und das Anwenden der Funktion, sodass sie die Arbeit verrichtet:

Function Get-SPInventory ([string]$filename) {
  $names = Get-Content $filename
  Get-WmiObject Win32_OperatingSystem –comp  $names | Select   CSName,BuildNumber,ServicePackMajorVersion
}

Wie Sie sehen können, wurde der Arbeitscode in einer Funktion namens „Get-SPInventory“ zusammengefasst. Diese wurde mit dem Eingabeparameter $filename definiert, sodass sie etwa so aufgerufen werden kann:

Get-SPInventory c:\computernames.txt

Die Ergebnisse können durch Verwendung gängiger Windows PowerShell-Befehle nach wie vor in eine CSV-Datei geleitet, in HTML umgewandelt oder anders formatiert werden, etwa so:

Get-SPInventory c:\computernames.txt | 
Export-CSV SPInventory.csv

Dies ist aber immer noch nicht perfekt. Was ist, wenn auch Informationen inventarisiert werden sollen, die nicht in der Win32_OperatingSystem-Klasse enthalten sind, z. B. die BIOS-Seriennummer für die einzelnen Computer? Das wäre nützlich, da viele Konfigurationsverwaltungsdatenbanken (Configuration Management Databases, CMDB) die BIOS-Seriennummer als eindeutigen Bezeichner für Computer verwenden.

Es könnte außerdem sein, dass die Ausgabe der Funktion etwas flexibler sein und das Sortieren oder Filtern der Ergebnisse nach Möglichkeit erleichtern soll. Dann könnten Sie z. B. in der endgültigen Ausgabe nur Windows Server 2003-Computer mit einer älteren Service Pack-Version angeben, um eine Liste der Computer zu erstellen, die aktualisiert werden müssen.

Pipelinefunktionen

Jetzt soll die Funktion neu geschrieben werden, sodass sie besser mit der Windows PowerShell-Pipeline funktioniert. Insbesondere soll erreicht werden, dass die Funktion Computernamen direkt von der Pipeline akzeptiert. So können Sie bei jeder Verwendung der Funktion entscheiden, wo die Computernamen abgerufen werden sollen. Die Computernamen können sich in einer Datei oder in Active Directory befinden, und die Funktion soll in beiden Fällen problemlos funktionieren. Hier ist also die für die Pipeline abgewandelte Funktion:

Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_OperatingSystem       –comp $_ | Select     CSName,BuildNumber,ServicePackMajorVersion
    Write-Output $wmi
  }
}

Dieser besondere Funktionstyp verwendet einen PROCESS-Skriptblock, der ein Mal für jedes Pipelineobjekt, das Sie in die Funktion leiten, ausgeführt wird. (Engagierte Leser werden sich daran erinnern, dass der PROCESS-Skriptblock im Artikel vom Juli 2008 behandelt wurde, der unter technet.microsoft.com/magazine/cc644947.aspx verfügbar ist.) Die besondere Variable $_ wird automatisch mit der Pipelineeingabe aufgefüllt. Solange Sie Computernamen in die Pipeline leiten, wird es keine Probleme geben. Die neue Funktion wird folgendermaßen verwendet:

Get-Content c:\computernames.txt | Get-SPInventory

Wie Sie sehen, bietet dies mehr Flexibilität für das Abrufen von Computernamen aus verschiedenen Quellen. Ersetzen Sie einfach den Get-Content-Anteil des Befehls durch den entsprechenden Befehl, der für das Abrufen der Computernamen erforderlich ist. Der Prozess wurde zudem so eingerichtet, dass Sie die Ausgabe durch das Abfragen verschiedener WMI-Klassen (oder anderer Datenquellen) stabiler gestalten können, bevor Sie die Ausgabe erstellen.

Doch damit ist die Diskussion noch nicht zu Ende. Im Artikel des nächsten Monats wird diese Funktion noch erweitert, sodass die Ausgabe die BIOS-Seriennummer enthält.

Don Jones ist Mitbegründer von Concentrated Technology und Autor zahlreicher IT-Bücher. Lesen Sie seine wöchentlichen Windows PowerShell-Tipps, oder kontaktieren Sie ihn unter www.ConcentratedTech.com.