Use Powershell to start a GUI program on a remote machine

There are two Windows 7 machines setup on the same network.
I enabled everything needed so that they could communicate with winrm.

When I run the following command:

Invoke-Command -ComputerName REMOTE-PC -ScriptBlock { Start-Process calc.exe }

It works correctly, but the program is never seen on the remote machine. As far as I can tell, this is expected behavior.

I assume the process starts correctly, and then is immediately closed as the session ends. How do I run the program so that it appears on the host machine?


By design, you are not really supposed to be able to launch processes in other people's sessions.

(To clarify, even if you are logged on interactively at a computer desktop, and also have another separate network logon to the same machine at the same time using the same credentials, those still count as two different logon sessions.)

This is simply against the security model of Windows itself and attempts to subvert it will be frowned upon. So you'll not likely find an easy, supportable way of doing this. It is technically possible, but it involves running as Local System, copying another logged on user's security token, and launching a process with that alternate token. You would need the Windows API for this, which is pretty much the only thing Powershell isn't very good at. See WTSQueryUserToken and CreateProcessAsUser in the Windows API for more detail on that.

One other idea, so as not to totally pee in your Cheerios, you might be able to accomplish this by remotely creating a scheduled task that launches the process. See https://devblogs.microsoft.com/scripting/how-can-i-remotely-start-an-interactive-process/ for more info on that.

Edit: Oh, and I forgot... pretty sure PsExec with the -i parameter can do that. You have to supply the logon session ID. And have permissions to do it. It most likely uses the same Windows API that I mentioned, which leverages the fact that PsExec installs a temporary service that runs as Local System.


I was able to get this working using PsExec as mentioned in another answer.

  1. Download PSTools
  2. Unzip to C:\Windows\PSTools
  3. Add C:\Windows\PSTools to your PATH
  4. Get process ID of RDP session (tasklist will work, or a fancy one-liner: $session = tasklist /fo CSV | findstr RDP ; $session = $session.Split(",")[3] ; $session.Split('"')[1])
  5. Start process: PsExec.exe -s -i 123 calc.exe ("123" being the RDP session ID)

This is how I am doing it with Ansible 2.3.0:

- name: get RDP session number
  win_shell: "{{ item }}"
  with_items:
    - $session = tasklist /fo CSV | findstr RDP ; $session = $session.Split(",")[3] ; $session.Split('"')[1]
  register: rdp_session

- name: start calc.exe
  win_shell: PsExec.exe -s -i {{ rdp_session.results[0].stdout_lines[0] }} calc.exe

You can create a 'callable hook' on the remote machine: It's a scheduled task set to "run only when user is logged on," assigned to run under the account of the user who will be logged in upon execution. The action of the task is the executable you want to run.

Scheduled tasks can be created remotely via powershell or schtasks, and subsequently called simply by the 'name' of the task itself using schtasks or powershell's Start-ScheduledTask.

  1. On the remote machine, create a barebones scheduled task that is run by the user who is running the current session.
  2. Set the task to run "only when user is logged on"
  3. If the .exe or item in the "Action" tab of the scheduled task has "Run as Administrator" set on the filesystem item (right-click, properties, Compatibility>Run as Administrator), the scheduled task needs to be run with elevated privileges as well or it will fail or not appear.
  4. The author of the scheduled task should have admin privileges, so that when it is called remotely you can use this admin account for calling a scheduled task which then gets executed on the user profile specified under "run only when user is logged on."
  5. Make sure the machine is logged on as that user.
  6. It may be application-dependent if this task will properly execute if the machine is locked. In my experience it does.
  7. From here, you can use schtasks.exe and call the Name of the scheduled task along with the hostname, and pass along the credentials of the elevated account used to author the scheduled task (not the user who will be logged on).
  8. You can alternatively call it in powershell if the remote machine is running a powershell version that supports Start-ScheduledTask.

To call the task you reference the task by the Name you gave it:

schtasks /run /TN "mytaskname" /s "host" /u "user" /p "password"

Creating a scheduled task remotely is possible with either schtasks.exe or New-ScheduledTaskPrincipal in powershell. If the executable or "Action" item in the task is flagged as 'run as administrator' on the filesystem, the task will require a New-ScheduledTaskPrincipal credential to be attached during the task creation in order to set that property appropriately.

The currently-logged-in user can be a moving target, although it can be queried with Get-LoggedOnUser via powershell prior to the scheduled task creation itself.

Verbose code for such is forthcoming in the next 48 hours, I wanted to make the basic structure available for you all.