Thursday, August 19, 2010

Managing Windows with Ruby (Part 1) or...

How I learned to stop worrying and manage Windows from Linux




Intro to Windows Remote Management

I have long been a UNIX guy and have always struggled living in a mixed OS environment. Traditionally Windows based systems have made it very hard to do things remotely from systems other than other Windows boxes. This is where Microsoft's implementation of the WS-Management Protocol comes into play. The newer versions of the OS (2003 R2, Vista, 2008, and Win7) all allow you to turn on Windows Remote Management (WinRm). This is a major step forward for heterogenious envirnments because WinRM communicates over HTTP and SOAP. For more information as well as instructions on how to configure WinRM, please visit Microsoft's WinRM site. If you would like to follow along and read the WinRM docs later you can simply open up an Admin Powershell and issue the following:

winrm quickconfig
This will create an HTTP listener on port 5985 by default. I still recommend that you read the docs for more information.

Once WinRM is configured and listening the hard part is really over. Now all we need to do is pass SOAP packets back and forth in order to tell the remote Windows box what we want it to do. I have created a Ruby library called 'winrm' that aids in the building and parsing of the SOAP packets so all you have to do is call your Powershell or run your WQL and go.


WinRM for Ruby Set-up

Before diving into the examples WinRM for Ruby needs a little set-up. First and foremost the gem needs to be installed. It is up on Rubygems.org so you can just do a:

gem install -r winrm
Every program that uses WinRM will have to set-up the following:
WinRM::WinRM.endpoint = 'http://mywinhost:5985/wsman'
WinRM::WinRM.set_auth('user','pass')
winrm = WinRM::WinRM.instance
The 'winrm' instance created on the last line will be used in the following examples.

Running Powershell scripts

Powershell has a nice little option called '-encodedCommand' that lets you run a Base64 encoded script. Because of this it is relatively simple to write Powershell scripts locally and run them remotely with WinRM. To do this with the Ruby library you can simply issue

winrm.powershell(script_file)
This will return an array in the form [stdout, stderr].

Just to clarify, these scripts are local to the machine running the WinRM for Ruby library. That means you can store them on your Linux desktop or wherever you store your scripts and run them from there.


Running WMI Queries (WQL)

WMI is invaluable when enumerating system information from a Windows system. One way at getting values out of WMI is via WMI Query Language (WQL). WQL is SQL-like in syntax and very easy to use. The WinRM Ruby library implements this feature with the WinRM::WinRM#wql method. You can issue WQL queries like this:

winrm.wql 'select * from Win32_Service'
This method will return an Array of Hashes. Each Hash will contain the name/value pairs retrieved from WMI. For instance, if your WQL was
'select Name,Status from Win32_Service'
your return would look something like this:
[
  {'Name' => 'Appinfo', 'Status' => 'OK'},
  {'Name' => 'Browser','Status' => 'OK},
...
]


Give it a try...

That's a quick overview of how to use WinRM. It's a very new library so enhancements will be coming and I hope to have a Part 2 to this post that discusses the actual SOAP protocol that WinRM uses. In the meantime, give it a try and let me know how you're using it in the comments. You can also clone the source on github or open an issue on Github if you run into any issues.


Github: http://github.com/zenchild/WinRM

Cheers!

2 comments:

  1. Also, in follow-up to this, here's an older article from Jeffrey Snover, that describes how to convert a string (eg. a PowerShell script) to Base64:

    http://blogs.msdn.com/b/powershell/archive/2006/04/25/583265.aspx

    ReplyDelete
  2. Two comments 1) I can't find the Base64.strict_encode64 in the library tree so I don't believe that is supported. 2) The Base64.encode64 returns a string with '\n' (new_line) characters and that seems to break the powershell script access. To fix that I added this after line 251 in winrm_service.rb - script.gsub!(/\n/, '') and then the powershell scripts work.

    ReplyDelete