Hey, Scripting Guy!Die Rückkehr von WinRM

Die Scripting Guys von Microsoft

Als die Scripting Guys mit der Entwicklung einer zweiteiligen Reihe zu Windows® Remote Management (WinRM) begannen, drängte sich ein wichtiges Thema sofort in den Vordergrund: die Sicherheit. Schließlich kannten die Scripting Guys die Probleme, die bei der Veröffentlichung des letzten Teils der Harry Potter-Reihe aufgetreten waren: Vorbestellte Exemplare der Bücher wurden versehentlich zu früh versandt und vor dem offiziellen Veröffentlichungstermin des Buchs geliefert. (Hat sich dies als Problem erwiesen? Nein, natürlich nicht, der Verlag bat die Kunden einfach, das Buch erst nach dem Veröffentlichungsdatum zu lesen.)

Das ist aber noch nicht alles. Das Ende des Buchs und der Reihe wurde verraten, bevor die Bücher erhältlich waren. (Falls Sie das Buch noch nicht gelesen haben, hier der Ausgang der Geschichte: Es stellt sich heraus, dass Harry Potter eine Art Zauberer oder so was ist!) Zudem waren gescannte Kopien aller Seiten sofort im Internet verfügbar, in einigen Fällen noch bevor die Autorin J. K. Rowling diese Seiten überhaupt geschrieben hatte. Insgesamt betrachtet handelte es sich schon um ein gewisses Sicherheitsdebakel, und die Scripting Guys wollten auf keinen Fall zulassen, dass ihnen etwas Ähnliches passierte. Denn wenn die Leute schon so erpicht darauf waren, das Ende der Harry Potter-Saga zu verraten, ist gar nicht auszudenken, wie weit sie gehen würden, um das Ende der zweiteiligen Reihe der Scripting Guys zu WinRM in Erfahrung zu bringen.

Glücklicherweise konnten die Scripting Guys sicherstellen, dass ihre Geheimnisse bewahrt wurden. Zugegebenermaßen war dies hauptsächlich der Tatsache zu verdanken, dass sie den zweiten Teil der Reihe erst weit nach dem Abgabetermin für den Artikel schrieben. Nun ja, der Scripting Guy, der diesen Artikel schreibt, war den ganzen August über im Urlaub, und im Gegensatz zu einem erstaunlich hohen Prozentsatz von Microsoft-Mitarbeitern verwendet er im Urlaub keinen Computer – er schaut nicht mal einen an.

Zugegeben: Selbst wenn er nicht im Urlaub ist, wirft er selten einen Blick auf einen Computer – von dessen Verwendung ganz zu schweigen. Aber das ist eine andere Geschichte.

Jedenfalls wissen wir, dass viele von Ihnen in den letzten vier Wochen nicht schlafen konnten und sich nächtelang den Kopf darüber zerbrochen haben, wie die WinRM-Saga wohl enden wird. Nun, die gute Nachricht ist, dass diese schmerzhaft langen Wochen schlafloser Spannung vorbei sind. Hier folgt als Premiere der aufregende Abschluss der zweiteiligen Reihe der Scripting Guys zu WinRM. Also, eigentlich ist er drüben in Abbildung 1 zu sehen.

Figure 1 Die Auflösung

strComputer = "atl-fs-01.fabrikam.com"

Set objWRM = CreateObject("WSMan.Automation")
Set objSession = objWRM.CreateSession("http://" & strComputer)

strDialect = "https://schemas.microsoft.com/wbem/wsman/1/WQL"
strResource = "https://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/*"
strFilter = "Select Name, DisplayName From Win32_Service Where State = 'Running'"

Set objResponse = objSession.Enumerate(strResource, strFilter, strDialect)

Do Until objResponse.AtEndOfStream
    strXML = objResponse.ReadItem

    Set objXMLDoc = CreateObject("Microsoft.XMLDom")
    objXMLDoc.async=False
    objXMLDoc.loadXML(strXML)

    Set objElement = objXMLDoc.documentElement
    Wscript.Echo "Name: " & objElement.ChildNodes(0).ChildNodes(0).NodeValue 
    Wscript.Echo "Display Name: " &  objElement.ChildNodes(1).ChildNodes(0).NodeValue 
    Wscript.Echo
