Hé, vous le scripteur !WinRM, le retour

Les scripteurs Microsoft

Lorsque les scripteurs ont décidé de créer une série en deux parties sur Windows® Remote Management (WinRM), un problème important leur a tout de suite sauté au visage : la sécurité. Après tout, nous les scripteurs étions bien conscients des problèmes qui ont accompagné la publication du dernier volet de la série Harry Potter : les exemplaires précommandés des livres ont été expédiés en avance par erreur et ont été livrés avant la date officielle de publication du livre. (Cela a-t-il posé problème ? Non, bien sûr que non ; l'éditeur a juste demandé aux gens de ne pas lire le livre jusqu' à sa date de publication).

Et ce n'est qu'une partie de l'histoire. Il y a eu des fuites sur le livre et la fin de la série bien avant la mise en vente du roman. (Si vous ne l'avez pas encore lu, voici ce qui se passe : il s'avère en fait qu'Harry Potter est un genre de sorcier) ! De même, les copies numérisées de toutes les pages étaient instantanément mises à disposition sur Internet, parfois avant même que l'auteur J. K. Rowling ait écrit ces pages. Pour tout dire, ce fut un fiasco en termes de sécurité, et les scripteurs étaient déterminés à ce que cela ne puisse pas leur arriver. Après tout, si des personnes étaient aussi déterminées à dévoiler la fin de la série Harry Potter, imaginez ce qu'elles auraient pu inventer pour pouvoir mettre la main sur la deuxième et dernière partie de la série des Scripteurs sur WinRM.

Heureusement, les Scripteurs ont réussi à tout verrouiller et à garder leurs secrets, heu... secrets. D'accord, c'était essentiellement dû au fait qu'ils n'avaient pas vraiment écrit la deuxième partie de cette série avant bien après la date limite pour remettre l'article. Mais heu... le scripteur qui écrit cet article était en vacances au mois d'août et, contrairement à un pourcentage étonnamment élevé d'employés de Microsoft, il ne regarde jamais, sans parler d'utiliser, un ordinateur pendant ses congés.

Bon d'accord : il regarde rarement, sans parler d'utiliser, un ordinateur même quand il n'est pas en congé. Mais ceci est une autre histoire.

Quoi qu'il en soit, nous savons que beaucoup d'entre vous ont perdu le sommeil au cours des quatre dernières semaines, perdus en conjectures et théories sur le suspense de la fin possible ou probable de la saga WinRM. Hé bien, la bonne nouvelle est que ces longues semaines d'insomnies sont terminées. Voici, pour la toute première fois, la passionnante conclusion de la série en deux parties des Scripteurs sur WinRM. Hmm, en fait, elle est là-bas à la figure 1.

Figure 1 Le dénouement

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

Oui, oui, cette incroyable intrigue nous a nous aussi fait frissonner de partout. objElement.ChildNodes(0).ChildNodes(0).NodeValue ! Qui l'avait vu venir ? Forcément, c'est parce que, dans le cadre de notre verrouillage de sécurité, les scripteurs eux-mêmes ne savaient pas comment l'histoire se terminerait. Mais maintenant le secret est éventé.

Avant de continuer, nous devrions récapituler rapidement la série dans son ensemble, pour le cas très improbable où l'un d'entre vous n'aurait pas lu la 1e partie (disponible à l'adresse technetmagazine.com/issues/2007/11/HeyScriptingGuy). Dans la 1e partie, nous avons présenté WinRM, une nouvelle technologie introduite dans Windows Server® 2003 R2, Windows Vista® et Windows Server 2008, qui facilite considérablement la gestion des ordinateurs via Internet, même en passant par des pare-feu. D'accord, Windows Management Instrumentation (WMI) a toujours offert la possibilité de gérer des ordinateurs à distance. Toutefois, WMI s'appuie sur DCOM (Distributed COM) comme technologie de gestion à distance. C'est bien, sauf que par défaut, un grand nombre de pare-feu bloquent le trafic DCOM. Certes, vous pouvez toujours ouvrir les ports appropriés et autoriser le trafic DCOM, mais de nombreux administrateurs réseau répugnent à le faire. Leur principal souci réside dans le fait qu'en ouvrant la porte à DCOM, ils ouvrent en même temps la porte à toutes sortes de malveillances.

D'où WinRM, « l'implémentation Microsoft du protocole WS-Management, protocole SOAP standard compatible avec les pare-feu qui permet l'interaction entre matériel et systèmes d'exploitation de différents fournisseurs ». Ce qui est juste une façon un peu pompeuse de dire que vous pouvez maintenant assurer la gestion à distance en utilisant des protocoles Internet standard comme HTTP et HTTPS.

