Neustart via Powershell

30. November 2009

Die Powershell gehört bei den aktuellen Windows-Versionen zum Lieferumfang und kann bei fast allen anderen Varianten nachgerüstet werden. Damit sind dann auch schon alle Bedingungen erfüllt, die zum Einsatz der hier vorgestellten Skripts nötig sind. Es geht um den Neustart von Computern. Dabei hat der Autor sie zudem noch so gestaltet, dass sich auch von einem Administrator eingesetzt werden können, der weder Zeit noch Muße hat, sich in die Feinheiten der Powershell-Programmierung zu vertiefen.

Viele Anwender und Administratoren sind sich zwar der Tatsache bewusst, dass die PowerShell ihnen nun als mächtiges Skripting-Instrument standardmäßig auf ihren Windows-Systemen zur Verfügung steht. Trotzdem scheuen viele von ihnen nach wie vor die vermeintlich hohe Hürde bei der Einarbeitung. Die in diesem Beitrag vorgestellten Skripts wurden vom Autor Bill Stewart so entwickelt, dass der Administrator sie leicht direkt von der Kommandozeile aus einsetzen kann.

Er muss sie dazu nur aufrufen und ihnen dabei nur die entsprechenden Informationen zu den Maschinen mitgehen, die er bearbeiten möchte. Dann kann er an diese Rechner eine Ping-Anfrage senden, sie neu starten, herunterfahren oder auch komplett ausschalten (Power Off). Dies kann er dabei mit einer beliebigen Anzahl von Maschinen in einer AD-Domäne tun. Zudem steht ihm sogar die Möglichkeit offen, Anwender aus der Ferne vom System abzumelden.

Manchmal dringend nötig: Neustart mehrerer Systeme in der Domäne

Das Problem kennt wohl jeder Systemverwalter aus seiner täglichen Praxis: Manchmal ist es notwendig, eine gewisse Anzahl von Rechner innerhalb einer Active-Directory-Domäne oder innerhalb einer Organisationseinheit (Organzational Unit, OU).

Dazu kann beispielsweise der Einsatz eines Gruppenrichtlinien-Objekts (GPO), das der Softwareverteilung auf den Systemen dient, der auslösende Grund sein. Eine Gruppenrichtlinie wird die Software nicht installieren, bevor auf dem System ein Neustart durchgeführt wurde.

Andere Gründe für den Reboot bestimmter Systeme können zum Beispiel die vorhergehende Installation eines Sicherheits-Patchces oder die Absicht des Administrators sein, auf den Rechnern ein Startup-Skript ausführen zu lassen. Aber ganz gleich, welche Gründe es dafür gibt: Ein Neustart von mehreren Computern in eigenen Netzwerk gehört zu den Standardaufgaben der Systembetreuung, die sehr gut mit Hilfe eines Skripts ausgeführt und erleichtert werden können.

Aus diesen Gründen entstand dann die Lösung des Autors, die grundsätzlich die folgenden Voraussetzungen erfüllen sollte:
•    Anlegen einer Liste der Computer,
•    Neustart jedes Systems in der Liste und
•    eine Meldung zu Erfolg oder Fehlschlag jedes Reboot-Versuchs.

Die ersten Überlegungen gingen noch davon aus, die direkt unter Windows zur Verfügung stehenden Kommandozeilen-Werkzeuge für diese Zwecke einzusetzen. So ist beispielsweise das „dsquery computer“-Kommando dazu in der Lage, eine Liste der Systeme produzieren und das „shutdown“-Kommando kann durchaus einen entfernten Computer neu starten.

Allerdings besitzen diese Kommandos auch deutliche Einschränkungen: So liefert das Kommando „dsquery computer“ alle gefundenen Computernamen in der Form, dass sie mit dem Zeichen „$“ enden und in Anführungszeichen eingeschlossen sind. Ein Skript, das mit diesem Kommando arbeitet, würde also einige Codezeilen allein dafür benötigen, in denen die Zeichenketten so bearbeitet werden, dass sie nur noch den gewünschten Namen beinhalten.

Die zweite Einschränkung bestand darin, dass die Entwickler der „shutdown“-Kommandos sicher nicht an eine Automatisierung dieser Vorgänge gedacht haben, so dass es ziemlich schwierig ist, die Ausgaben des Programms anschließend in ein lesbares Format zu bekommen.