Loop

Ja, wir verstehen schon: Die atemberaubende Wendung verursacht auch bei uns eine Gänsehaut. objElement.ChildNodes(0).ChildNodes(0).NodeValue! Wer hätte das geahnt? Dies liegt natürlich daran, dass aufgrund unserer strengen Sicherheitsmaßnahmen sogar die Scripting Guys selbst keine Ahnung hatten, wie die Reihe enden würde. Aber jetzt ist das Geheimnis gelüftet.

Bevor wir jedoch ins Detail gehen, sollten wir die wichtigsten Punkte der Reihe kurz wiederholen, falls es noch jemanden gibt, der Teil 1 (der unter technetmagazine.com/issues/2007/11/HeyScriptingGuy zu finden ist) aus unerfindlichen Gründen nicht gelesen hat. In Teil 1 wurde WinRM vorgestellt, eine neue Technologie von Windows Server® 2003 R2, Windows Vista® und Windows Server 2008, die die Verwaltung von Computern über das Internet und sogar über Firewalls hinweg stark vereinfacht. Zwar konnten mit der Windows-Verwaltungsinstrumentation (Windows Management Instrumentation, WMI) schon immer Computer von einem Remotestandort aus verwaltet werden, WMI stützt sich jedoch auf DCOM (Distributed COM) als Remoteverwaltungstechnologie. Das ist soweit auch kein Problem, abgesehen davon, dass viele Firewalls DCOM-Datenverkehr standardmäßig blockieren. Sie können zwar die entsprechenden Ports öffnen und DCOM-Datenverkehr zulassen, viele Netzwerkadministratoren machen jedoch von dieser Möglichkeit nur ungern Gebrauch. Ihre Hauptsorge besteht darin, dass durch Zulassen von DCOM gleichzeitig allen möglichen böswilligen Angriffen die Tür geöffnet wird.

Daher wurde WinRM entwickelt, „die Microsoft-Implementierung von WS-Management Protocol, eines SOAP-basierten, firewallfreundlichen Standardprotokolls, das die Zusammenarbeit von Hardware und Betriebssystemen verschiedener Hersteller ermöglicht“. Damit wird eigentlich nur vornehm ausgedrückt, dass Sie jetzt die Remoteverwaltung mit Standardinternetprotokollen wie HTTP und HTTPS durchführen können.

Wie im letzten Monat angemerkt wurde, können mit WinRM mühelos Verbindungen zu Remotecomputern hergestellt und WMI-Informationen von ihnen abgerufen werden. Bedeutet dies also, dass diese Technologie absolut perfekt ist? Nun, das ist nicht ganz richtig. Es wurde bereits darauf hingewiesen: Wenn WinRM Daten an das aufrufende Skript sendet, kommen diese Daten im XML-Format zurück. Die Analyse und Verwendung von XML kann etwas schwierig sein, insbesondere für Systemadministratoren mit begrenzter Erfahrung in diesem Bereich. Aus diesem Grund umfasst WinRM eine XSL-Transformation, die die zurückgegebenen Daten in ein leichter lesbares Format konvertiert.

So weit so gut, allerdings bedeutet dies auch, dass die Ausgabe immer ungefähr so aussieht:

Win32_Service
    AcceptPause = false
    AcceptStop = true
    Caption = User Profile Service
    CheckPoint = 0
    CreationClassName = Win32_Service

Dies ist nicht unbedingt eine schlechte Sache, bedeutet jedoch auch, dass die Ausgabe immer in das Befehlsfenster geschrieben wird. Sie können standardmäßig nicht ohne Weiteres Daten in einer Textdatei speichern, diese Daten in eine Datenbank oder Microsoft® Excel®-Tabelle schreiben und nicht viel anderes mit diesen Daten anfangen als die Informationen auf dem Bildschirm anzuzeigen. Das ist nicht gut genug.

