Windows PowerShellDas beste Inventurprogramm aller Zeiten!

Don Jones

Inhalt

Untersuchen des Tools
Probelauf
Computer en masse
Verwendungszwecke
Weitere Verbesserungen
Leistungsfähige Do-It-Yourself-Tools

In den letzten Ausgaben dieser Rubrik wurden verschiedene Verfahren für die Verwendung von Windows PowerShell zur Sammlung von Computerinventarinformationen vorgestellt. In dieser Ausgabe werden alle Elemente in einem ausführungsbereiten Tool assembliert, mit dem beliebige Arten von Informationen von der Windows-Verwaltungsinstrumentation (Windows Management Instrumentation, WMI) erfasst werden können.

Das Tool wird für die Arbeit mit einer Textliste von Computernamen oder mit von Active Directory abgefragten Computernamen konzipiert.

Untersuchen des Tools

Ich beginne mit dem Skript selbst (siehe Abbildung 1) und erläutere nacheinander die Elemente seiner Struktur sowie einige der in ihm verwendeten Tricks. Einige dieser Verfahren erscheinen möglicherweise etwas seltsam oder unpassend, aber ich fasse sie an dieser Stelle zusammen, um verschiedene Tricks demonstrieren zu können.

Abbildung 1 Das Skript

1. function Get-WmiInventory {
2.  param (
3.  $wmiclass = "Win32_OperatingSystem"
4.  )
5.  PROCESS {
6.   $ErrorActionPreference = "SilentlyContinue"
7.   $computer = $_
8.   trap {
9.    $computer | out-file c:\errors.txt -append
10.    set-variable skip ($true) -scope 1
11.    continue
12.   }
13.   $skip = $false
14.   $wmi = Get-WmiObject -class $wmiclass -computer $computer -ea stop
15.   if (-not $skip) {
16.    foreach ($obj in $wmi) {
17.     $obj | Add-Member NoteProperty ComputerName $computer
18.     write $obj
19.    }
20.   }
21.  }
22. }  

In Zeile 1 können Sie sehen, dass die Funktion „Get-WmiInventory“ heißt. Direkt darunter wird ein Eingabeparameter namens „$wmiclass“ definiert und ihm der Standardwert „Win32_OperatingSystem“ zugewiesen. Der PROCESS-Skriptblock gibt an, dass es sich hierbei um eine Filterfunktion handelt, die eine Auflistung von Computernamen von der Pipeline annimmt – Sie erfahren in Kürze, wie diese Informationen an die Funktion übergeben werden.

Durch Zeile 6 wird das normale Fehlerberichterstattungsverhalten der Shell deaktiviert, da ich eine eigene Fehlerprotokollierung bereitstellen möchte. In Zeile 7 wird einfach der aktuelle Computername (wie er an die Funktion geleitet wurde) erfasst und in der Variablen „$computer“ gespeichert.

Fragen und Antworten zu Windows PowerShell

F Kann ich Windows PowerShell zur Verwaltung von Windows Server 2008 Server Core verwenden?

A Selbstverständlich. Sie haben wahrscheinlich gehört, dass Windows PowerShell nicht auf Server Core installiert werden kann, weil Server Core derzeit .NET Framework nicht unterstützt, was für die Shell erforderlich ist. Dies ist aber kein Problem. Viele kluge Administratoren installieren nur dann etwas auf ihren Servern, wenn dies unbedingt erforderlich ist, und Windows PowerShell ist in dieser Hinsicht keine Ausnahme. Stattdessen können Sie Windows PowerShell auf Ihrem Clientcomputer installieren und zur Remoteverwaltung von Server Core mithilfe von Technologien wie der Windows-Verwaltungsinstrumentation (Windows Management Instrumentation, WMI), Active Directory und vielen mehr verwenden. Sie können beispielsweise problemlos die meisten Aspekte von Active Directory auf einem Server Core-basierten Domänencontroller bequem von Ihrem Schreibtisch aus verwalten.

