Ei, Equipe de Scripts!O retorno do WinRM

The Microsoft Scripting Guys

Quando a Equipe de Scripts se propôs a criar uma série de duas partes sobre o Windows® Remote Management (WinRM), uma questão importante nos saltou aos olhos: segurança. Afinal, nós da Equipe de Scripts sabíamos muito bem dos problemas relacionados ao lançamento do capítulo final da série Harry Potter: exemplares previamente reservados foram enviados acidentalmente com antecedência e entregues antes da data do lançamento oficial do livro. (Isso se tornou um problema? Não, claro que não; a editora apenas pediu para que as pessoas não lessem o livro até a data de lançamento.)

E não chegamos à metade da história. O livro e o desfecho da série vazaram muito antes dos exemplares estarem disponíveis. (Se você não leu o livro ainda, eis o que acontece: no final, Harry Potter é, na verdade, um tipo de mágico ou coisa parecida!) Da mesma forma, cópias digitalizadas de todas as páginas foram disponibilizadas instantaneamente na Internet e, em alguns casos, até mesmo antes da autora J. K. Rowling escrevê-las. De uma forma geral, isso foi uma amostra da ruína da segurança, e a Equipe de Scripts estava decidida a não deixar isso acontecer com ela. Afinal, se as pessoas estavam tão determinadas a vazar o final da série Harry Potter, imagine até onde elas poderiam ir para conseguir ter em mãos o desfecho da série de duas partes da Equipe de Scripts sobre o WinRM.

Felizmente, a Equipe de Scripts conseguiu ser hábil e manter seus segredos, quer dizer, segredo. Tudo bem, isso aconteceu principalmente pelo fato de que ela realmente não escreveu a segunda parte desta série até bem depois do prazo final para o envio do artigo. Mas, espere lá, o responsável da Equipe de Scripts que escreve esta coluna estava em férias durante o mês de agosto e, surpreendentemente, ao contrário de uma grande porcentagem de funcionários da Microsoft, ele sequer olha, tampouco usa, um computador quanto está em férias.

Certo, então: ele raramente olha ou sequer usa um computador, mesmo quando não está em férias. Mas essa é outra história.

Cada um à sua medida, sabemos que muitos de vocês não conseguiram dormir durante as últimas quatro semanas, se debatendo e com a preocupação de como a saga do WinRM terminará. Bem, a boa notícia é que essas longas semanas de agonia e de insônia acabaram. Aqui, em primeiríssima mão, está a emocionante conclusão da série de duas partes da Equipe de Scripts sobre o WinRM. Bem, na verdade, está tudo lá, na Figura 1.

Figure 1 The denouement

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

Sim, já sabemos: a impressionante reviravolta deu calafrios também na gente. objElement.ChildNodes(0).ChildNodes(0).NodeValue! E quem sabia disso? É claro: é porque, como parte do nosso bloqueio de segurança, até mesmo a Equipe de Scripts não fazia idéia de como a série acabaria. Mas agora o segredo já foi revelado.

Antes de irmos adiante, devemos recapitular rapidamente a série como um todo, apenas caso haja alguém que, inexplicavelmente, deixou de ler a Parte 1 (que pode ser encontrada em technetmagazine.com/issues/2007/11/HeyScriptingGuy). Voltando à Parte 1, nós apresentamos o WinRM, uma nova tecnologia, encontrada no Windows Server® 2003 R2, no Windows Vista® e no Windows Server 2008, que simplifica muito o gerenciamento de computadores na Internet, mesmo por meio de firewalls. Tudo bem, o WMI (Instrumentação de Gerenciamento do Windows) sempre contou com a capacidade de gerenciar computadores remotamente; no entanto, o WMI depende do Distributed COM (DCOM) como tecnologia de gerenciamento remoto. Tudo bem, exceto pelo fato de que, por padrão, muitos firewalls bloqueiam o tráfego DCOM. É verdade ser possível abrir as portas apropriadas e permitir o tráfego DCOM, mas muitos administradores de rede relutam em fazer isso; sua principal preocupação é que a abertura para DCOM também abrirá, simultaneamente, a porta para todos os tipos de dano mal-intencionado.

