Hey, Scripting Guy!Il ritorno di WinRM

Gli Scripting Guy di Microsoft

Quando gli Scripting Guy hanno deciso di creare una serie in due parti su Gestione remota Windows® (WinRM), si sono subito imbattuti in un problema importante: la protezione. Dopotutto, noi Scripting Guy eravamo ben consapevoli dei problemi che hanno afflitto l'uscita dell'ultima puntata della saga di Harry Potter: le copie dei libri ordinate in anticipo sono state spedite e consegnate inavvertitamente prima della data ufficiale di uscita del libro (ma si è poi trattato di un vero problema? No di certo; la casa editrice si è limitata a chiedere ai clienti di non di leggere il libro fino alla data di uscita).

Ma il bello deve ancora venire. Il finale di libro e serie è stato rivelato prima ancora che i libri fossero distribuiti alle librerie (se non l'hai ancora letto, ecco come va a finire: pare che Harry Potter sia diventato una specie di mago o qualcosa del genere). Allo stesso modo, le copie di tutte le pagine digitalizzate sono state immediatamente rese disponibili su Internet, in alcuni casi addirittura prima che l'autore J. K. Rowling le scrivesse. Insomma, una bella falla nei sistemi di protezione: gli Scripting Guy erano quindi determinati a impedire che ciò accadesse anche a loro. D'altra parte, se tante persone si sono dimostrate così determinate a svelare il finale della saga di Harry Potter, è facile immaginare ciò che sarebbero capaci di fare per mettere le mani sul finale della serie in due parti su WinRM degli Scripting Guy.

Per fortuna, gli Scripting Guy sono riusciti a tenere i segreti, per l'appunto, segreti. È vero, il motivo principale di tale notevole successo è che gli Scripting Guy hanno scritto effettivamente la seconda parte della serie molto dopo la scadenza prevista per la presentazione dell'articolo. Ma diamine, lo Scripting Guy che scrive questo articolo è andato in vacanza ad agosto e, a differenza di una percentuale sorprendentemente grande di dipendenti Microsoft, non guarda mai un computer mentre è in vacanza e di usarlo nemmeno se ne parla.

Ecco, a dire il vero guarda raramente, e usa ancora meno, un computer anche quando non è in vacanza. Ma questa è un'altra storia.

In ogni caso sappiamo che molti degli utenti hanno dormito poco e male nelle ultime quattro settimane, preoccupati e impazienti di sapere come sarebbe terminata la saga di WinRM. Bene, le settimane dolorosamente lunghe di attesa insonne sono terminate. Qui, per la prima volta nella storia, è raccontata la fine della strabiliante serie in due parti su WinRM degli Scripting Guy. Bene, in effetti, finisce alla Figura 1.

Figure 1 Epilogo

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

Sì, è vero: anche l'imprevedibile stravolgimento della trama ha impressionato e fatto correre lunghi brividi sulla schiena dei lettori. objElement.ChildNodes(0).ChildNodes(0).NodeValue! Chi poteva aspettarselo? In verità, data l'efficacia del dispositivo di protezione, nemmeno gli Scripting Guy avevano idea di come si sarebbe conclusa la serie. Ma ora il segreto è svelato.

Prima di procedere oltre, è il caso di riassumere rapidamente l'intera serie, per tutti quelli che per qualche misterioso motivo non hanno letto la Parte 1 (disponibile all'indirizzo technetmagazine.com/issues/2007/11/HeyScriptingGuy). Nella Parte 1 è stata presentata la nuova tecnologia WinRM, disponibile in Windows Server® 2003 R2, Windows Vista® e Windows Server 2008, che semplifica la gestione di computer su Internet, anche in presenza di firewall. Se è vero che Strumentazione gestione Windows (WMI) ha sempre consentito di gestire i computer in modalità remota, tuttavia la tecnologia di gestione remota su cui si fonda WMI è DCOM. Ottima scelta se non fosse per il fatto che la maggior parte dei firewall, per impostazione predefinita, blocca il traffico DCOM. È comunque possibile aprire le porte necessarie e consentire il traffico DCOM, ma molti amministratori di rete non sono entusiasti di questa soluzione, preoccupati come sono di evitare che l'apertura di una porta per DCOM possa rappresentare l'apertura di una breccia per ogni sorta di codice dannoso.