Der zweite Ansatz bestand dann darin, ein Skript für den Windows Script Host (WSH) zu entwickeln, das dann ActiveX-Datenobjekte (ADO) einsetzen sollte, um auf diese Weise die Computer zu finden. Daran anschließend sollte WMI-Methoden (Windows Management Instrumentation) dazu verwendet werden, die gewünschten Reboots auf den Systemen auszuführen.

Wer derartige Methoden und Vorgehensweisen aber schon mal eingesetzt hat, der weiß auch, dass die formatierte Ausgabe von Daten unter WSH ein aufwändiger manueller Prozess ist. So fiel dann schließlich die Entscheidung, zwei Powershell-Skripts zu entwerfen:
•    GetEnabledComputerCN.ps1: Dieses Skript legt die Liste der Rechner an.
•    SetComputerState.ps1: Dieses Programm startet jeden Computer in der Liste neu und berichtet über Erfolg oder Fehlschlag des Vorgangs. Zudem erlaubt es dem Administrator, einen Rechner herunter zu fahren, ihn komplett abzuschalten (Power Off) oder Anwender von diesem System abzumelden.

Natürlich wäre es auch möglich gewesen, diese Funktionalitäten in einem einzigen Skript zu bündeln, doch es stellte sich heraus, dass sie so, als zwei unabhängige Lösungen weitaus nützlicher sind und vielseitiger eingesetzt werden können.

Geht es für einen Systemverwalter beispielweise zum aktuellen Zeitpunkt einfach nur darum, die Namen allen Computer in einer Domäne oder einer Organisationseinheit aufzulisten, so kann er dazu recht einfach das Skript GetEnabledComputerCN.ps1 verwenden.

Das zweite Skript kann er wiederum verwenden, wenn er bereits weiß, welche Systeme er neu starten oder herunterfahren möchte, oder wenn es gerade nur darum geht, ein paar Anwender von den Systemen zu „entfernen“.

Will er hingegen den kompletten Ablauf durchführen, also zunächst die Namen aller Systeme in der Domäne einsammeln und dann die entsprechenden Operationen darauf ausführen, dann kann der Administrator die beiden Skripts leicht mit einem PowerShell-Aufruf starten, was wir im weiteren Verlauf des Artikels noch demonstrieren werden.

Einsatz und Anwendung: Einfacher Start der Skripts

Das erste Skript GetEnabledComputerCN.ps1 ist einfach und schnell einzusetzen. Der folgende Aufruf zeigt die genaue Syntax dazu. Auch wenn der Aufruf hier auf mehrere Zeilen verteilt ist, so erfolgt er in der Konsole der Powershell doch in einer Zeile. Das gilt auch für alle weiteren Aufrufe, die in diesem Artikel gezeigt werden:

get-enabledcomputercn
-basename <String[]>
[-searchscope <String>]

Bild 1. Ein beispielhafter Aufruf des Kommandos set-computerstate: Die Ausgabe des Skripts erfolgt in drei Kolumnen. Wurde es mit der Aktion „Test“ aufgerufen, so bestätigt der Eintrag „Connect“, das eine Verbindung zu dem System zustande gekommen ist.

Den Parameter „-basename“ kann der Administrator dabei einsetzen, um einen oder auch mehrere so genannte „Distinguished Names“ als Startpunkt für die Suche des Skripts anzugeben. Bei diesen „Distinguished Names“(DN) handelt es sich um eindeutige Objektnamen in einem LDAP-Verzeichnis, wie beispielsweise Active Directory. Gibt er an dieser Stellen einen Blank beispielsweise in der Form „“ ein, so verwendet das Skript den aktuellen „Distinguished Name“ der Domäne.

Der zweite Parameter „-searchscope“ kann dazu verwendet werden, den Bereich der Suche genauer festzulegen. Hier stehen dem Anwender drei Werte zur Verfügung: Base, Onelevel und Subtree. Wird dieser Wert nicht explizit vom Anwender gesetzt, so verwendet das Script den Wert „Subtree“.

