Using WS-Man to invoke a Powershell Cmdlet

First, let me apologize for the lack of posts to this blog.  Out original team goal was to have a new post every month, but holidays/vacations/work got in the way.  I’ll try to restart this rhythm.  For this first post of the New Year, I’m going to cover a more technical topic.

Most Windows administrators should already be aware that Powershell is the preferred ITPro scripting and command-line experience.  Many products from Microsoft and also third-parties expose their management interface as Powershell cmdlets.  Since Powershell is currently only available on Windows, how can you manage Windows from a non-Windows box?

Although Powershell remoting is built on top of WS-Management, it’s really a protocol within a protocol and trying to interop with PSRP (Powershell Remoting Protocol) directly would essentially require replicating Powershell on the client.  The alternative is to make use of a lesser known remote command-line tool called WinRS.  WinRS is a simple utility allows remote cmd.exe and is also built on top of WS-Management.  The difference is that WinRS reuses Create and Delete from WS-Transfer and introduces a few new custom SOAP web-methods.  For this blog post, I’ll be focusing on the WinRS “protocol” and won’t be discussing WS-Transfer, SOAP, nor HTTP in detail.  The complete details of the WinRS WS-Management Protocol extensions is documented here:

WinRS has a relatively simple protocol, the workflow is:

1. WS-Transfer Create to create a shell, an EPR (End-Point Reference) to the created Shell is returned which is used for the next set of operations
2. Invoke the Command custom SOAP action to start a new command
3. Invoke the Receive custom SOAP action to receive output from the command (there is a corresponding Send for sending input, but it’s not needed for this scenario)
4. repeat step 3 until CommandState is Done
5. WS-Transfer Delete on the shell EPR

Let’s go through each step in a bit more detail.

For the WS-Transfer Create SOAP message, the body should contain the streams you want to send and receive.  The resource URI should be:  We are essentially creating a cmd.exe shell which we use to run powershell.exe.

<Shell xmlns='’>
  <OutputStreams>stdout stderr</OutputStreams>

If the request was successful, you’ll receive a standard WS-Transfer Create SOAP response which contains the newly created Shell EPR which resembles:

  <w:Selector Name="ShellId">AFCFB065-F551-4604-BFDFD9B706798B5D</w:Selector>

This EPR should be cached for all subsequent operations.  The first custom SOAP action Command uses the Action uri:  WinRS supports two console modes: interactive and batch.  For an interactive session, WinRS will wait for input (even if the command completes) until the client indicates there is no more.  For a batch session, WinRS will expect input to be sent only during the lifetime of the running command.  For this scenario, it is important to specify the WS-Management option WINRS_CONSOLEMODE_STDIN with a value of true which means to use batch mode.  The command-line is split into separate Command and Arguments.  The SOAP fragment would look like:

    <w:Option Name='WINRS_CONSOLEMODE_STDIN'>TRUE</w:Option>
<CommandLine xmlns=''>
  <Arguments>get-service | format-csv </Arguments>

If this request was successful, the response will contain a CommandId element in the body that should be cached and used for subsequent operations for receiving the output.  Although the protocol was defined to allow one shell to host multiple commands, WinRS is limited to a single command per shell.  An example body of this response would resemble:


Once the Command response is received, the command is running on the server.  WinRS will block output (and therefore the command) once the maximum envelope size is reached.  The custom SOAP action Receive uses the action uri: .  Since the resulting output may exceed a single SOAP envelope, the client needs to specify an incrementing SequenceId in case any packets were lost. WinRS will only cache the last sent packet.  The request should contain the streams you want to read from and the CommandId associated to the stream in the body:

<Receive SequenceId='0'
  <DesiredStream CommandId='772B44DF-2EA2-4AA5-87D1-A07E1FAE7A4E'>
    stdout stderr