Da queste considerazioni è nato WinRM, "l'implementazione Microsoft del protocollo WS-Management, un protocollo standard, basato su SOAP, di facile integrazione con i firewall, che consente all'hardware e ai sistemi operativi di diversi fornitori di interoperare". Un modo piuttosto elaborato per dire che ora si può eseguire la gestione remota con i protocolli Internet standard, come HTTP e HTTPS.

Come già sottolineato il mese scorso, WinRM semplifica la connessione a computer remoti e il recupero delle informazioni WMI presenti in tali computer. Questo significa che si tratta di una tecnologia assolutamente perfetta? Non è esattamente così. Come già evidenziato, quando WinRM invia di nuovo i dati allo script che ha eseguito la chiamata, i dati vengono restituiti in formato XML. Inutile dire che il formato XML può risultare un po' complesso da analizzare e utilizzare, soprattutto per gli amministratori di sistema con esperienza limitata in questa area specifica. È per questo che WinRM viene fornito con una trasformazione XSL che converte i dati restituiti in un formato più leggibile.

Anche questa è un'ottima idea, peccato che però l'output tenda sempre a somigliare al seguente:

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

Questo non è necessariamente uno svantaggio, ma significa anche che l'output verrà sempre scritto nella finestra di comando; per impostazione predefinita, non è possibile salvare direttamente i dati in un file di testo, scrivere i dati in una database o in un foglio di calcolo di Microsoft® Excel® oppure eseguire qualsiasi altra operazione con i dati, oltre a visualizza le informazioni sullo schermo. Ma questo raramente basta.

Per di più, il problema si aggrava quando si decide di restituire solo un certo numero di proprietà per una classe WMI (per ridurre, ad esempio, il traffico in rete). Quando si lavora con un numero ridotto di proprietà di una classe, invece che con tutte le proprietà di una classe, si ottiene un output simile al seguente:

XmlFragment
    DisplayName = Windows Event Log
    Name = EventLog

XmlFragment
    DisplayName = COM+ Event System
    Name = EventSystem