Betrachten Sie nun Zeile 13, in der eine Variable namens „$skip“ erstellt und ihr der boolesche Wert „False“ zugewiesen wird (mehr dazu gleich). In Zeile 14 wird dann versucht, die gewünschten WMI-Informationen mithilfe des Cmdlet „Get-WmiObject“ vom aktuellen Computer abzurufen. Beachten Sie, dass der Parameter „–ErrorAction“ (oder „–EA“) angegeben wurde, der die Shell anweist, eine Ausnahme zu generieren, wenn der WMI-Abruf aus irgendeinem Grund fehlschlägt.

In Zeile 15 wird geprüft, ob die $skip-Variable immer noch „False“ enthält. Ist dies der Fall, werden in Zeile 16 die von WMI zurückgegebenen Werte aufgelistet, und jedem WMI-Objekt wird (in Zeile 17) eine ComputerName-Eigenschaft hinzugefügt. Dadurch wird, wenn das Skript WMI-Objekte von mehreren Computern zurückgibt, jedes Objekt in einer praktischen Eigenschaft mit seinem übergeordneten Computernamen beschriftet. In Zeile 18 werden die einzelnen Objekte an die Pipeline ausgegeben, damit es von einem anderen Cmdlet bearbeitet werden kann oder das Formatierungssubsystem der Shell übernehmen kann, um einige der Eigenschaften des Objekts anzuzeigen.

Wie sieht es jedoch aus, wenn ein Fehler auftritt? Da „–EA Stop“ angegeben wurde, führt die Shell die Auffangroutine in Zeile 8 aus. Zuerst wird der Computername in eine Textdatei geschrieben, um ein Protokoll der Computernamen zur Verfügung zu stellen, bei denen während der Inventur ein Fehler aufgetreten ist. Dann wird in Zeile 10 für die $skip-Variable der boolesche Wert „True“ festgelegt. Dadurch wird verhindert, dass das Skript versucht, bei Zeile 15 etwas auszugeben – wäre dieser Schritt nicht durchgeführt worden, würde jeder Computer, bei dem ein Fehler auftritt, dazu führen, dass die Inventarinformationen des vorhergehenden Computers erneut ausgegeben werden.

In Zeile 10 wird ein anderes Verfahren zum Festlegen der Variablen verwendet: Die Auffangroutine selbst ist ein privater Variablenbereich und enthält keine $skip-Variable. Die gewünschte $skip-Variable befindet sich im übergeordneten Bereich der Auffangroutine eine Ebene höher. Das Cmdlet „Set-Variable“ ermöglicht es, die Variable mithilfe des -scope-Parameters zu ändern, wodurch angegeben wird, dass sich die gewünschte $skip-Variable eine Ebene höher befindet. Beachten Sie auch, dass bei der Verwendung von Set-Variable dem Variablennamen nicht das Symbol „$“ vorangestellt ist.

Probelauf

Eine einfache Testmöglichkeit besteht darin, wie folgt einfach einen einzelnen Computernamen an die Pipeline zu senden:

"localhost" | Get-WmiInventory 

Da kein WMI-Klassenname angegeben wurde, wird der Standardwert „Win32_OperatingSystem“ verwendet. Sie können folgendermaßen eine andere Klasse angeben:

"localhost" | Get-WmiInventory "Win32_LogicalDisk"

In Abbildung 2 werden die Ergebnisse angezeigt. Wenn Sie mehr Computernamen einbeziehen möchten, erstellen Sie einfach wie folgt eine durch Trennzeichen getrennte Liste:

"localhost","server2","client17" | 
Get-WmiInventory "Win32_LogicalDisk"

fig02.gif

Abbildung 2 Ergebnisse von Get-WmiInventory

Computer en masse

Das Angeben einzelner Computernamen ist ziemlich mühsam, wenn Sie eine lange Liste haben. Wenn Sie bereits über eine Liste von Computernamen in einer Textdatei verfügen – mit einem Computernamen pro Zeile – können Sie diese Namen folgendermaßen direkt an die Inventurfunktion leiten:

Get-Content c:\names.txt | Get-WmiInventory "Win32_Service"

Ein anderes Verfahren besteht in der Verwendung des Cmdlet „Get-QADComputer“, das Bestandteil der kostenlosen Active Directory-Cmdlets ist, die unter quest.com/powershell verfügbar sind. Sie müssen die Active Directory-Cmdlets installieren, Windows PowerShell öffnen und folgenden Befehl ausführen:

