The previous blog post described a way to upload and (in theory at least) download text files to/from a remote Windows machine using WS-Management. In practice, the applicability of the method is limited for upload (text files only, slow for large files) and almost nonexistent for download. Here is a much improved version.
This is another example of something that was too obvious for me to see last weekend when I was in the thick of fighting with WS-Management SOAP messages and learning about WMI classes. It just took a day of not thinking about it to have the solution pop in my mind: use ftp.exe. For the longest time (at least since Windows NT) Windows has been shipping with this FTP client. And the documentation shows that you can call it from the command line and provide it with the name of a text file containing the commands to execute. Bingo.
Specifically, here are the steps. Let’s say that I want to run a program called task.exe on a remote Windows machine and that program takes a large binary file (data.bin) as input. I want to transfer both to the remote machine and then run the program. This can be done in 3 simple steps:
Step 1: upload the FTP command file to the remote Windows machine. The content of the command file is below. mgmtserver.myco.com is the name of the machine from which the two files can be retrieved over FTP. I use anonymous FTP here, but you could just as well provide a username and password.
open mgmtserver.myco.com anonymous binary get task.exe get data.bin quit
Step 2: execute the FTP commands above. This downloads task.exe and data.bin from mgmtserver.myco.com onto the remote Windows machine.
Step 3: execute the program on the remote Windows machine (“task.exe data.bin”).
Here are the on-the-wire messages corresponding to each step:
Step 1: upload the FTP command file to the remote Windows machine
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"> <s:Header> <a:To>http://server:80/wsman</a:To> <w:ResourceURI s:mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process </w:ResourceURI> <a:ReplyTo> <a:Address s:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address> </a:ReplyTo> <a:Action s:mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process/Create</a:Action> <a:MessageID>uuid:9A989269-283B-4624-BAC5-BC291F72E854</a:MessageID> </s:Header> <s:Body> <p:Create_INPUT xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process"> <p:CommandLine>cmd /c echo open mgmtserver.myco.com>ftpscript&&echo anonymous>>ftpscript&&echo binary>>ftpscript&&echo get task.exe>>ftpscript&&echo get data.bin>>ftpscript&&echo quit>>ftpscript</p:CommandLine> <p:CurrentDirectory>C:datawinrm-test</p:CurrentDirectory> </p:Create_INPUT> </s:Body> </s:Envelope>
As before, you need to set the Content-Type HTTP header to “application/soap+xml;charset=UTF-8” (or UTF-16).
Step 2: execute the FTP commands to download the files from your server
It’s the same message, except the <p:CommandLine> element now has this value:
Step 3: execute the task.exe program on the remote Windows machine
Again, the same message except that the command line is simply:
Note that I have broken this down in three messages for clarity, but you can easily bundle all three steps in one SOAP message. Just use this command line:
<p:CommandLine>cmd /c echo open mgmtserver.myco.com>ftpscript&&echo anonymous>>ftpscript&&echo binary>>ftpscript&&echo get task.exe>>ftpscript&&echo get data.bin>>ftpscript&&echo quit>>ftpscript&&ftp -s:ftpscript&&C:datawinrm-testtask.exe data.bin</p:CommandLine>
Of course this can also be used in reverse, to download files from the remote Windows machine rather than upload files to it. Just use PUT or MPUT as FTP commands instead of GET or MGET.
This mechanism is a major improvement, for many use cases, over what I originally described. I feel a bit like someone who just changed a flat tire by loosening the lug nuts with his teeth and then found the lug wrench under the spare tire.