Comme nous l'avons dit le mois dernier, WinRM permet de se connecter facilement à des ordinateurs distants et d'y récupérer les informations WMI. Cela veut-il dire qu'il s'agit d'une technologie absolument parfaite ? Hé bien, non, pas exactement. Comme nous l'avons fait remarquer, lorsque WinRM renvoie des données au script d'appel, ces données reviennent au format XML. Inutile de dire que XML peut être un peu difficile à analyser et à exploiter, surtout pour les administrateurs système ayant une expérience limitée dans ce domaine. Pour cette raison, WinRM est fourni avec un convertisseur XSL qui restitue les données renvoyées dans un format plus lisible.

C'est formidable, sauf que cela veut également dire que votre résultat ressemblera toujours à quelque chose de ce type :

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

Ce n'est pas nécessairement une mauvaise chose, mais cela signifie également que votre résultat sera toujours écrit dans la fenêtre de commande : par défaut, vous ne pouvez pas facilement enregistrer des données dans un fichier texte, écrire ces données dans une base de données ou une feuille de calcul Microsoft® Excel®, ni faire grand chose de ces données à part les afficher à l'écran. Cela n'est pas suffisant.

En outre, le problème s'aggrave si vous choisissez de ne renvoyer qu'un certain nombre de propriétés pour une classe WMI (par exemple en vue de réduire le trafic réseau). Lorsque vous travaillez avec seulement quelques propriétés d'une classe (par opposition à toutes les propriétés d'une classe) vous obtenez un résultat semblable à ceci :

XmlFragment
    DisplayName = Windows Event Log
    Name = EventLog

XmlFragment
    DisplayName = COM+ Event System
    Name = EventSystem

Intéressant, mais d'un point de vue esthétique, ce n'est pas l'affichage d'informations le plus agréable que nous ayons vu (surtout le titre XmlFragment qui apparaît sur le rapport).

Mais que pouvez-vous y faire ? Écrire un code personnalisé pour analyser et mettre en forme ces données XML ? Cela ne ressemble pas à ce qu'un scripteur d'administration de système ferait. Á moins que ?

Si vous voulez que quelque chose soit bien fait, faites-le vous-même

Il s'avère en fait que travailler avec les données XML brutes renvoyées par WinRM n'est finalement pas si insurmontable que cela pourrait paraître de prime abord. Ce mois-ci, nous allons vous présenter une méthode simple pour analyser et mettre en forme les données XML. L'approche que nous utiliserons n'est en aucun cas la seule façon possible de travailler avec XML, mais ce n'est pas grave : notre objectif principal est juste de démontrer que vous ne devez pas obligatoirement vous appuyer sur la transformation XSLT. Une fois que vous aurez saisi cette idée de base, hé bien, votre imagination sera la seule limite à ce que vous pourrez faire avec les données de WinRM.

Il y a cependant un tout petit hic : pour faire rentrer cet article dans l'espace qui nous est attribué, nous allons laisser de côté l'essentiel du script que nous avons reproduit à la figure 1. Cela ne devrait toutefois pas poser trop de problèmes parce que les deux premiers tiers du script ont été décrits en détail dans l'article du mois dernier. Nous allons plutôt nous concentrer sur le dernier tiers du script, qui est la partie où nous travaillons effectivement avec les données renvoyées.