Darüber hinaus wird das Problem verstärkt, wenn Sie sich dafür entscheiden, nur eine bestimmte Anzahl von Eigenschaften für eine WMI-Klasse zurückzugeben (um beispielsweise den Netzwerkverkehr zu verringern). Bei der Arbeit mit nur einigen, nicht allen Eigenschaften einer Klasse erhalten Sie eine ähnliche Ausgabe wie die folgende:

XmlFragment
    DisplayName = Windows Event Log
    Name = EventLog

XmlFragment
    DisplayName = COM+ Event System
    Name = EventSystem

Interessant, aber nicht gerade die ästhetisch ansprechendste Darstellung von Informationen (insbesondere die Überschrift „XmlFragment“, die immer wieder im Bericht erscheint).

Was können Sie jedoch dagegen tun? Benutzerdefinierten Code zum Analysieren und Formatieren dieser XML-Daten schreiben? Das klingt nicht gerade nach einem Schritt, den ein im Bereich Systemverwaltung tätiger Skriptersteller ausführen würde. Oder doch?

Wenn Sie möchten, dass etwas ordentlich erledigt wird, nehmen Sie die Sache selbst in die Hand

Wie sich herausstellt, ist die Arbeit mit den von WinRM zurückgegebenen XML-Rohdaten bei weitem keine so schwierige Aufgabe, wie es vielleicht scheint. In diesem Monat wird eine einfache Methode zum Analysieren und Formatieren von XML-Daten aufgezeigt. Der hier verwendete Ansatz ist keineswegs die einzige Möglichkeit für die Arbeit mit XML. Das ist jedoch kein Problem, da unser Hauptziel an dieser Stelle darin besteht, aufzuzeigen, dass Sie nicht auf die XSLT-Transformation angewiesen sind. Sobald Sie den Grundgedanken verstehen, sind Ihrer Kreativität in Bezug auf die Verwendung von WinRM-Daten keine Grenzen mehr gesetzt.

Es gibt jedoch einen kleinen Haken: Damit dieser Artikel nicht die festgelegte Länge überschreitet, wird der größte Teil des Skripts, das Sie gerade in Abbildung 1 gesehen haben, übersprungen. Das sollte kein allzu großes Problem darstellen, da die ersten beiden Drittel des Skripts im letzten Monat in dieser Rubrik ausführlich beschrieben wurden. Stattdessen konzentriert sich dieser Artikel auf das letzte Drittel des Skripts – den Teil, in dem tatsächlich mit den zurückgegebenen Daten gearbeitet wird.

Wie Sie sehen, setzt dieser Teil der WinRM-Reihe an der Stelle an, an der eine Instanz des WSMan.Automation-Objekts erstellt, ein Remotecomputer abgefragt (in diesem Fall atl-fs-01.fabrikam.com) und Informationen zu allen Diensten, die auf diesem Computer ausgeführt werden, erhalten wurden. Dies führt zu der Zeile im Skript, in der eine Do Until-Schleife zum Lesen und Verarbeiten der zurückgegebenen XML-Daten eingerichtet wird. Diese Schleife wird fortgesetzt, bis keine zu lesenden und zu verarbeitenden Daten mehr vorhanden sind. (Oder um es so klingen zu lassen, als ob wir wüssten, wovon wir reden: Diese Schleife wird bis zum Zeitpunkt fortgesetzt, an dem die AtEndOfStream-Eigenschaft der XML-Datei „True“ ist.)

Dies ist die Codezeile, die hier gemeint ist, und bei der die Geschichte in diesem Monat beginnt:

Do Until objResponse.AtEndOfStream

Innerhalb der Schleife wird als Erstes die ReadItem-Methode verwendet, um den ersten Abschnitt der zurückgegebenen XML-Daten zu lesen. (Da mit WinRM gearbeitet und Dienstinformationen abgerufen werden, besteht dieser erste Abschnitt aus allen Daten, die für den ersten Dienst in der Sammlung zurückgegeben wurden.) Nach dem Speichern dieser Daten (die wiederum im XML-Format vorliegen) in einer Variablen namens „strXML“ wird eine Instanz des Microsoft.XMLDom-Objekts erstellt. Dadurch entsteht effektiv ein leeres XML-Dokument, mit dem gearbeitet werden kann:

