When working with PowerShell remoting via WinRM, a common frustration emerges: GUI applications launched through Invoke-Command
appear to run successfully (confirmed via process listing) but never display visibly on the remote machine's desktop. This behavior stems from fundamental Windows session isolation mechanisms.
Windows maintains strict separation between:
- Session 0: System services and background processes
- Session 1+: User interactive sessions
By default, PowerShell remoting creates a non-interactive session (typically Session 1 but without GUI rights), causing launched GUI apps to terminate when the session ends.
Method 1: Scheduled Task Approach
$action = New-ScheduledTaskAction -Execute "calc.exe"
$trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName "RemoteGUIApp" -Action $action -Trigger $trigger -Force
Invoke-Command -ComputerName REMOTE-PC -ScriptBlock {
Start-ScheduledTask -TaskName "RemoteGUIApp"
Unregister-ScheduledTask -TaskName "RemoteGUIApp" -Confirm:$false
}
Method 2: PSEXEC Alternative
When WinRM isn't mandatory, Sysinternals' PSEXEC can bridge the session gap:
.\PsExec.exe \\REMOTE-PC -i -d calc.exe
The -i
flag specifies interactive mode, while -d
prevents waiting for process completion.
Method 3: Permanent WinRM Configuration
For frequent GUI remote access, modify the WinRM service:
Invoke-Command -ComputerName REMOTE-PC -ScriptBlock {
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
-Name "LocalAccountTokenFilterPolicy" -Value 1 -Type DWord
Restart-Service WinRM
}
Each solution carries security implications:
- Scheduled tasks leave temporary artifacts
- PSEXEC requires SMB ports (often blocked)
- WinRM configuration changes affect all remote connections
Always evaluate these in your security context before implementation.
For developers needing to launch Visual Studio remotely:
$vsPath = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\devenv.exe"
Invoke-Command -ComputerName DEV-MACHINE -ScriptBlock {
schtasks /create /tn "VSRemote" /tr "$using:vsPath" /sc once /st 00:00 /ru $env:USERNAME
schtasks /run /tn "VSRemote"
Start-Sleep -Seconds 5
schtasks /delete /tn "VSRemote" /f
}
If applications still don't appear:
- Verify the remote user has active desktop sessions
- Check Group Policy for "Interactive logon" restrictions
- Test with simpler apps (notepad.exe) before complex GUIs
When working with PowerShell remoting via WinRM, you might encounter a peculiar behavior when trying to launch GUI applications. The command executes successfully, but the application window never appears on the remote machine's desktop. This happens because:
- The process starts in a non-interactive session
- The GUI has no desktop context to render to
- The session terminates immediately after execution
Windows implements session isolation for security reasons. When you run Invoke-Command
, your remote command executes in session 0 (the non-interactive session), while the user's GUI runs in session 1 or higher.
# This won't show the GUI on remote desktop
Invoke-Command -ComputerName REMOTE-PC -ScriptBlock {
Start-Process notepad.exe
}
Method 1: Using Scheduled Tasks
The most reliable approach is to create a scheduled task that runs in the user's session:
Invoke-Command -ComputerName REMOTE-PC -ScriptBlock {
$action = New-ScheduledTaskAction -Execute "calc.exe"
Register-ScheduledTask -TaskName "RemoteGUI" -Action $action -RunLevel Highest
Start-ScheduledTask -TaskName "RemoteGUI"
# Optional: Clean up after execution
Start-Sleep -Seconds 2
Unregister-ScheduledTask -TaskName "RemoteGUI" -Confirm:$false
}
Method 2: Using psexec with Interactive Flag
If you have Sysinternals tools available, psexec can solve this:
psexec \\REMOTE-PC -i -d calc.exe
Method 3: Remote Desktop Session Execution
For persistent access, you can use RDP and run commands directly:
# First establish RDP connection
mstsc /v:REMOTE-PC
# Then run this locally on the remote machine after logging in
Start-Process "C:\Path\To\YourApp.exe"
- Always use encrypted WinRM connections (HTTPS)
- Configure proper firewall rules
- Use constrained endpoints when possible
- Consider using Just Enough Administration (JEA)
If you're still having issues:
# Check if WinRM is properly configured
Test-WSMan -ComputerName REMOTE-PC
# Verify firewall rules
Invoke-Command -ComputerName REMOTE-PC -ScriptBlock {
Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*WinRM*" }
}
# Check session details
query session /server:REMOTE-PC