Add-PSSnapin Quest.ActiveRoles.ADManagement

Führen Sie Get-QADComputer aus, um alle Computer von Active Directory abzurufen. Beachten Sie jedoch, dass dies in einer großen Domäne eine Weile dauert! Sie können „Help Get-QADComputer“ ausführen, um einige der Optionen anzuzeigen, die zum Filtern der Liste der Computer verfügbar sind, z. B. zur Erfassung nur der Computer, die sich in einer bestimmten Organisationseinheit befinden. Sie müssen auch eine kleine Änderung am Skript vornehmen und Zeile 7 folgendermaßen ändern:

$computer = $_

$computer = $_.Name

Dies ist erforderlich, weil bei den von Get-QADComputer erzeugten Objekten der Computername in einer Name-Eigenschaft und nicht als einfaches Zeichenfolgenobjekt gespeichert wird.

Verwendungszwecke

Die Standardausgabe dieses Skripts nimmt die vom Windows PowerShell-Formatierungssubsystem standardmäßig erstellte Form an. Die Win32_OperatingSystem-Klasse ist beispielsweise eine kleine Teilmenge (nur sechs Eigenschaften) der tatsächlich über diese Klasse verfügbaren Informationen. Sie können die Ausgabe dieses Skripts an ein beliebiges Standard-Cmdlet der Shell leiten, um diese Ausgabe anzupassen. Um zusammen mit den Computernamen Informationen zu Buildnummer und Service Pack anzuzeigen, gehen Sie daher wie folgt vor:

Get-Content c:\names.txt | Get-WmiInventory | 
Format-List BuildNumber,ServicePackMajor­Version,­ComputerName

Folgendermaßen können Sie eine Inventur der installierten Dienste durchführen und eine HTML-Tabelle generieren:

Get-Content c:\names.txt | Get-WmiInventory "Win32_Service" | 
ConvertTo-HTML | Out-File c:\services_inventory.html

Wenn Sie vollständige Inventarinformationen zu den logischen Datenträgern abrufen und sie in eine durch Trennzeichen getrennte Datei (CSV-Datei) schreiben möchten, gehen Sie wie folgt vor:

Get-Content c:\names.txt | 
Get-WmiInventory "Win32_LogicalDisk" | Export-CSV c:\disks.csv

Da die Funktion „Get-WmiInventory“ Objekte und keinen einfachen Text erzeugt, kann Ihnen die Shell dabei helfen, die Daten in die benötigte Form zu bringen – sei es für statistische Trendanalysen, Verwaltungsberichte oder nur für einen kurzen Überblick über wichtige Informationen.

Weitere Verbesserungen

Eine Schwäche des aktuellen Skripts besteht darin, dass dadurch alle WMI-Objekte aus der von Ihnen angegebenen Klasse abgerufen werden. Bei einigen Klassen können dies zu viele Daten sein. Ich bevorzuge es in der Regel, Win32_LogicalDisk so einzuschränken, dass nur lokale Datenträger abgerufen werden, deren DriveType-Eigenschaft den Wert 3 aufweist. Das Skript kann problemlos zur Aufnahme derartiger Filterkriterien geändert werden – das überarbeitete Skript wird in Abbildung 3 gezeigt. Um dieses neue Skript aufzurufen, verwenden Sie den folgenden Befehl:

Get-Content c:\names.txt | 
Get-WmiInventory "Win32_LogicalDisk" "DriveType='3'"

Abbildung 3 Das überarbeitete Skript zur Unterstützung der Filterung

1. function Get-WmiInventory {
2.  param (
3.   $wmiclass = "Win32_OperatingSystem"
4.   $filter = ""
5.  )
6.  PROCESS {
7.   $ErrorActionPreference = "SilentlyContinue"
8.   $computer = $_
9.   trap {
10.    $computer | out-file c:\errors.txt -append
11.    set-variable skip ($true) -scope 1
12.    continue
13.  }
14.  $skip = $false
15.  $wmi = Get-WmiObject -class $wmiclass -computer $computer -ea stop –filter $filter
16.  if (-not $skip) {
17.    foreach ($obj in $wmi) {
18.     $obj | Add-Member NoteProperty ComputerName $computer
19.     write $obj
20.    }
21.   }
22.  }
23. }

