Friday, February 03, 2017

Powershell problem : With remote PSSession, Running EXE in path with spaces and getting back response using Write-Output

I think some of the most trouble I've recently had in coding Powershell has been trying to run an executable on a remote Hyper-V VM that was located under Program Files and then actually getting the response back properly so I could iterate though the results.

The first trickiness was with running the command properly when there were spaces in the path.

In double quotes you have to use the ampersand (&) and then write the path to the EXE in single quotes and then at the end of the string before the closing quotes character, append an argument.

For example :

$commandLocation = "& 'C:\Program Files\....\application_name.exe' argument"

This is the proper syntax that you can then pass to an Invoke-Expression that's inside a ScriptBlock called by Invoke-Command. Invoke-Expression is a very helpful commandlet to run EXEs, when, for example, running Powershell on a remote machine.

The second issue was understanding how I could use Write-Output to pipe the remote call output back a Powershell variable within the ScriptBlock that I could then use in the calling function.

I read that you could use 4>&1 at the end of the ScriptBlock but never found it useful in my case.

In my case, I just had a ScriptBlock assigned to a variable and then ran the ScriptBlock using an Invoke-Command on a separate line.

There was some funkiness with WriteHost appending to the invoke-command so you have to disable that and then return the pure unadulterated Invoke-Command Result that I assigned to a variable.

Then I could iterate over the lines in the method that called the method below.

Here's the example code :

Function RunCommandOnRemoteMachine([string] $blahParameter, [string] $remoteMachineAddress)

    $defaultBlahCommandLocation = "& 'C:\Program Files\...\blah.exe' "

    WriteLogAndConsole "Default blah directory : $defaultBlahCommandLocation"
    $fullCommand = $defaultBlahCommandLocation +  $blahParameter

    WriteLogAndConsole "Full command with arguments being run : $fullCommand"

    $scriptToExecute = { param($passedCommand)
        begin {
            Write-Host "Start of command:"
        process {
            $consoleOutput = Invoke-Expression -Verbose -Command $passedCommand
        end {
            Write-Host "Output from remote command:"
            Write-Host $consoleOutput
            Write-Host "************"
            Write-Output $consoleOutput

    $blahCommandResult = Invoke-Command -Session $global:session -Verbose -ScriptBlock $scriptToExecute -ArgumentList $fullCommand

    #Write-Host "Result of blah argument command: " $blahCommandResult

    return $blahCommandResult