Wählt der Administrator hingegen der Wert „Onelevel“ aus, so sucht das Skript nach aktiven Systemen in der oder den bezeichneten DNs, durchsucht aber Container, die sich darunter befinden, auf keinen Fall mit. Höchstwahrscheinlich wird aber kaum ein Administrator in der Praxis je eine Basissuche einsetzen. Wer mehr Informationen dazu benötigt, findet diese auf der MSDN-Seite zum Thema SearchScope-Enumeration.

Der Autor hat beide Parameter im Skript so angelegt, dass sie auch durch ihre Position bestimmt werden. Deshalb kann der Anwender die Parameternamen einfach weglassen, wenn er die entsprechenden Werte jeweils als ersten und zweiten Parameter nach dem Aufruf des Scripts an der Kommandozeile übergibt. So gibt beispielsweise das folgende Kommando:

get-enabledcomputercn ““

eine Liste der aktiven Computer in der aktuellen Domäne aus. Etwas spezifischer geht der folgende Aufruf vor:

get-enabledcomputercn
“OU=Sales, DC=wascorp, DC=net“,
“OU=Mktg, DC=wascorp, DC=net“

Er liefert der aktiven Computer in den Organisationseinheiten „Sales“ und „Mktg“ und natürlich in möglichen OUs, die sich unterhalb dieser Organisationseinheiten befinden. Der Vorgang läuft dabei innerhalb der Domäne „wascorp.net“ ab.

Da die DNs bei diesem Aufruf durch die Anführungszeichen eingeschlossen wurden, verarbeitet die Powershell jeden DN als einen abgeschlossenen String. Ohne diese „Maskierung“ würde die Powershell die Angaben OU=Sales, DC=wascorp, DC=Net als ein Array von drei Zeichenkette und nicht als einen String verarbeiten.

das zweite Skript mit der Bezeichnung „Set-ComputerState.ps1“ setzt WMI ein, um seine Aufgaben zu erfüllen. Dazu gehört es, einen oder mehrere Computer neu zu starten, sie herunter zu fahren oder auch Anwender abzumelden. Dann gibt es Objekte aus, in denen die Resultate jeder Operation zu finden sind. Das Kommando, das dieses Skript zur Ausführung bringt, verwendet dabei die folgende Syntax:

set-computerstate
-computername <String []>
-action <string>
[-force] [-ping]

Der Parameter –computername dient natürlich dazu, den Name eines oder auch mehrerer Computer anzugeben. Was mit diesem System oder Systemen geschehen soll, wird dann mit -action festgelegt, wobei hier die Begriffe Logoff, PowerOff, Reboot oder Test verwendet werden können.

Der -force-Parameter veranlasst das Script, die Aktion unbedingt auszuführen, während der -ping-Schalter das Script auffordert, dem bestimmten Computer zunächst ein „ping“ zu senden.

Im Gegensatz zu den anderen Werten benötigt das hier verwendete „Test“ noch eine Erklärung: Verwendet der Anwender diesen Wert, so wird das Skript angewiesen, zunächst zu versuchen, eine WMI-Verbindung zu jedem spezifiziertem System aufzubauen, ohne dass dabei schon irgendeine Aktion ausgeführt wird.

So kann der Administrator zunächst einmal überprüfen, ob er überhaupt die gewünschten Systeme erreichen und auf zu entsprechend zugreifen kann. Auch eine Kombination dieses Wertes mit dem „-ping“ kann in vielen Fällen sinnvoll sein:

set computerstate pc4 Test -ping

Dieser Aufruf stellt fest, ob das Skript sich mit System verbinden und es mit einem ping-Kommando erreichen kann. Der Einsatz des Schalters -force mit dem „Test“-Wert ignoriert, da sein Einsatz nur bei den anderen Aktionen wirklich sinnvoll ist.

Auch bei diesem Skript werden die Parameter -computername und -action durch ihre Position bestimmt, so dass der Anwender sie weglassen kann, wenn er sie in der entsprechenden Reihenfolge einsetzt.

Die Tabelle 1 zeigt einige beispielhafte Aufrufe des Kommandos in der Übersicht.

Kommando                                                             Ergebnis
set-computerstate -computername pc1                 Der Aufruf startet den PC
– action Reboot                                                       „pc1“ neu
set-computerstate pc1,pc2 Logoff -force                Erzwingt ein“Logoff“ an den
                                                                               Systemen „pc1“ und „pc2“
