1

tl;dr

If I locally start Invoke-Command to start an EXE file remotely, it fails, if there is no active user session on the remote system.


I am facing the challenge of installing an application with an EXE file on 20+ virtual machines (Windows Server VMs) and would like to automate this process using PowerShell. Here are the steps I have implented so far:

  • Copying the Installer File: this works, the EXE installer is copied to the "C:\tmp" folder on the remote system
  • Copying Configuration Files: same as the installer file
  • Executing the EXE File: The EXE file should be executed with specific parameters on each remote system.

In the very last step (executing the EXE file), I am desperately looking for a solution. Here are my observations:

  • I start the EXE file with, e.g. this command:
Invoke-Command -Session $SessionToRemoteComputer `
    -ScriptBlock { 
        Start-Process -FilePath "C:\tmp\PathToExe.exe" -ArgumentList "-q -c -dir 'C:\Installer\target\directory'" -Verb RunAs -Wait 
    } -AsJob

(Parameters "q" and "c" indicate the installer to start in "quiet" and "console" mode, hence no GUI window is involved in this process)

The command executes whitout any errors. But the EXE file is not started. However, if I connect to the remote computer with RDP and re-run the Invoke-Command on my system, the EXE file starts and installs the application successfully on the remote computer. I can reproduce it on every VM, but I have no clue why! And it is very useless, to first start a RDP connection to execute the installer remotely. I thought, to fully automate this process.

The user I'm using to start the Invoke-Command is a admin user and it is the same user, I use to login via RDP to the Remote Machines.

I'm feeling, I did every combination of Invoke-Command and Start-Process with all combinations of the respecitive parameters and I feel, I'm loosing my mind on this :-(.

Can anyone help me please!

0

2 Answers 2

2
  • You're using Start-Process to directly launch your installer executable, C:\tmp\PathToExe.exe.

  • As such, the single string encoding all arguments to pass to the executable via the
    -ArgumentList parameter is placed as-is on the process command line for the target executable and must therefore only use embedded "...", i.e. double-quoting to enclose arguments, as this is the only form of quoting recognized in such no-shell invocations.[1]

    • Conversely, '...' (single-quoting) would only work if the -ArgumentList string were interpreted by a shell that also recognizes this form of quoting, such as PowerShell.

Therefore (focusing just on the Start-Process call):

# Note the use of embedded " rather than '
Start-Process -FilePath "C:\tmp\PathToExe.exe" `
              -ArgumentList "-q -c -dir `"C:\Installer\target\directory`"" -Verb RunAs -Wait

Note:

  • Since your command doesn't require expansion (string interpolation), you could use a verbatim, single-quoted PowerShell string literal, which obviates the need to `-escape the embedded " chars:

    -ArgumentList '-q -c -dir "C:\Installer\target\directory"'
    
  • With the specific path at hand, since it doesn't contain spaces, you can even make do without embedded quoting altogether:

    -ArgumentList '-q -c -dir C:\Installer\target\directory'
    

Taking a step back:

  • Instead of using Start-Process you may alternatively use direct invocation even with a GUI(-subsystem) application, in which case use of '...' (single-quoting) is an option, because it is PowerShell that handles the quoting in that case.

  • To make the call synchronous (await the application's termination), you can use the following trick: pipe the call to Write-Output

Aside from such a call being less complex and more concise, the added advantage is that the application's process exit code is automatically reflected in the automatic $LASTEXITCODE variable (whereas with Start-Process you'd have to use -PassThru, capture the process-info object that is then returned, and query its .ExitCode property):

# Note the | Write-Output part, which makes even calls to GUI applications
# synchronous.
# After the call, the process exit code is reflected in $LASTEXITCODE
& 'C:\tmp\PathToExe.exe' -q -c -dir 'C:\Installer\target\directory' | Write-Output

As noted above, with the specific file paths used here, you don't strictly need quoting at all. If the executable path doesn't need quoting and contains no variable references, use of &, the call operator, isn't strictly necessary (that is, you could call C:\tmp\PathToExe.exe ...).


[1] Ultimately, on Windows it is up to each target executable to parse its own process command line into distinct arguments, but the vast majority of Windows CLIs recognize "..." quoting only.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you @mklement0 for your help. With your advice, I could clear the correct quoting of the arguments. Unfortunately, it does not solve my original problem. Still, after executing the correct qutoed command, nothing happens. However, if I connect with RDP to the remote computer and execute the Invoke-Command again from my local system, the installer starts and installs the software successfully. Any other ideas?
@LukasHT The install executable may require different parameters or transform files for silent or remote installations to handle wizard options that appear during interactive setups with current parameters you use that work when logged in interactively. Check documentation, ping the developers or vendors to help understand to ensure those things are being used which allow silent installation of the package.
If it is not an install package, confirm that the process is developed to run as SYSTEM or a non-interactive session that is logged on such as a service account, gmsa, etc. Or make a task scheduler job run it at login, etc. events interactively for the current logged in user account. Ensure it works as expected when more than one user is logged (e.g. another disconnected) on if that's a possible variable in your environment. The user running it interactively will need sufficient permission to the folder or share it resides.
@LukasHT, I'm afraid I have little to offer beyond BitCoinMurderousManiac's recommendations. While the command does run with your user identity, it is by impersonation via a service, which, as noted, runs invisibly and without a desktop. It is conceivable that your installer isn't designed for that, the -q and -c options notwithstanding. See if you can get the installer to perform logging and inspect the logs to get further clues.
@LukasHT, please see my update re using direct invocation as an alternative to Start-Process, which also makes it easier to query the process exit code, which may provide a clue too.
0

I just wanted to take a moment to thank both of you @mklement0 and @Bitcoin Murderous Maniac for your incredible help. After much effort and troubleshooting, I have finally managed to solve the issue with your help 🎉 Here is my solution I'd like to use until further notice:

$InstallerPath = "C:\tmp\PathToExe.exe"
$argList=@('-q','-c','-dir','C:\Installer\target\directory')

$ScriptBlock = {
    param(
        Parameter()][string]$PathToExe,
        Parameter()][System.Object[]]$ArgumentList
    )
        # Just for debugging
        "PathToExe: $PathToExe, args: $ArgumentList"
    
        & $PathToExe $ArgumentList | Write-Output;
        $LASTEXITCODE
    }
                    
Invoke-Command -Session $SessionToRemoteHost `
    -ScriptBlock $ScriptBlock `
    -ArgumentList $InstallerPath,$argList `
    -AsJob

I found this post: Powershell Call-Operator(&) with Parameters from Variable which provided me the last and final hint with passing arguments as System.Object[]to the & call operator.

With this solution, I hope, this is a quite future proof solution and I shouldn't get any troubles for the next installers/EXE files.

Thanks again for your support!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.