Durch die Änderungen in den Zeilen 4 und 15 wird einfach ein neuer Parameter namens „$Filter“ erfasst und an den –filter-Parameter von Get-WmiObject übergeben. Beim Aufrufen der Funktion kommt der $filter-Parameter an zweiter Stelle, sodass er als zweiter Wert nach dem WMI-Klassennamen übergeben wird. Sie haben zahlreiche weitere Möglichkeiten zur Anpassung dieser Funktion an Ihre spezielle Umgebung. Einige Dinge sollten Sie jedoch vermeiden:

  • Lassen Sie die Funktion ihre Ausgabe ausschließlich an die Pipeline senden, was in Zeile 19 des überarbeiteten Skripts erfolgt. Anders gesagt, lassen Sie die Funktion keine Informationen in eine Datei ausgeben. Senden Sie stattdessen die Pipelineausgabe der Funktion an ein Cmdlet wie „Out-File“, das die Ausgabe in eine Datei oder auf ein anderes Medium verschiebt. Dadurch wird sichergestellt, dass die Funktion ihre Flexibilität behält.
  • Filtern Sie keine Informationen innerhalb der Funktion heraus – wie das Entfernen von WMI-Objekteigenschaften oder das Ausgeben nur einer Teilmenge der Eigenschaften. Dadurch würde die Flexibilität der Funktion für zukünftige Szenarios ebenfalls verringert werden. Wenn Sie nicht alle ausgegebenen Informationen benötigen, leiten Sie diese Ausgabe stattdessen an ein Format-Cmdlet oder an Select-Object, damit Sie nur die Informationen erhalten, die Sie für die anstehende Aufgabe benötigen.
  • Fügen Sie der Funktion keine zusätzlichen Aufgaben hinzu. Lassen Sie jede Funktion nur eine Aufgabe erfüllen, statt Superfunktionen zu erstellen, die viele verschiedene Dinge tun. Wenn Sie über einen komplexen Prozess verfügen, setzen Sie diesen um, indem Sie jede Einzelaufgabe als eigenständige Funktion erstellen und dann Informationen von einer Funktion zur anderen leiten. Dadurch bleiben die einzelnen Funktionen flexibler und lassen sich leichter verwalten und viel einfacher debuggen.

Leistungsfähige Do-It-Yourself-Tools

Obwohl ein Skript wie dieses kein Ersatz für ein leistungsfähiges Konfigurationsverwaltungssystem wie System Center Configuration Manager ist, bietet es dennoch eine einfache Möglichkeit für die Erfassung von Inventarinformationen von einem Satz von Remotecomputern. Dieses Skript ist ein gutes Beispiel für einen der Zwecke, für die Windows PowerShell am nützlichsten ist: die Bereitstellung einer schnellen Ad-hoc-Lösung für Probleme, die Microsoft bei der Entwicklung von Windows oder des von Ihnen verwendeten Produkts nicht vorhergesehen hat.

Da in diesem speziellen Skript WMI verwendet wird, funktioniert es bei Computern mit älteren Betriebssystemen bis hin zu Windows NT 4.0 – obwohl Windows PowerShell selbst nicht auf so einem alten Computer installiert werden kann! Da viele Microsoft-Produkte Verwaltungsinformationen über WMI verfügbar machen, können Sie zudem Informationen von verschiedenen Produkten sammeln. Ich verwende gern ein WMI-Explorertool (ein kostenloses Tool ist unter scriptinganswers.com im Bereich „Tools Zone“ verfügbar) zum Durchsuchen der auf einem Computer verfügbaren WMI-Informationen und sammle dann mithilfe eines ähnlichen Skripts wie dem, das in diesem Artikel gezeigt wurde, Informationen von mehreren Computern.

Don Jones ist ein Partner bei Concentrated Technology und Mitautor von Windows PowerShell: TFM (SAPIEN Press). Lesen Sie seine wöchentlichen Windows PowerShell-Tipps, oder kontaktieren Sie ihn unter ConcentratedTech.com.