Comme vous pouvez le voir, cet épisode des Chroniques de WinRM se passe après que nous avons créé une instance de l'objet WSMan.Automation, interrogé un ordinateur distant (dans ce cas, atl-fs-01.fabrikam.com) et reçu les informations sur tous les services s'exécutant sur cet ordinateur. Cela nous amène à la ligne du script où nous définissons une boucle Do Until pour lire et traiter les données XML renvoyées ; cette boucle continuera jusqu'à ce qu'il ne reste plus rien à lire et à traiter. (Ou, pour faire croire que nous savons de quoi nous parlons, cette boucle se poursuivra jusqu'au moment où la propriété AtEndOfStream du fichier XML aura la valeur True).

C'est la ligne de code dont nous parlons, et c'est là que commence l'intrigue du mois :

Do Until objResponse.AtEndOfStream

dans la boucle, la première étape consiste à utiliser la méthode ReadItem pour lire la première section de données XML renvoyées. (Comme nous travaillons avec WinRM et que nous récupérons des informations sur les services, cette première section inclura toutes les données renvoyées pour le premier service de notre collection.) Après avoir enregistré ces données (qui, encore une fois, sont au format XML) dans une variable strXML nommée, nous créons une instance de l'objet Microsoft.XMLDom. Dans les faits, cela nous donne un document XML vide avec lequel nous pouvons travailler :

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

Dès que notre document vide est prêt, nous définissons la valeur de la propriété Async sur False, puis nous appelons la méthode loadXML.

Ce qui nous amène à... qu'avez-vous dit ? Pourquoi définir la valeur de la propriété Async sur False ? Et pourquoi appeler la méthode loadXML ? Bonnes questions... si seulement nous y avions nous-mêmes réfléchi !

Pour les débutants, la propriété Async indique si le script autorise un téléchargement asynchrone des informations XML. Si cette propriété est True, le téléchargement démarrera et le contrôle sera renvoyé au script immédiatement, même si le téléchargement n'est pas terminé. D'accord, cela paraît plutôt pas mal. Malheureusement, cependant, votre script se poursuivra alors comme s'il avait toutes les informations dont il a besoin. Si ce n'est pas le cas, vous risquez d'avoir des problèmes. C'est pourquoi nous déterminons la propriété Async sur False.

Remarque : Certes, vous pourriez écrire du code pour contrôler périodiquement l'état du téléchargement, ce qui garantirait que le script ne se termine pas prématurément. Cela fonctionne, mais il est beaucoup plus facile de simplement définir la valeur de la propriété Async sur False. En effet, le script se bloque alors jusqu'à ce que le téléchargement soit terminé. En d'autres termes, une fois le téléchargement démarré, le script attend patiemment qu'il se termine avant de faire quoi que ce soit d'autre.

Quant à la méthode loadXML, sa fonction correspond essentiellement à ce que son nom suggère : elle charge un document XML (ou un fragment de document) correctement formé dans notre document vide. Il nous suffit d'appeler la méthode loadXML, en transmettant la variable strXML comme paramètre unique de la méthode :

objXMLDoc.loadXML(strXML)

Le résultat ? Nous avons maintenant converti nos données WinRM en document XML virtuel. Cela signifie que nous pouvons commencer à utiliser les méthodes XML standard pour traiter ce document virtuel.

À cette fin, nous devons ensuite utiliser la ligne de code suivante pour créer une référence d'objet à l'élément racine du fichier XML :

Set objElement = objXMLDoc.documentElement

À ce stade, nous sommes prêts à nous amuser un peu. (En supposant, bien sûr, que l'analyse d'un fichier XML corresponde à votre vision de l'amusement. Pour nous scripteurs, c'est indéniablement très amusant.)

Comme vous vous en souvenez peut-être, notre requête WQL (WMI Query Language) ou, pour utiliser la terminologie WinRM, notre Filtre, ressemble à ceci :

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

Comme vous pouvez le voir, nous avons interrogé deux propriétés de la classe Win32_Service : Name et DisplayName. (Nous avons également inclus une clause Where qui limite nos données renvoyées aux services en cours d'exécution. Mais nous ne devons pas nous en préoccuper ici.) Est-ce important que nous ayons interrogé deux propriétés ? L'ordre de cette requête est-il important ? Peut-être. Comment diable sommes-nous censés le savoir ?

Oh, bien vu. Peut-être, en tant qu'auteurs de cet article, devrions-nous connaître les réponses à ces questions. D'accord. Il se trouve que nous savons que la réponse à ces deux questions est oui. Est-ce important que nous interrogions deux propriétés dans notre requête WQL ? Oui, ça l'est, car après tout, ce seront les seules valeurs de propriétés qui nous seront renvoyées. (À la différence d'une requête Select * From, qui renvoie des valeurs pour toutes les propriétés d'une classe.)

Donc ensuite, l'ordre de ces deux propriétés est-il également important ? Et comment ! L'ordre dans lequel nous spécifions les noms de propriétés est le même ordre que celui dans lequel les valeurs de propriété sont renvoyées. Cela est important parce que chaque valeur de propriété sera renvoyée sous forme de nœud enfant (ou section) de notre élément racine. Quelle valeur de propriété reviendra en tant que premier nœud enfant ? Facile. Dans ce cas-ci, le premier nœud enfant sera la propriété Name, parce qu'elle est la première propriété figurant dans notre requête WQL. Quelle propriété reviendra comme second nœud enfant ? Effectivement, ce sera la propriété DisplayName, parce qu'elle figure en deuxième position dans notre requête.

Attendez un instant. Vous avez trouvé cela tout seul, ou quelqu'un vous a-t-il montré un exemplaire de cet article à l'avance ? Hmmmm...

Quoi qu'il en soit, cette méthode permet de récupérer facilement la valeur de la propriété Name. Il nous suffit de référencer le NodeValue du premier élément (l'élément 0) dans la première collection ChildNodes (l'article 0), comme ceci :

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

Et comment référençons-nous la valeur de la propriété DisplayName ? Dans ce cas-ci, nous référençons le NodeValue du premier élément dans la deuxième collection ChildNodes (l'élément 1) :

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

Et que se passe-t-il si nous avons une troisième propriété (disons, Status) dans notre requête WQL ? Dans ce cas, nous référençons simplement le NodeValue du premier élément dans la troisième collection ChildNodes (élément 2) :

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

Et ainsi de suite jusqu'à la dernière propriété.

Donc, à quoi va ressembler notre résultat de script maintenant ? À quelque chose comme ceci :

Display Name: Windows Event Log
Name: EventLog

Display Name: COM+ Event System
Name: EventSystem

D'accord, cela n'a pas l'air très différent du résultat par défaut de WinRM (bien que nous soyons débarrassés de ce stupide titre XmlFragment). La différence tient en ceci : en travaillant avec les valeurs individuelles des propriétés, nous ne sommes plus limités au format par défaut (notez que nous utilisons l'étiquette Display Name au lieu de DisplayName), et nous ne sommes plus obligés d'afficher les informations uniquement dans la fenêtre de commandes.

Vous préféreriez écrire ces données dans une feuille de calcul Excel ? C'est relativement facile. Pour commencer, insérez le bloc de code suivant (qui crée et configure une nouvelle feuille de calcul Excel) immédiatement après la ligne du script WinRM qui appelle la méthode Enumerate :

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"

Maintenant, remplacez la boucle Do Until par celle illustrée à la figure 2. Faites un essai et voyez ce que vous obtenez.

Figure 2 La nouvelle boucle Do Until

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, 3e partie ?

Entre l'article de ce mois et le précédent, vous devriez en savoir assez pour démarrer avec WinRM. Nous l'espérons ! WinRM est une nouvelle technologie passionnante qui promet de faciliter considérablement la gestion à distance de vos ordinateurs (tout en maintenant la sûreté et la sécurité). Ce qui, bien sûr, nous amène directement à la question qui obsède tout le monde : ce la signifie-t-il qu'il y aura un troisième volet à la saga WinRM ?

Nous ne pouvons malheureusement pas révéler cette information. Pas à cause de problèmes de sécurité, mais simplement parce qu'en raison de l'agenda normal et du processus de décision des scripteurs, nous ne savons absolument pas si nous allons écrire quoi que ce soit d'autre sur WinRM. Comme on dit, tenez-vous au jus !

Les scripts embrouillés du Dr. Scripto

Docteur Scripto a eu un petit accident. Il a laissé l'un de ses scripts traîner (au lieu de le ranger comme tout bon scripteur) et il a trébuché accidentellement dessus, ce qui a fait voler des morceaux de script partout. Il a réussi à trouver toutes les variables, les mots clés, les symboles et autres, et il les a gentiment rangés par ordre alphabétique, mais maintenant il doit les remettre ensemble pour recréer son script complet. Cela va lui prendre un peu de temps, mais il compte avoir la réponse au moment de la publication du TechNet Magazine du mois prochain. D'ici là, n'hésitez pas à vous y essayer et voyez si vous réussissez à faire rentrer cette série de morceaux de script apparemment aléatoire dans un script correct et complet. Bonne chance !

Petite astuce : OK, voici une petite piste. Le script final supprimera sur l'ordinateur local tous les fichiers antérieurs à une date spécifiée.

ANSWER:

Les scripts embrouillés du Dr. Scripto

Réponse : Les maladresses du Dr. Scripto, décembre 2007

Oui, Dr. Scripto a réussi à réassembler son script. Voici le script reconstruit, opérationnel qui supprime sur l'ordinateur local tous les fichiers antérieurs à une date spécifiée :

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

Les scripteurs Microsoft travaillent pour (enfin, sont employés par) Microsoft. Lorsqu’ils ne sont pas en train de jouer au base-ball, d’entraîner une équipe ou de regarder un match (ou de se livrer à de multiples autres activités), ils dirigent le Script Center TechNet. Vous pouvez le vérifier vous-même sur www.scriptingguys.com.

© 2008 Microsoft Corporation et CMP Media, LLC. Tous droits réservés. Toute reproduction, totale ou partielle, est interdite sans autorisation préalable.