set-computerstate pc3 Test                                    Stellt fest, ob das Skript        
                                                                               erfolgreich eine Verbindung
                                                                               zum System „pc3“ aufbauen
                                                                               kann.

Der Screenshot in Bild 1 zeigt zudem einen beispielhaften Aufruf des Kommandos set-computerstate. Dort ist auch zu sehen, dass die Ausgabe des Befehls in drei Kolumnen erfolgt:
•    Unter „Computer“ ist der Name des Systems zu finden,
•    während unter „Action“, die jeweilige Aktion angezeigt wird, die das Skript auf dieses System ausgeführt hat. Wurde dabei, wie in diesem Beispiel „Test“ angegeben, so wird an dieser Stelle der Wert „Connect“ angezeigt.
•    Unter „Result“ wird schließlich das Ergebnis der entsprechenden Aktion angezeigt. Dies geschieht in hexadezimalen Ziffern oder in Form einer Zeichenkette.

War die Aktion wie in diesem Beispiel erfolgreich, so wird in der Regel der Wert „0x00000000“ angezeigt. Trat dagegen ein Fehler auf, so findet der Anwender an dieser Stelle einen Hexadezimal-Wert der ungleich „0“ ist oder die entsprechende Fehlermeldung.

Wer den zurück gegebenen Fehlercode auslesen will oder muss, kann dies mit Hilfe des Kommandos „Net Helpmsg“ tun, das den folgenden Aufruf verlangt:

net helpmsg (0x<die letzten 4 Ziffern>)

Wobei der Begriff <die letzten 4 Ziffern> für die letzten vier hexadezimalen Ziffern des Fehlercodes steht. Wurde also beispielsweise der Wert „0x800706BA“ ausgegeben, so muss der Anwender die folgende Anweisung eingeben:

net helpmsg (0x6BA)

und wird dann vom System die Meldung „Der RPC-Server ist nicht verfügbar“ angezeigt bekommen.

Die wahre Kraft der Skripts: die Kombination

Bereits zu Anfang des Artikels wurde erwähnt, dass es die Powershell dem Anwender recht einfach macht, die beiden hier vorgestellten Kommandos zusammen in einem einzigen Kommandoaufruf einzusetzen.

Das folgende Beispiel bietet dem Systemverwalter die Möglichkeit, alle Computer in der Organisationseinheit „MTK OU“ innerhalb der Domäne „wascorp.net“ neu zu starten. Dazu kann er dann zwei Möglichkeiten des Aufrufs wählen. Die erste sieht so aus:

get-enabledcomputercn
              "OU=Mktg,DC=wascorp,DC=net" |
                foreach-object
               { set-computerstate $_ reboot }

während die zweite Möglichkeit in diesem Aufruf dargestellt wird:

set-computerstate
     (get-enabledcomputercn
      "OU=Mktg,DC=wascorp,DC=net") reboot

In der ersten Form des Aufrufs führt zunächst Get-EnabledComputerCN.ps1 aus und sendet die Ausgabe des Skripts über eine Pipe an das Cmdlet „Foreach“. Diese führt dann wiederum Set-ComputerState.ps1 auf jedes System aus, dass mittels des Cmdlets übergeben wurde.

In der zweiten etwas kürzeren Form des Aufrufs wird eine andere Vorgehensweise gewählt, die aber zu dem genau gleichen Ergebnis führt: Hier wird Set-ComputerState.ps1 ausgeführt, wobei dem Skript dann Get-EnabledComputerCN.ps1 als Parameter „-computername“ übergeben wird. Diese Beispiele zeigen sehr schön, wie die beiden Skripts zusammen arbeiten können.

Im folgenden Abschnitt werden wir nun erläutern, wie die Skripts intern arbeiten. Das Skript GetEnabledComputerCN.ps1 ist relativ geradlinig programmiert und verwendet die Dotnet-Klassen „DirectoryEntry“ und „DirectorySearcher“, um das AD nach entsprechenden Systemen abzusuchen.

Dabei hat der Autor den Typ Accelerator (eine Art Shortcut auf eine tiefer liegende Dotnet-Komponente) für ADSI(Active Directory Service Interface) verwendet. Dieser legt dann wiederum Objekt „System.DirectoryServices.DirectoryEntry“ an.