The response will contain the text output of the streams base64 encoded (to keep the SOAP xml well-formed and valid).  The client should check the state of the command to know whether to continue to invoke Receive for more output. 

  <rsp:Stream Name="stdout" CommandId="772B44DF-2EA2-4AA5-87D1-A07E1FAE7A4E">DQo=</rsp:Stream>
  <rsp:Stream Name="stdout" CommandId="772B44DF-2EA2-4AA5-87D1-A07E1FAE7A4E">
  <rsp:Stream Name="stdout" CommandId="772B44DF-2EA2-4AA5-87D1-A07E1FAE7A4E">
  <rsp:Stream Name="stdout" CommandId="772B44DF-2EA2-4AA5-87D1-A07E1FAE7A4E" End="true"></rsp:Stream>
  <rsp:Stream Name="stderr" CommandId="772B44DF-2EA2-4AA5-87D1-A07E1FAE7A4E" End="true"></rsp:Stream>
  <rsp:CommandState CommandId="772B44DF-2EA2-4AA5-87D1-A07E1FAE7A4E" 

Once the CommandState is “Done”, there is no more output and WS-Transfer Delete should be called on the shell EPR.  This will clean-up any resources being used on the server. 

The example code shows how to invoke a Powershell cmdlet.  It does not make use of any WinRM api’s, but instead creates the necessary SOAP messages from templates and uses System.Net.HttpWebRequest to send it over the wire.  To enable using the sample code in Windows, you’ll need to enable Basic authentication in the WinRM service config (which only works for local acocunts), you can run this Powershell command as admin: Set-Item WSMan:\localhost\Service\Auth\Basic $true.  You will need to deploy a server cert to use HTTPS or for testing purposes, set the WinRM service config to allow unencrypted HTTP.  Here's an example command line to use with the sample code:

WinRSPsh http://server:5985/wsman user password "get-service"

If you want the output in a application consumable form, you can convert to XML (.Net serialization):

WinRSPsh http://server:5985/wsman user password "(get-service ^| convertto-xml).OuterXml"

Note that in the above example, you have to escape the pipe so that cmd.exe doesn't try to interpret it.