Set objXMLDoc = _
    CreateObject("Microsoft.XMLDom")

Sobald das leere Dokument bereitsteht, wird der Wert der Async-Eigenschaft auf „False“ festgelegt und anschließend die loadXML-Methode aufgerufen.

Somit kommen wir zu – was haben Sie da gerade gesagt? Warum der Wert der Async-Eigenschaft auf „False“ festgelegt wird? Und weshalb die loadXML-Methode aufgerufen wird? Gute Fragen – die könnten glatt von uns stammen!

Zunächst einmal gibt die Async-Eigenschaft an, ob das Skript das asynchrone Herunterladen von XML-Informationen zulässt. Wenn diese Eigenschaft „True“ ist, wird das Herunterladen gestartet und die Kontrolle sofort an das Skript zurückgegeben, selbst wenn der Download noch nicht abgeschlossen ist. Das klingt zugegebenermaßen recht gut. Unglücklicherweise wird jedoch das Skript dann so fortgesetzt, als ob alle benötigten Informationen zur Verfügung stehen. Wenn allerdings nicht alle erforderlichen Informationen vorhanden sind, kommt es zu Problemen. Daher wird die Async-Eigenschaft auf „False“ festgelegt.

Hinweis: Sie könnten natürlich Code schreiben, um den Downloadstatus regelmäßig zu überwachen, und dadurch sicherstellen, dass das Skript nicht vorzeitig fortgesetzt wird. Dies funktioniert, aber eine weitaus einfachere Methode besteht darin, einfach den Wert der Async-Eigenschaft auf „False“ festzulegen. In diesem Fall wird das Skript blockiert, bis der Download abgeschlossen ist. Das heißt bei einem angefangenen Download wartet das Skript, bis dieser beendet ist, bevor weitere Schritte durchgeführt werden.

Was die loadXML-Methode angeht, so stimmt ihre Funktion weitgehend mit dem überein, worauf ihr Name bereits schließen lässt: Sie lädt ein wohlgeformtes XML-Dokument (oder Dokumentfragment) in das leere Dokument. Jetzt muss lediglich die loadXML-Methode aufgerufen werden, wobei die Variable „strXML“ als einziger Methodenparameter übergeben wird:

objXMLDoc.loadXML(strXML)

Das Ergebnis? Die WinRM-Daten wurden in ein virtuelles XML-Dokument verwandelt. Das bedeutet, dass Sie mit der Verwendung von XML-Standardmethoden beginnen können, um dieses virtuelle Dokument zu analysieren.

Zu diesem Zweck müssen Sie dann die folgende Codezeile verwenden, um einen Objektverweis auf das Stammelement in der XML-Datei zu erstellen:

Set objElement = objXMLDoc.documentElement

Jetzt wird die Sache richtig unterhaltsam. (Natürlich vorausgesetzt, dass Ihre Vorstellung von Unterhaltung in der Analyse einer XML-Datei besteht. Für die Scripting Guys ist dies definitiv der Fall.)

Wie Sie sich vielleicht erinnern, sieht die Abfrage in der WMI-Abfragesprache (WMI Query Language, WQL) – in der WinRM-Terminologie als „Filter“ bezeichnet – folgendermaßen aus:

strFilter = "Select Name, DisplayName " & _
  "From Win32_Service Where State = 'Running'"

Es wurden also zwei Eigenschaften von der Win32_Service-Klasse angefordert: „Name“ und „DisplayName“. (Dazu wurde eine Where-Klausel eingefügt, die die zurückgegebenen Daten auf Dienste beschränkt, die gerade ausgeführt werden. Darum müssen Sie sich jedoch an dieser Stelle nicht kümmern.) Ist es wichtig, dass zwei Eigenschaften angefordert wurden? Ist die Reihenfolge der Anforderung von Bedeutung? Vielleicht. Woher sollen wir das denn wissen?