Das Skript verbindet sich dann mit dem angefragten Objekt im Active Directory indem es den Namen hinter dem Type Accelerator angibt, wie der folgenden Ausschnitt aus dem Listing zeigt:

# Retrieve the domain’s DN.
  $domainDN = ([ADSI] "").distinguishedName[0]
foreach ($dn in $BaseName)
          {    if ($dn -eq "") {
         $dn = $domainDN
}
    $direntry = [ADSI] "LDAP://$dn"

Hat der Anwender an dieser Stelle dann einen leeren String eingegeben, so wird das Objekt DirectoryEntry eine Verbindung zu aktuellen Domäne aufbauen.

Bild 1. Ein beispielhafter Aufruf des Kommandos set-computerstate: Die Ausgabe des Skripts erfolgt in drei Kolumnen. Wurde es mit der Aktion „Test“ aufgerufen, so bestätigt der Eintrag „Connect“, das eine Verbindung zu dem System zustande gekommen ist.

Im nächsten Schritt legt das Skript ein Objekt „SystemDirectoryServices.DirectorySearcher“ an und setzt dann die Eigenschaften „SearchRoot“ und „Filter“ dieses Objekts, was im folgenden Ausschnitt aus der Main-Funktion gezeigt wird:

$searcher = new-object System.DirectoryServices.DirectorySearcher
$searcher.SearchRoot = $direntry
$searcher.Filter = "(&(objectCategory=Computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
$searcher.PageSize = 1000
$searcher.SearchScope = $SearchScope

Die Eigenschaft „SearchRoot“ des Objekts wird dabei mit dem DirektoryEntry-Objekt belegt, dessen im vorherigen Code-Ausschnitt angelegt wurde. Dabei kommt ein Suchfilter zum Einsatz, um die entsprechenden Systeme zu finden, gleich ob es sich bei ihnen um Workstations, Member-Server oder Domänen-Controller handelt

Nähere Informationen zur Arbeitsweise der Suchfilter des ADSI sind auf einer MSDN-Seite zum Thema „Search Filter Syntax“ zu finden.
In einem nächsten Schritt wird die Eigenschaft „PageSize“ des Objekts „Directory Searcher“ mit dem Wert 1000 belegt. Dadurch wird der Verzeichnisdienst in die Lage versetzt, pro Suche 1000 Objekte zurück zu geben.

Würde dieser Wert nicht so belegt, würde AD grundsätzlich nur die ersten 1000 Treffer zurückgeben. Danach konfiguriert das Skript noch die Eigenschaft „SearchScope“, die wie schon zuvor beschrieben die Werte „Base“, „OneLevel“ oder „Subtree“ besitzen kann.

Der abschließende Schritt, um das DirectorySearcher richtig aufzusetzen, besteht dann darin, festzulegen welche Eigenschaft der Anwender für jedes Objekt abfragen möchte. Zu diesem Zweck ruft das Programm die „Add“-Methode der Eigenschaft „PropertiesToLoad“ des Objekts „Directory Searcher“ auf, was im folgenden Listingausschnitt gezeigt wird:

[Void] $searcher.PropertiesToLoad.Add("cn")
    # Set the DirectorySearcher’s Sort property to a new SortOption
    # object, and configure the property name.
    $searcher.Sort = new-object System.DirectoryServices.SortOption
    $searcher.Sort.PropertyName = "cn"
    # Output the names for all the computers.
    $searcher.FindAll() | foreach-object {
      $_.Properties.cn
       }
   }
}

Diese „Add“-Methode gibt dem Programm einen Index zurück, da wir aber in diesem Skript keinen Index verwenden, wird der Wert mittels eines Typ-Casts zu „void“ geändert, so dass der Wert des Index nicht in der Ausgabe auftauchen kann. Das Programm soll ja nur den „Common Name“ jedes Systems ausgeben („cn“), weshalb dieser Parameter auch an die Add-Methode übergeben wird.

Danach wird mittels eines „SortOption“-Objekts erreicht, dass die Ergebnisse in aufsteigender Folge sortiert werden. Zur eigentlichen Ausgabe der Resultate ruft das Skript dann „FindAll“-Methode des Objekts „DirectorySearcher“ auf. Dann wird diese zum Cmdlets „Foreach“ mittels einer Pipe übergeben, das dann den „cn“-Eigenschaft eines jeden Objekts in der Liste ausgibt.

