How to execute commands using powershell after doing remote login using winrs

1.7k views Asked by At

I am new to PowerShell scripting. So please pardon me if I am asking a silly question. I have a task to connect to the command prompt of a remote desktop and run a series of commands on the command line.

I am using winrm to access the remote desktop machine. I have written the following code in a test.ps1 file:

powershell.exe -c "winrm set winrm/config/client '@{TrustedHosts=remoteHostServer}'"
powershell.exe -c "winrs /r:remoteHostServer /username:admin /password:xxxxxx cmd.exe"
powershell.exe -c "C:\test\"
powershell.exe -c "java test.java"

When I am executing the above code, I am reaching the following line:

C:\Users\admin>

But then it is not changing the directory to the path "C:\test" and execute the java file. Can anyone say what else should I do to execute the java files present in the path "C:\test" ?

1

There are 1 answers

11
mklement0 On BEST ANSWER

Note: Instead of using winrs calls via powershell.exe to execute remote commands, consider using PowerShell's remoting, which is equally WinRM-based.


  • You don't need to make your winrm and winrs calls via powershell.exe.
  • To execute a command remotely, you must submit it as part of the winrs command line.
  • Separate powershell.exe calls create independent sessions, so you cannot use one call to create state for another; use a single call with multiple commands.

Therefore:

winrm set winrm/config/client "@{TrustedHosts=remoteHostServer}"
winrs /r:remoteHostServer /username:admin /password:xxxxxx powershell -c "Set-Location C:\test; java test.java"

Note that you do not need PowerShell to execute your specific remote command either. Here's the version without PowerShell, using the -d option to set the remote working directory:

winrm set winrm/config/client "@{TrustedHosts=remoteHostServer}"
winrs /r:remoteHostServer /username:admin /password:xxxxxx -dC:\test java test.java

Note that the remote cmd.exe session that winrs implicitly creates automatically ends when a given command finishes executing (the powershell ... call in this case).
That is, the remote session ends and returns control to the caller once the remote command terminates.

An interactive remote session can only be entered if you specify a shell executable as the command to execute, such as cmd.exe and powershell.exe without arguments (or with /K / -NoExit).
However, note that the commands you submit remotely will be echoed before their output prints; while there is a -noecho option to suppress that, it unfortunately also hides commands as they're being typed.


Sending many commands to execute remotely via winrs.exe and PowerShell:

The above shows that you can simply pass multiple commands, separated with ;, inside the "..." string passed to the -Command (-c) parameter of powershell.exe, the Windows PowerShell CLI.
Given that the command-line length limit is close to 32,767 characters, this makes it possible to send many commands.
However, the single-line requirement and the need to escape embedded " chars. make this approach awkward.

The alternatives are:

  • (Untested) Place the commands in a *.ps1 script file on a file share that is accessible from the remote machine and invoke it from there, using powershell -ExecutionPolicy Bypass -File ...; doing so also requires passing the /allowdelegate option to winrs; e.g.:

    winrs /r:remoteHostServer /allowdelegate /username:admin /password:xxxxxx powershell -ExecutionPolicy Bypass -File "\\Server1\share1\script.ps1"
    
    • Note: If the script to execute is on the remote machine's local drive, simply use the local path - no need for a file share or /allowdelegate

    • Caveat: If the execution policy on the remote machine is GPO-controlled, -ExecutionPolicy Bypass will have no effect.

  • Pipe the contents of a script file to powershell -c -, i.e. make the remote PowerShell instance read the commands to execute from stdin:

    type script.ps1 | winrs /r:remoteHostServer /username:admin /password:xxxxxx powershell -c -
    
    • Note: Since you're sending code via stdin in this case, you cannot also send data that way; if you need the latter (e.g. to automate Read-Host responses), you'll have to use the script-file-based approach or the approach where you pass the code directly to
      -c

    • Caveat: Using -Command - (-c -) has quirks, notably:

      • Make sure that script.ps1 has two newlines at the end of the file - otherwise, a multi-line statement (anywhere in the script) that isn't followed by an empty line won't get processed, along with any subsequent statements.

      • Each statement received via stdin executes in a separate pipeline (this slows down execution and can affect for-display output formatting)

      • See GitHub issue #3223 for details.