Por isso, o WinRM, é "a implantação da Microsoft do protocolo WS-Management, um protocolo padrão baseado em SOAP que permite a interoperabilidade entre hardwares e sistemas operacionais de diferentes fornecedores". Na verdade, trata-se apenas de uma forma mais elaborada de dizer que agora é possível realizar o gerenciamento remoto usando protocolos de Internet padrão como HTTP e HTTPS.

Como observamos no mês passado, o WinRM facilita a conexão e a recuperação das informações do WMI de computadores remotos. Então isso quer dizer se tratar de uma tecnologia absolutamente perfeita? Bem, não, não exatamente. Como dissemos, quando o WinRM envia os dados novamente para o script de chamada, eles retornam em formato XML. Nem é preciso dizer que XML pode ser um pouco confuso de analisar e usar, especialmente para administradores do sistema com pouca experiência nessa área. Por isso, o WinRM acompanha uma transformação XSL que converte os dados retornados em um formato mais legível.

Isso é ótimo, exceto por isso também significar que a saída sempre será mais ou menos assim:

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

Não se trata de algo necessariamente ruim, exceção feita ao fato de também significar que a saída será sempre gravada na janela de comando; por padrão, não é possível salvar prontamente os dados em um arquivo de texto, gravá-los em um banco de dados ou em uma planilha do Microsoft® Excel®, ou ainda fazer muita coisa com os dados senão exibir as informações na tela. Isso ainda não está bom.

Além de tudo, o problema é exacerbado caso você opte por retornar apenas um número selecionado de propriedades de uma classe do WMI (algo que você pode fazer para ajudar a reduzir o tráfego de rede). Ao trabalhar com apenas algumas propriedades de uma classe – e não todas elas – você obtém uma saída semelhante a esta:

XmlFragment DisplayName = Nome do log de eventos do Windows = EventLog

XmlFragment DisplayName = Nome do sistema de eventos do COM+ = EventSystem

Interessante, mas não a mais estética forma de exibição das informações que já vimos (especialmente o título XmlFragment exibido em todo o relatório).

E o que você pode fazer a respeito? Escrever códigos personalizados para analisar e formatar esses dados XML? Isso não parece ser algo que um criador de scripts para a administração do sistema deva fazer. Ou parece?

Se quiser que algo seja feito corretamente, faça você mesmo

Acontece que trabalhar com os dados XML brutos retornados pelo WinRM não é uma tarefa tão árdua quanto possa parecer. Neste mês, mostraremos a você uma forma simples de analisar e formatar dados XML. A abordagem que usaremos não é, de maneira nenhuma, a única forma de trabalho com XML, mas tudo bem; o nosso principal objetivo aqui é apenas demonstrar que você não precisa depender da transformação XSLT. Uma vez compreendida a idéia básica, bem, daí em diante o céu é o limite em relação ao que você pode fazer com os dados do WinRM.

Há um pequeno detalhe aqui: para que a coluna caiba no espaço destinado a ela, nós ignoraremos grande parte do script que acabamos de lhe mostrar na Figura 1. No entanto, isso não deve significar um grande problema, porque os dois primeiros terços do script foram descritos detalhadamente na coluna do mês passado. Na verdade, nos atentaremos para o terceiro terço do script, a parte em que efetivamente trabalhamos com os dados retornados.

Como você pode ver, este episódio das Crônicas do WinRM se dá depois que nós criamos uma instância do objeto WSMan.Automation, consultamos um computador remoto (no caso, atl-fs-01.fabrikam.com) e recebemos informações sobre todos os serviços em execução no computador. Isso nos traz de volta à linha no script em que configuramos um loop Do Until para leitura e processamento dos dados XML retornados; esse loop continuará até que não haja nada a ser lido e processado. (Ou, para deixar claro que sabemos do que estamos falando, o loop continuará até o momento em que a propriedade AtEndOfStream do arquivo XML for True.)

Esta é a linha de código de que falamos, e é onde a história deste mês começa:

Do Until objResponse.AtEndOfStream