Das zweite Skript, Set-ComputerState.ps, verwendet WMI (Windows Management Instrumentation), um die gewünschten Aktionen auf dem jeweiligen System auszuführen. Dabei kommt ganz speziell die Methode „Win32Shutdown“ der Klasse „Win32_OperationSystem“ von WMI zum Einsatz.

Diese Methode benötigt allerdings einen Parameter, der ihr mitteilt, was sie tun soll. Die Auflistung in Tabelle 2 zeigt die gültigen Parameter für diese Methode.

Wert      Bedeutung
  0          Logoff
  1          Shutdown
  2          Reboot
  4          Forced logoff
  5          Forced shutdown
  6          Forced reboot
  8          PowerOff
12          Forced PowerOff

Dazu ein Hinweis: Zwar ist „Test“ kein gültige Aktion für die Methode „Win32Shutdown“, aber das Skript verwendet den Wert „16“, um die Aktion „Test“ zu kennzeichnen.

Die Werte dieser Parameter werden im Skript dann einer Serie von Variablen zugewiesen. Anschließend verwendet das Programm eine Hash-Tabelle, um die Variablen mit dem ersten Buchstaben der jeweiligen Aktion zu verbinden, wie der folgende Listingausschnitt aus dem Programm „Set-ComputerState.ps1“ zeigt:

$ACTION_LOGOFF   = 0
$ACTION_SHUTDOWN = 1
$ACTION_REBOOT   = 2
$ACTION_FORCE    = 4
$ACTION_POWEROFF = 8
$ACTION_TEST     = 16
$ACTION_LIST = @{"L" = $ACTION_LOGOFF;
                 "S" = $ACTION_SHUTDOWN;
                 "R" = $ACTION_REBOOT;
                 "P" = $ACTION_POWEROFF;
                 "T" = $ACTION_TEST}

Das Skript vergleicht das erste Zeichen des jeweiligen Parameters mit den Schlüsseln der Hash-Tabelle. Gibt es keine Übereinstimmung, ist damit also die spezifizierte Aktion nicht gültig, so gibt das Skript einen Fehler aus.
Die Hash-Tabelle wird vom Programm zudem dazu verwendet, den nummerischen Wert der Win32Shutdown-Methode zu bekommen.

Diesen Wert speichert es dann in der Variablen $flags ab. Hat der Anwender zudem den Parameter „-force“ mit angegeben, so verwendet das Programm den Operator „-bor“ um den Wert der „Force-Version“ der jeweiligen Aktion zu bekommen. Das gilt natürlich nur, wenn es sich bei der Aktion nicht um „Test“ handelt.

In einem nächsten Schritt wird ein Objekt des Typs „ManagementObjectSearcher“ angelegt, wobei das Skript den Typ Accelerator des WMI-Searchers der Powershell verwendet. Mittels einer Abfrage werden auf diese Weise dann alle Eigenschaften der Klasse „Win32_OperationSystem“ ausgewählt.

Danach konfiguriert das Skript die Optionen des Objekts „ManagementObjectSearcher“, um auf diese Weise alle WMI-Privilegien zu aktivieren. Dies ist auch der Grund, warum dieses Skript das Objekt „ManagementObjectSearcher“ und nicht das Cmdlet „Get-WMIObjekt“ einsetzt, da das Cmdlet die Aktivierung aller Privilegien nicht unterstützt.

Das Programm setzt eine „ForEach“-Schleife ein, um die System abzuarbeiten, die ihm mittels des Parameters „-computername“ übergeben wurden. Dabei wird für jeden Computer ein eigenes Output-Objekt angelegt und dessen Namen konfiguriert.

Sollte der Parameter „-ping“ auf der Kommandozeile angegeben worden sein, so verwendet ruft das Skript die Funktion „testIPHost“ auf. Diese wiederum verwendet die WMI-Klasse „Win32_PingStatus“, um mit ihrer Hilfe festzustellen, ob das entsprechende System auf die Ping-Anfrage reagiert oder nicht.

