by thorhalvor

Executing commands and programs on a remote machine – Part 2

One month a ago my friend Njål blogged about executing commands and programs on a remote machine using powershell.
Njål wrote about PsExec and Invoke-WmiMethod and I will present some more ways to do this. First one is through MSDeploy.exe and the second, my preferred way of doing it, using WinRM, – and then WinRS. I won’t give pros and cons merely inform about the different techniques.

Msdeploy
Msdeploy has two properties that can be set when calling the executable which is called preSync and postSync. MsDeploy is build around a synchronization from source to destination and the commands given in preSync will be executed before the actual synchronization and postSync after. This comes in handy when for example deploying a Windows Service. If the services isn’t stopped before new files are synchronized then you might have seen that a “File(s) in use exception” is thrown.

Example:

 Msdeploy.exe –verb:sync –source: -dest: -preSync: runCommand="net stop " –postSync:runCommand="net start "

When executed this command will first stop the service on the remote machine, then synchronize all the files and then start the service when done. (when actually doing this live you might add waitInterval and waitAttempts since the stopping of the service might take a while).
The value in “runCommand” can contain all sorts of script executing and you can also add “&&” and execute two commands after each other directly.

WinRM
To be able to use WinRM it must be installed and started on the remote computer. Open powershell and run “winrm qc” (qc=quickconfig) and everything should be fixed ready for use. If it’s a test server and you don’t care that much about security you can enable the listener on port 80 (will not interfere with IIS) running this command:

winrm set winrm/config/service @{EnableCompatibilityHttpListener="true"}

You can enable unencrypted messaging setting “allowunencrypted = true“. You can do this almost like the command above or you can browse to the setting and set it specific:

cd wsman:localhostclient
set-item .allowunencrypted $true

The above settings have to be executed on the server. On the client (buildserver when running automated deploys) you might have to enable AllowUnencrypted and also add the server as a TrustedHosts:

winrm s winrm/config/client '@{TrustedHosts="RemoteComputer"}'

Everything should now be set up for running unencrypted remote commands. This can be done through Invoke-Command:

Invoke-Command -Session $ps -ScriptBlock {
   Unzip-File x y
   Copy-Item x y
   …
 }

Within the ScriptBlock you can add all the commands you want and they will all be executed on the remote macine
The $ps variable above is a session which I create and remove like this:

$ps = new-pssession -Computername $computername -Port 80 -credential $cred
Invoke-Command……
Remove-PSSession $ps

If your script got the password in cleartext you can not put it directly into a parameter in the new-pssession command but you can write is as a SecureString to variable and then use this as Credential:

$secureString = ConvertTo-SecureString $password -AsPlainText –Force
 $cred = new-object -typename System.Management.Automation.PSCredential `
 -argumentlist $usernameAndDomain, $secureString

WinRS
I am not sure, but I think WinRS only a wrapper for the Invoke-Command used in the examples above. Atleast it simplifies the whole process. I have actually not used this command I just saw it when “browsing around” and I haven’t botherd changing my current scripts.
But as an example you can write

winrs –r:http://remotemachine <command>

The <command>  will be run on the remote macine if it got WinRM enabled and running.