Interessare, ma non è certo la migliore schermata di dati che si sia mai vista (soprattutto per l'intestazione XmlFragment che compare in tutto il report).

Cosa si può fare per renderla migliore? Scrivere del codice personalizzato per analizzare e formattare i dati XML? Non sembra un'operazione particolarmente adatta a un amministratore di sistema. O forse lo è?

Chi fa da se, fa per tre

Utilizzare i dati XML non formattati restituiti da WinRM non è difficile come sembra. Di seguito verrà illustrato un modo semplice di analizzare e formattare i dati XML. L'approccio utilizzato nell'esempio non è certo l'unico possibile per operare sui dati in formato XML, ma è un buon metodo; lo scopo principale consiste nel dimostrare che non è necessario fare affidamento sulla trasformazione XSLT. Una volta afferrato il concetto di base, non vi saranno limiti alle possibilità di manipolazione dei dati di WinRM.

Piccola avvertenza: per motivi di spazio, non verrà illustrata la maggior parte dello script riportato nella Figura 1. Ciò non dovrebbe costituire un problema, tuttavia, dal momento che i primi due-terzi dello script sono stati descritti in dettaglio nell'articolo del mese scorso. L'attenzione si concentrerà, invece, sull'ultimo terzo dello script, ovvero sulla parte in cui si lavora effettivamente con i dati restituiti.

Come si può notare, questo episodio delle Cronache di WinRM iniziano dalla creazione di un'istanza dell'oggetto WSMan.Automation, dalla query a un computer remoto (in questo caso, atl-fs-01.fabrikam.com) e dalle informazioni ricevute relative a tutti i servizi in esecuzione su tale computer. Questo ci riporta alla riga dello script in cui si imposta un loop Do Until per leggere ed elaborare i dati XML restituiti. Il loop continua fino a che non resta più niente da leggere ed elaborare (oppure, tanto per dare l'impressione che sappiamo quel che diciamo, il loop continua fino a quando la proprietà AtEndOfStream del file XML non diventa True).

Ecco la riga di codice e questo è l'inizio della storia di questo mese:

Do Until objResponse.AtEndOfStream

All'interno del loop, la prima operazione eseguita è l'utilizzo del metodo ReadItem per la lettura della prima sezione dei dati XML restituiti. Poiché si utilizza WinRM e poiché vengono recuperate le informazioni del servizio, questa prima sezione consisterà in tutti i dati restituiti per il primo servizio dell'insieme. Dopo la memorizzazione dei dati (ancora una volta, in formato XML) in una variabile denominata strXML, si crea un'istanza dell'oggetto Microsoft.XMLDom. In tal modo si ottiene un documento XML vuoto con cui lavorare:

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

Una volta approntato il documento vuoto, si imposta il valore della proprietà Async su False, quindi si chiama il metodo loadXML.

A questo punto... come dici? Perché si imposta il valore della proprietà Async su False? E perché si denomina il metodo loadXML? Ottime domande! Avremmo dovuto pensarci noi.

Per i principianti, la proprietà Async indica se lo script consente un download asincrono di informazioni XML. Se il valore della proprietà è True, il download viene avviato e il controllo viene restituito immediatamente allo script, anche se il download non è ancora terminato. Una funzionalità davvero utile. Purtroppo, in questo caso, lo script procederebbe come se disponesse già di tutte le informazioni necessarie. Quando uno script rileva che le informazioni necessarie non sono tutte disponibili, cominciano guai seri. Ecco perché nell'esempio proprietà Async è impostata su False.

Nota: si potrebbe scrivere del codice per monitorare periodicamente lo stato del download, garantendo che lo script non proceda in modo prematuro. È una soluzione praticabile, ma impostare il valore della proprietà Async su False è un approccio molto più semplice. Infatti lo script si blocca fino a che il download non è completo; in pratica, una volta iniziato il download, lo script resta pazientemente in attesa della fine del download prima di procedere.

Quanto al metodo loadXML, l'operazione che esegue è quella suggerita dal nome: carica un documento XML (o un frammento di documento) ben formattato nel documento vuoto. A tale scopo, è sufficiente richiamare il metodo loadXML, passando la variabile strXML quale unico parametro del metodo:

objXMLDoc.loadXML(strXML)

Il risultato finale? I dati WinRM sono stati trasformati in un documento XML virtuale. Ne consegue che è possibile iniziare a utilizzare i metodi XML standard per analizzare il documento virtuale.

A questo punto, è necessario utilizzare la riga di codice seguente per creare un riferimento oggetto all'elemento radice del file XML:

Set objElement = objXMLDoc.documentElement

Ora tutto è pronto per iniziare a divertirsi (sempre che si condivida la stessa idea di divertimento degli Scripting Guy, che corrisponde a eseguire l'analisi di un file XML).

Come si ricorderà, la query WQL (o, utilizzando la terminologia WinRM, il filtro) ha l'aspetto seguente:

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

Come si può vedere, si sono richieste due proprietà dalla classe Win32_Service: Name e DisplayName. È presente anche una clausola Where che limita i dati restituiti ai servizi in esecuzione, ma non è il caso di occuparsene qui. È importante che siano state richieste due proprietà? L'ordine della richiesta è importante? Forse. Come forse?

L'obiezione è lecita. Forse, in qualità di autori dell'articolo, dovremmo avere una risposta a queste domande. Va bene. Per quanto ne sappiamo, la risposta a entrambe le domande è sì. È importante che siano state richieste due proprietà nella query WQL? Sì lo è; dopotutto, saranno questi i soli valori della proprietà restituiti (al contrario di quanto accade con una query Select * From, che restituisce i valori per tutte le proprietà di una classe).

L'ordine delle due proprietà è importante? Certo che lo è. L'ordine in cui si specificano i nomi di proprietà corrisponde all'ordine in cui vengono restituiti i valori della proprietà. È importante perché ciascun valore della proprietà viene restituito come nodo figlio (o sezione) dell'elemento radice. Quale valore della proprietà viene restituito come primo nodo figlio? La risposta è facile. In questo caso il primo nodo figlio sarà la proprietà Name, perché si tratta della prima proprietà riportata nella query WQL. Quale proprietà viene restituita come secondo nodo figlio? Esatto, la proprietà DisplayName, perché è la seconda voce riportata nella query.

Un momento. L'hai indovinato da solo o qualcuno ti ha passato una copia non autorizzata dell'articolo? Hmmmm...

In ogni caso, questo facilita la restituzione del valore della proprietà Name. Non resta che impostare un riferimento a NodeValue del primo elemento (elemento 0) nel primo insieme ChildNodes (elemento 0), come segue:

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

Come si imposta il riferimento al valore della proprietà DisplayName? In questo caso il riferimento a NodeValue del primo elemento viene impostato nel secondo insieme ChildNodes (elemento 1):

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

E se nella query WQL fosse disponibile una terza proprietà (ad esempio, Status)? In tal caso il riferimento a NodeValue del primo elemento verrebbe impostato nel terzo insieme ChildNodes (elemento 2):

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

E così via fino all'ultima proprietà.

A questo punto che aspetto ha assunto l'output dello script? Qualcosa di simile a quanto segue:

Display Name: Windows Event Log
Name: EventLog

Display Name: COM+ Event System
Name: EventSystem

È vero che non sembra essere molto diverso dall'output WinRM predefinito (anche se quell'inutile intestazione XmlFragment ora non compare più). Una differenza esiste ed è questa: quando si lavora con singoli valori di proprietà, non è necessario limitarsi alla formattazione predefinita (si noti l'utilizzo dell'etichetta Display Name al posto di DisplayName) o alla sola visualizzazione delle informazioni nella finestra di comando.

È necessario scrivere i dati in un foglio di calcolo Excel? L'operazione è relativamente semplice. Per iniziare, inserire il blocco di codice seguente (che crea e configura un nuovo foglio di calcolo Excel) subito dopo la riga dello script WinRM che chiama il metodo 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"

Quindi sostituire il loop originale Do Until con quello illustrato nella Figura 2. Provare e verificare il risultato.

Figure 2 Nuovo loop 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 Parte 3?

Le informazioni fornite con questo articolo e con il precedente dovrebbero consentire di iniziare a utilizzare WinRM con profitto. Almeno si spera; WinRM è una nuova tecnologia molto interessante che promette di semplificare la gestione remota dei computer (senza compromettere sicurezza e protezione). Considerazione che fa nascere spontanea una domanda: questo significa che ci sarà una terza puntata della saga di WinRM?

L'informazione è riservata e non può essere rivelata. Non tanto per i rischi di protezione, ma più semplicemente perché a causa del solito processo di pianificazione e decisione, gli Scripting Guy non hanno la più pallida idea di come si concluderà davvero la saga di WinRM. Come si suol dire, resta sintonizzato!

Il rompicapo di script del Dottor Scripto.

Il Dottor Scripto ha avuto un piccolo incidente. Ha lasciato uno dei suoi script sul pavimento (invece di archiviarlo con cura, come fanno tutti i bravi autori di script) ed è inciampato sparpagliando accidentalmente i pezzi dello script in ogni dove. È riuscito a trovare tutte le variabili, le parole chiave, i simboli e così via e li ha disposti in ordine alfabetico, ma adesso deve riassemblarli di nuovo per ricostruire lo script. L'operazione gli costerà del tempo, ma conta di avere la risposta pronta per l'uscita del prossimo numero di TechNet Magazine. Nel frattempo, prova ad aiutarlo cercando di assemblare in uno script completo questa serie apparentemente casuale di pezzi di script. Buona fortuna!

Suggerimento: OK, ecco un piccolo aiuto. Lo script finale elimina dal computer locale tutti i file con una data precedente a quella specificata.

ANSWER:

Il rompicapo di script del Dottor Scripto.

Risposta: Il goffo Dottor Scripto, dicembre 2007

Sì, il Dott. Scripto è riuscito a rimettere insieme il suo script. Ecco lo script ricostruito e funzionante, che elimina dal computer locale tutti i file con una data precedente a quella specificata:

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

Gli Scripting Guy di Microsoft lavorano o, per meglio dire, sono stipendiati da Microsoft. Quando non si dedicano al baseball (o ad altre attività) come giocatori, allenatori o semplici spettatori, gestiscono il TechNet Script Center. Consultare la pagina www.scriptingguys.com.

© 2008 Microsoft Corporation e CMP Media, LLC. Tutti i diritti riservati. È vietata la riproduzione completa o parziale senza autorizzazione.