Gibt diese Funktion einen Wert ungleich „0“ zurück, so bedeutet dies, dass der Ping-Aufruf fehlgeschlagen ist. Dann führt das Skript ein Update auf die Eigenschaften „Action“ und „Result“ des Output-Objekts aus und macht mit dem nächsten System weiter.

Bild 1. Ein beispielhafter Aufruf des Kommandos set-computerstate: Die Ausgabe des Skripts erfolgt in drei Kolumnen. Wurde es mit der Aktion „Test“ aufgerufen, so bestätigt der Eintrag „Connect“, das eine Verbindung zu dem System zustande gekommen ist.

Der folgende Auszug aus dem Programmlisting zeigt die Fehlerbehandlung und wie dazu das Powershell-Statement „trap“ verwendet wird:

trap [System.Management.Automation.MethodInvocationException] {
      set-variable ok $FALSE -scope 1
      # Try to get the error code.
      $result = $_.Exception.GetBaseException().ErrorCode
      if (($result) -and ($result.GetType() -eq [Int])) {
        $output.Result = "0x{0:X8}" -f $result
      }
      else {
        # Get the exception message.
        $result = $_.Exception.GetBaseException().Message
        # Try extracting the error code from the exception message.
        ([Regex] ".*(0x[0-9A-F]{8}).*").Matches($result) | foreach-object {
          $output.Result = $_.Groups[1].Value
        }

        # If the regex didn’t match, just use the entire message.
        if ($output.Result -eq "") {
          $output.Result = $result
        }
      }
      continue
    }

Wenn eine Exception auftritt, wird dieser hier gezeigt Skript-Block die Variable $ok auf den aktuellen Stand bringen. Dann wird das Programm versuchen, die Eigenschaft „ErrorCode“ der Exception auszulesen.

Da aber nicht alle Exceptions eine derartige Eigenschaft zur Verfügung stellen, verwendet das Skript an dieser Stelle reguläre Ausdrücke, um mit ihrer Hilfe zu testen, ob die Nachricht der Exception einen hexadezimalen Fehlercode beinhaltet.

Je nach Ergebnis wird die „Result“-Eigenschaft des Output-Objekts mit dem Hexcode oder Fehlmeldung versehen. Da verwendet der „trap“-Block die „continue“-Anweisung, um auf diese Weise mit dem Statement des Programms fortzufahren, das dem Aufruf folgt, der die Exception verursacht hat.
Schließlich setzt das Skript „ManagementObjectSearcher“ auf den Namespace „root\cimv2“ der jeweiligen Maschine und ruft anschließend die Get-Methode des Objekts auf, um die Anfrage als Teil einer Foreach-Schleife ausführen zu können.

Hierbei wird dann noch überprüft, ob die Variable $ok den Wert $TRUE enthält, denn nur so ist sicher, dass keine Exception aufgetreten ist. Das Skript muss nun noch überprüfen, ob „Test“ die gewünschte Aktion war. Wenn dem so war, wird es dem Output-Objekt den Code „0“ zuweisen, der für erfolgreiche Durchführung steht.

Wurde als Aktion nicht „Test“ gewählt, so wird das Programm die Methode „Win32_Shutdown“ aufrufen und die Eigenschaften des Output-Objekts mit „Action“ und „Result“ auf den aktuellen Stand bringen. Das Skript verwendet an dieser Stelle dann die Funktion „decodeFlags“, um die Variable $flags in Form einer Zeichenkette auszugeben.

Ein Vorteil dieses Skripts besteht abschließend darin, dass zur Ausgabe Output-Objekte und nicht einfacher Text verwendet werden. Dadurch hat der Anwender die Möglichkeit, „das Format-Table“- Cmdlets der PowerShell einzusetzen, um beispielsweise nur die Eigenschaften „Computer“ und „Results“ auszugeben.

Die beiden hier vorgestellten Skripts sollen zeigen, wie relativ einfach es ist, mit Hilfe der Powershell verschiedene Skripts so miteinander zu kombinieren, dass sie eine Aufgabe zusammen erfüllen können. Die Skripte stehen in einer ZIP-Datei zum Download auf unserer Webseite bereit und können so wie sie sind, direkt eingesetzt werden, eine spezielle Anpassung vor der Anwendung ist hier nicht mehr nötig.

Bill Stewart/fms

Lesen Sie auch