Dentro do loop, a primeira coisa que fazemos é usar o método ReadItem para ler a primeira seção dos dados XML retornados. (Como estamos trabalhando com o WinRM e recuperando as informações do serviço, a primeira seção consistirá em todos os dados retornados referentes ao primeiro serviço da nossa coleção.) Depois de armazenar esses dados (que, mais uma vez, estão no formato XML) em uma variável chamada strXML, criamos uma instância do objeto Microsoft.XMLDom. Dessa forma, isso nos dá um documento XML em branco com o qual podemos trabalhar:

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

Assim que o nosso documento em branco estiver pronto, definimos o valor da propriedade Async como False e chamamos o método loadXML.

O que nos traz a – o que foi mesmo que você disse? Por que definimos o valor da propriedade Async como False? E por que chamamos o método loadXML? Boas perguntas; se tivéssemos pensado nelas sozinhos!

Para os iniciantes, a propriedade Async indica se o script permite um download assíncrono das informações XML. Se essa propriedade for True, o download começará e o controle retornará imediatamente para o script, mesmo que o download não tenha terminado. Tudo bem, isso parece ser ótimo. Mas, infelizmente, o script continuará como se tivesse todas as informações necessárias. E caso ele não tenha todas as informações de que precisa, bem, aí é melhor você se preparar para os problemas. É por isso que definimos o valor da propriedade Async como False.

Observação: certo, você poderia escrever um código para monitorar periodicamente o status do download, o que garante que o script não seja encerrado de maneira prematura. Isso funciona, mas uma abordagem muito mais simples é definir o valor da propriedade Async como False. Quando você faz isso, o script permanece bloqueado até a conclusão do download; em outras palavras, assim que o download começar, o script aguardará pacientemente a sua conclusão antes de fazer qualquer outra coisa.

Já em relação ao método loadXML, ele basicamente faz o que o nome sugere: carrega um documento XML bem formado (ou fragmento de documento) no nosso documento em branco. Tudo o que temos de fazer é chamar o método loadXML, passando a variável strXML como sendo o único parâmetro do método:

objXMLDoc.loadXML(strXML)

O resultado líquido? Agora nós transformamos os nossos dados do WinRM em um documento XML virtual. E isso significa que podemos começar a usar métodos XML padrão para analisar esse documento virtual.

Para isso, precisamos usar a seguinte linha de código a fim de criar uma referência de objeto para o elemento raiz no arquivo XML:

Set objElement = objXMLDoc.documentElement

Neste ponto, estamos prontos para alguma diversão. (Considerando, é claro, que a sua idéia de diversão seja de análise de um arquivo XML. É certamente isso o que nós da Equipe de Scripts consideramos divertido.)

Como você deve se lembrar, a nossa consulta WQL (WMI Query Language) (ou, usando a tecnologia WinRM, o nosso Filter) é semelhante à seguinte:

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

Como você pode ver, solicitamos duas propriedades da classe Win32_Service: Name e DisplayName. (Também incluímos uma cláusula Where que limita os nossos dados retornados aos serviços em execução. No entanto, não há nada com o que devemos nos preocupar aqui.) É importante o fato de termos solicitado duas propriedades? A ordem dessa solicitação é importante? Talvez. E como deveríamos saber disso?

Ah, bem observado. Talvez, por sermos os autores deste artigo, nós realmente devêssemos saber as respostas para essas perguntas. Tudo bem. Acontece que sabemos que a resposta para essas duas perguntas é sim. É importante o fato de termos solicitado duas propriedades em nossa consulta WQL? Sim, é; afinal, esses serão os únicos valores de propriedade retornados para nós. (Ao contrário de fazer uma consulta Select * From, que retorna valores para todas as propriedades de uma classe.)

Então a ordem dessas duas propriedades também é importante? Pode apostar que sim. A ordem em que especificamos os nomes de propriedade é a mesma na qual os valores de propriedade são retornados. Isso é importante porque todos os valores de propriedade serão retornados como um nó filho (ou seção) do nosso elemento raiz. Qual será o valor de propriedade a retornar como sendo o primeiro nó filho? Esta é uma pergunta fácil. Nesse caso, o primeiro nó filho será a propriedade Name, porque essa é a primeira propriedade listada em nossa consulta WQL. Qual será a propriedade a retornar como sendo o segundo nó filho? Certo, será a propriedade DisplayName, por se tratar do segundo item listado em nossa consulta.