Ach ja, richtig: Vielleicht sollten wir als Autoren dieses Artikels wirklich die Antworten auf diese Fragen kennen. Kein Problem: Zufällig wissen wir, dass die Antwort auf beide Fragen „Ja“ lautet. Ist es wichtig, dass in der WQL-Abfrage zwei Eigenschaften angefordert wurden? Ja, schließlich handelt es sich dabei um die einzigen Eigenschaftswerte, die zurückgegeben werden, im Gegensatz zu einer Select * From-Abfrage, bei der Werte für alle Eigenschaften einer Klasse zurückgegeben werden.

Ist dann auch die Reihenfolge dieser beiden Eigenschaften wichtig? Absolut. Die Reihenfolge, in der die Eigenschaftennamen angegeben werden, ist dieselbe Reihenfolge, in der die Eigenschaftswerte zurückgegeben werden. Dies ist wichtig, da jeder Eigenschaftswert als untergeordneter Knoten (oder Abschnitt) des Stammelements zurückgegeben wird. Welcher Eigenschaftswert wird als erster untergeordneter Knoten zurückgegeben? Diese Frage lässt sich leicht beantworten. In diesem Fall ist der erste untergeordnete Knoten die Name-Eigenschaft, da diese als erste Eigenschaft in der WQL-Abfrage aufgelistet wird. Welche Eigenschaft wird als zweiter untergeordneter Knoten zurückgegeben? Richtig, die DisplayName-Eigenschaft, weil sie das zweite Element ist, das in der Abfrage aufgelistet wird.

Moment mal! Haben Sie das selbst herausgefunden, oder hat Ihnen jemand heimlich ein Vorausexemplar dieses Artikels zur Verfügung gestellt?

Jedenfalls wird es dadurch einfach, den Wert der Name-Eigenschaft auszugeben. Sie müssen lediglich auf den „NodeValue“ des ersten Elements (Element 0) in der ersten ChildNodes-Sammlung (Element 0) verweisen, ungefähr so:

Wscript.Echo "Name: " & _
objElement.ChildNodes(0).ChildNodes(0).NodeValue

Wie wird auf den Wert der DisplayName-Eigenschaft verwiesen? In diesem Fall verweisen Sie auf den „NodeValue“ des ersten Elements in der zweiten ChildNodes-Sammlung (Element 1):

Wscript.Echo "Display Name: " & _
objElement.ChildNodes(1).ChildNodes(0).NodeValue 

Wie verhält es sich, wenn eine dritte Eigenschaft (beispielsweise „Status“) in der WQL-Abfrage enthalten ist? In diesem Fall verweisen Sie einfach auf den „NodeValue“ des ersten Elements in der dritten ChildNodes-Sammlung (Element 2):

Wscript.Echo "Status: " & _
objElement.ChildNodes(2).ChildNodes(0).NodeValue 

So geht es weiter bis zur letzten Eigenschaft.

Wie sieht jetzt die Skriptausgabe aus? Etwa so:

Display Name: Windows Event Log
Name: EventLog

Display Name: COM+ Event System
Name: EventSystem

Zugegebenermaßen unterscheidet sich dies scheinbar nur wenig von der WinRM-Standardausgabe (obwohl die alberne XmlFragment-Überschrift beseitigt wurde). Folgendes macht den Unterschied aus: Durch die Arbeit mit den einzelnen Eigenschaftswerten sind Sie nicht mehr auf die Standardformatierung angewiesen (beachten Sie, dass die Bezeichnung „Display Name“ anstelle von „DisplayName“ verwendet wird), und es entfällt die Beschränkung, dass Informationen nur im Befehlsfenster angezeigt werden können.

Möchten Sie stattdessen diese Daten lieber in eine Excel-Tabelle schreiben? Nichts leichter als das: Fügen Sie zunächst den folgenden Codeblock (durch den eine neue Excel-Tabelle erstellt und konfiguriert wird) unmittelbar nach der Zeile im WinRM-Skript ein, durch die die Enumerate-Methode aufgerufen wird:

Set objExcel = _
  CreateObject("Excel.Application")
objExcel.Visible = True
Set objWorkbook = objExcel.Workbooks.Add()
Set objWorksheet = objWorkbook.Worksheets(1)

