Wednesday, September 16, 2009 (Not) Using PowerShell v2 to View WSMan Data From ESX

Today I was trying to get at my “Health Status” data on my ESX servers though PowerShell using the tutorial posted on the PowerCLI Blog (Monitoring ESX Hardware with Powershell), but I kept getting an “Access Denied” message. I did a little more research into using WSMan in PowerShell and discovered that before V2, there was a way to use a COM object to query WSMan that gave you a little more control over the connection (http://technet.microsoft.com/en-us/magazine/2007.11.heyscriptingguy.aspx).

Here is a PowerShell script based on the PowerCLI script that uses the COM object from the Scripting Guy article (it assumes you’re already connected to your Virtual Center server):

 1 $vmhost = get-vmhost ""
 2 $view = get-view $vmhost.id
 3 
 4 $token = $view.acquireCimServicesTicket().sessionId
 5 
 6 $uri = "https:///wsman"
 7 $object = "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_NumericSensor"
 8 
 9 $wsman = new-object -com WSMan.automation
10 $options = $wsman.createConnectionOptions()
11 $options.username = $token
12 $options.password = $token
13 $flags = $wsman.sessionFlagUseBasic() -bor $wsman.sessionFlagCredUserNamePassword() -bor $wsman.sessionflagskipcacheck() -bor $wsman.sessionflagskipcncheck() -bor $wsman.sessionflagskiprevocationcheck()
14 
15 $session = $wsman.createSession($uri, $flags, $options)
16 $result = $session.enumerate($object)
17 
18 $xml = while(!$result.atendofstream) {
19   $result.readitem()
20 }

This proved that it was possible to pull this data from Windows, but I wanted to use my shiny new v2 cmdlets to do it (plus I wanted objects instead of XML that I was having problems casting without modification).

My next step was to sniff the network from my workstation and see exactly what Get-WSManInstance was passing to the ESX server for the username and password. That involved locating a version of Wireshark that had the SSL decryption libraries enabled (I’m using x64 Windows 7) and copying the private key from the ESX server to my workstation.

After installing many different versions of Wireshark (1.2.2 for x86 worked) , I discovered that OpenWSMan logs authentication messages (that include the username) to /var/log/messages on the ESX server. Either way you decide to go, what you will find is that Get-WSManInstance is pre-pending a ”" before the username:

Sep 16 21:34:00 xvhalp22 openwsman(pam_unix)[12388]: bad username [\5297c1ab-ecf9-43c1-6b4a-cb7dce63d813]

I decided to try and determine if the Powershell cmdlet was the culprit or if it was the .Net implementation of WSMan that was adding the extra character, so I did a little searching and came across this blog post that includes a function that allows you to pipe a cmdlet to it and it will locate the appropriate class and DLL and then load it up in Reflector (A trick to jump directly to a Cmdlet’s implementation in Reflector). The command would be:

PS> get-command Get-WSManInstance | Reflect-Cmdlet

What I found after a little digging is that in the CreateSessionObject function of the WSManHelper class, they are setting the credentials as follows:

 1 if (credential != null)
 2 {
 3   NetworkCredential networkCredential = new NetworkCredential();
 4   if (credential.get_UserName() != null)
 5   {
 6     networkCredential = credential.GetNetworkCredential();
 7     if (string.IsNullOrEmpty(networkCredential.Domain))
 8     {
 9       if (authentication.Equals(AuthenticationMechanism.Digest))
10       {
11          connectionOptions.UserName = networkCredential.UserName;
12       }
13       else
14       {
15         connectionOptions.UserName = @"\" + networkCredential.UserName;
16       }
17     }
18     else
19     {
20       connectionOptions.UserName = networkCredential.Domain + @"\" + networkCredential.UserName;
21     }
22     connectionOptions.Password = networkCredential.Password;
23     if ((!authentication.Equals(AuthenticationMechanism.Credssp) || !authentication.Equals(AuthenticationMechanism.Digest)) || authentication.Equals(AuthenticationMechanism.Basic))

I think line 15 is the cause of my problem and I believe this is a bug in the cmdlet. I’m not sure which “basic” authentication schemes would be expecting a username like “\user”, and in fact the System.Net.WebClient doesn’t add this leading slash when you use Basic and a PSCredential.

I haven’t tried this on the Vista or XP PowerShell v2 RC, but I have to assume that this behavior was introduced sometime after CTP3, or the PowerCLI Blog people would never have been able to connect.


comments powered by Disqus