Espere um pouco. Você chegou a essa conclusão sozinho ou alguém vazou uma cópia desta coluna em primeira mão para você? Hmmmm...

De qualquer forma, isso simplifica a recuperação do valor da propriedade Name. Tudo o que precisamos fazer é referenciar NodeValue do primeiro item (item 0) na primeira coleção ChildNodes (item 0), assim:

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

E como referenciamos o valor da propriedade DisplayName? Nesse caso, referenciamos o NodeValue do primeiro item na segunda coleção ChildNodes (item 1):

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

E se tivéssemos uma terceira propriedade (digamos, Status) em nossa consulta WQL? Nesse caso, simplesmente referenciaríamos o NodeValue do primeiro item na terceira coleção ChildNodes (item 2):

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

E assim por diante até você chegar à última propriedade.

E como ficará a saída do nosso script agora? Algo parecido com:

Display Name: Nome do log de eventos do Windows: EventLog

Display Name: Nome do sistema de eventos do COM+: EventSystem

Tudo bem, isso não parece ser tão diferente em relação à saída do WinRM padrão (muito embora tenhamos conseguido nos livrar daquele título XmlFragment tolo). A diferença é: trabalhando com os valores de propriedade individuais, nós não estamos mais limitados à formatação padrão (observe que usamos o rótulo Display Name, e não DisplayName), nem restritos à exibição das informações apenas na janela de comando.

Mas você acharia melhor gravar esses dados em uma planilha do Excel? Isso é fácil de fazer. Para começar, insira o seguinte bloco de código (que cria e configura uma nova planilha do Excel) logo após a linha no script do WinRM que chama o método 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"

Agora substitua o loop Do Until original pelo exibido na Figura 2. Faça uma tentativa e veja o que acontece.

Figure 2 New Do Until loop

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?

Entre as colunas deste mês e do mês passado, você viu o bastante para começar a usar o WinRM. Esperamos que sim; o WinRM é uma nova tecnologia intrigante que promete tornar o gerenciamento remoto dos computadores muito mais fácil (ao mesmo tempo em que mantém a segurança). O que, é claro, nos remete diretamente à pergunta que está na cabeça de todos: isso quer dizer que haverá um terceiro capítulo da saga sobre o WinRM?

Desculpem, mas não podemos revelar essa informação. Não por conta de preocupações com a segurança, mas apenas devido ao processo de tomada de decisão e de planejamento habituais da Equipe de Scripts, não temos a menor idéia se escreveremos mais alguma coisa sobre o WinRM. Como dizem por aí, fique ligado!

O desafio do script do Dr. Scripto

Houve um pequeno acidente com o Doctor Scripto. Ele deixou um de seus scripts por aí (em vez de guardá-lo em um bom criador de scripts), e tropeçou por acidente sobre ele, mandando pedaços do script pelos ares. Ele conseguiu encontrar todas as variáveis, palavras-chave, símbolos e, assim, os colocou em ordem alfabética, mas agora precisa reuni-los novamente para recriar todo o script. Vai demorar um pouco, mas ele espera ter a resposta pronta quando a TechNet Magazine do próximo mês for lançada. Enquanto isso, sinta-se à vontade para fazer uma tentativa e ver se você consegue transformar esse conjunto aparentemente aleatório de scripts em um script completo. Boa sorte!

Dica: certo, aqui está uma pequena ajuda. O script final excluirá todos os arquivos do computador local que tiverem uma data posterior à especificada.

ANSWER:

O desafio do script do Dr. Scripto

Resposta: o atarefado Doctor Scripto, dezembro de 2007

Sim, o Dr. Scripto conseguiu reunir novamente seu script. Aqui está o script reconstruído, funcional, que exclui todos os arquivos do computador local que tiverem uma data posterior à especificada:

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

The Microsoft Scripting Guys trabalha para a — bem, é empregada da — Microsoft. Quando não está jogando/treinando/assistindo beisebol (e diversas outras atividades), ela administra o Script Center da TechNet. Confira no site www.scriptingguys.com.

© 2008 Microsoft Corporation e CMP Media, LLC. Todos os direitos reservados. A reprodução parcial ou completa sem autorização é proibida..