i = 2
objWorksheet.Cells(1,1) = "Name"
objWorksheet.Cells(1,2) = "Display Name"

Ersetzen Sie nun die ursprüngliche Do Until-Schleife durch die in Abbildung 2. Probieren Sie es aus.

Figure 2 Neue Do Until-Schleife

Do Until objResponse.AtEndOfStream
  strXML = objResponse.ReadItem

  Set objXMLDoc = CreateObject("Microsoft.XMLDom")
  objXMLDoc.async=False
  objXMLDoc.loadXML(strXML)

  Set objElement = objXMLDoc.documentElement
  objExcel.Cells(i, 1) = objElement.ChildNodes(0).ChildNodes(0).NodeValue 
  objExcel.Cells(i, 2) = objElement.ChildNodes(1).ChildNodes(0).NodeValue 
  i = i + 1
Loop

WinRM, Teil 3?

Dieser Artikel und der des vorigen Monats dürften Ihnen genügend Informationen für Ihre ersten Schritte mit WinRM bieten. Wir hoffen es jedenfalls. WinRM ist eine faszinierende neue Technologie, die eine wesentlich einfachere Remoteverwaltung Ihrer Computer (unter Aufrechterhaltung der Sicherheit) verheißt. Was natürlich direkt zu der Frage führt, die allen unter den Nägeln brennt: Bedeutet das, dass es einen dritten Teil der WinRM-Saga geben wird?

Leider können wir Ihnen das nicht verraten. Nicht aus Gründen der Sicherheit, sondern einfach, weil wir aufgrund des normalen Planungs- und Entscheidungsfindungsprozesses der Scripting Guys nicht wissen, ob ein weiterer Artikel zu WinRM geschrieben wird. Wie heißt es doch so schön: Bleiben Sie dran!

Der Scripting Perplexer von Dr. Scripto

Doktor Scripto hatte einen kleinen Unfall. Er ließ eines seiner Skripts herumliegen (statt es wie ein ordentlicher Skriptersteller fortzuräumen) und ist versehentlich darüber gestolpert, sodass die Skriptteile in alle Richtungen flogen. Er hat es geschafft, alle Variablen, Schlüsselwörter, Symbole usw. zu finden, und sie hübsch in alphabetischer Reihenfolge angeordnet, aber jetzt er muss sie wieder zusammensetzen, um sein Skript als Ganzes wiederherzustellen. Er wird eine Weile dafür brauchen, aber er geht davon aus, dass er bis zum Erscheinen des TechNet Magazins im nächsten Monat damit fertig wird. In der Zwischenzeit können Sie versuchen, diesen scheinbar willkürlichen Satz von Skriptteilen in ein vollständiges Skript zu verwandeln. Viel Glück!

Tipp: OK, hier ist eine kleine Hilfestellung. Durch das fertige Skript werden alle Dateien auf dem lokalen Computer gelöscht, die älter als ein angegebenes Datum sind.

ANSWER:

Der Scripting Perplexer von Dr. Scripto

Antwort: Tollpatschiger Doktor Scripto, Dezember 2007

Ja, Dr. Scripto hat es geschafft, sein Skript wieder zusammenzusetzen. Hier ist das rekonstruierte, funktionierende Skript, durch das alle Dateien auf dem lokalen Computer gelöscht werden, die älter als ein angegebenes Datum sind:

strDate = "20060102000000.000000+000"

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colFiles = objWMIService.ExecQuery("Select * From CIM_DataFile Where CreationDate < '" & strDate & "'")
For Each objFile in colFiles
    Wscript.Echo objFile.Name
Next

Die Scripting Guys von Microsoft arbeiten für Microsoft (oder sind zumindest dort angestellt). Wenn sie nicht gerade ihrem Hobby, dem Baseball (oder verschiedenen anderen Aktivitäten) nachgehen, betreiben sie das TechNet-Skriptcenter. Besuchen Sie es unter www.scriptingguys.com.

© 2008 Microsoft Corporation und CMP Media, LLC. Alle Rechte vorbehalten. Die nicht genehmigte teilweise oder vollständige Vervielfältigung ist nicht zulässig.