Comments (20)
  1. Dan Wanek says:

    Great post! Thanks for sharing… I just wish it had been posted a few months ago 😉  I wrote a Ruby based winrm client ( and I wasn't aware of the <Arguments/> element to <Command/>. The way I got around this that may come in handy for some people is passing a Base64 encoded script across the wire using Powershell's 'encodedCommand' switch. That way you can write your script file on the client in your favorite editor and pass it this way:

    script = Base64.strict_encode64(myscript)

    … soap req…

    <Command>"powershell -encodedCommand #{script}"</Command>

    Just thought someone might find it of interest. Thanks again for your great posting. Looking forward to many more.


  2. @Dan, thanks for the feedback.  The <Arguments> element isn't strictly needed/validated today (as you noticed), but it keeps the xml a bit cleaner.  

    If you have suggestions for any topics you'd like to see covered, let me know.

  3. shell usage says:


    One thing that has been of interest to me while using the wsman interface is maintaining a current working directory. When I open up a shell and issue a command "cd Desktop", for instance, the next command to that same shell_id is not from the Desktop directory. I can get around this somewhat using syntax like this 'cd Desktop && dir' but it would be nice if there was a way to maintain the state for the shell. Any tips?



  4. @Dan, this is possible by using an interactive pattern than a batch pattern (what I presented here).  The difference is not huge, but significant as the client now owns the lifetime of the shell instead of the server which includes use of the Send and potentially Signal actions.  Let me see if I can find some time next week to provide an article on how to achieve this.

  5. SandyG says:

    The above example uses .Net, how can you manage Windows/execute the powershell cmdlets from a AIX/Solaris box?

  6. @Sandy: Although the sample code is written in C# and uses .Net, it is not doing anything Windows specific (you might even be able to re-compile the code using Mono).  The code is intended to show how to craft the necessary SOAP messages and send them via HTTP.  You should be able to use the sample and re-write it in Perl/Java/etc… In fact, Dan above has a link to a Ruby implementation he wrote you could use on Unix.  

  7. WinRM for collecting Performance Data? says:

    This is off topic (I've been looking for an appropriate forum to discuss programattic (C#) WinRM usage… the MSDN forums aren't really all that helpful here)… Is there any way to access WMI Performance classes through WinRM?  As far as I can tell, there's no refresher object to work with, so I'm working if there's a trick that isn't made clear from the documentation…

  8. You are correct that there isn't a WinRM equivalent of the WMI Refresher object.  For perf counters that don't require multiple samples to compute, you can make use of the Win32_PerfFormattedData classes, otherwise, you should use several samples of the Win32_PerfRawData instance and compute the result yourself.  This sounds like a good topic for a future blog post.  Thanks.

  9. beegee says:

    As you indicsted, able to compile the sample on mono on linux.  When I run it as in

        mono Program.exe http://WIN-IP/winrm user passwd get-services

    seem to be able to get past cred check but end up with this.  


    Error getting response stream (Write): SendFailure

    Any ideas as to why?

  10. @beegee the URL should be http://WIN-IP/wsman unless you explicitly changed the "urlprefix" in the configuration to be "winrm"

  11. Hi

    I’ve been trying to get the interactive pattern to work so I can create a custom remote powershell client. When I invoke powershell with a script as argument, input and output work fine with multiple receive messages. But when I invoke powershell with a script and “-NoExit”, receive stops working as soon as the script is done executing. I can send the request, but it’s waiting on something and never returns a response. I can still send messages such as “del file.txt” and they are processed correctly, so it’s just the receive message that stops working. Same thing happens when I invoke powershell without any arguments, the copyright text is displayed, but after that receive never returns anything.

    Any idea why I’m getting this behavior?

    Best regards


  12. @JMD Software, it seems that there may be a issue preventing an interactive PowerShell session via WinRS from working correctly. On my Win2k8R2 box, just using "winrs -r:foo powershell.exe", I'm able to observe the behavior I believe you are describing.  Executing "winrs -r:foo cmd" works, but starting powershell.exe under that remote cmd session exhibits the same problem.  Due to how PowerShell uses the console, it appears that it may not be possible to use WinRS protocol for interactive PowerShell and you may need to implement PSRP (…/dd357801(v=PROT.10).aspx).

  13. @JMD Software, I did some research and it appears this was a known issue in Win7/Win2k8R2.  PowerShell is designed to not prompt if stdin is redirected (which would be the case via WinRS).  So there is no way to use WinRS Protocol to establish an interactive PowerShell session.  

  14. Barak says:

    Kind of late to be asking this, but is there any way to use the same technique to transfer a file from the client to the server? Maybe pass a base64 encoded string as a parameter somehow?

  15. You could certainly use this technique to transfer a file using base64 encoding.  However, it won't perform very well since the binary file would go through several levels of encoding (even if you skip PowerShell and just use cmd.exe).  Recommendation is to use BITS to transfer the file if possible which goes over HTTP.…/aa362708(v=vs.85).aspx

    If you could articulate your specific scenario further (assuming BITS is not something you are able to use), I could give you a better answer.

  16. Barak says:

    Well, I want to upload a script (which may be generated/modified by my code first) and then execute it. I think I have the upload sorted (using Base64) and I expect the files to be fairly small, so the Base64 overhead should not be a huge problem. I can't use BITS since the client running this may not be running Windows – it's a cross-platform project.

    The next thing I have to tackle is encryption. The remote windows machines have encryption enabled (over HTTP on port 5985) and use NTLM authentication. Is there a sample somewhere showing how to encrypt the messages?

  17. For cross-platform, it'll probably be easier to enable HTTPS instead of trying to implement session key based encryption (although you could follow the spec here:…/cc251574(v=PROT.10).aspx).

  18. It appears that this Open Source project successfully implemented session key HTTP encryption:…/WinRM.  I have not tried that Ruby client myself so I can't speak to it's quality.

  19. Barfi says:

    Steve Lee: Thanks for the informative post !

    We are performing a series of Powershell commands in batch mode. Objects are returned from one powershell command are to be used in the subsequent command. This if I run by embedding a batch script within args[3] or the commandline param, there will be a problem as all$variables will be substituted.

    One option then is to run each command of the script as a separate "Command custom SOAP action".

    1) In that case, is it possible to de-serialize all objects to text and then using the stderr+stdout of a command and again serialize the text into objects for the next command.

    2) We have one more problem where when another powershell module is loaded in one "Command custom SOAP Action". We get an error – "Process is terminated due to StackOverflowException" as part of the output.

    Are there any known restrictions in each Powershell shell EPR ? How to avoid this/handle this."  

  20. Now that Windows Server 2012 is out, my recommendation for you would to be use the Management OData IIS Extension (…/powershell-cmdlets-invocation-through-management-odata-using-wcf-client.aspx).  This is a new feature designed around calling PowerShell through a REST interface.  

Comments are closed.

Skip to main content