Start a Program on the Remote Active Desktop with PowerShell

PowerShell Remoting: Start a Program on Active Desktop

The goal is to run a command to open a program window like Notepad or Photoshop on a remote computer desktop using PowerShell Remoting. This powerful feature allows us to establish remote sessions, but by default, it only allows executing scripts on the target machine as services. So, the established session is disconnected and not visible to the user watching the remote computer screen. The graphic interfaces of programs started in the session are not visible either. However, we are set in this tutorial to show how to leverage the Task Scheduler to open an application window on a remote desktop and make it visible to the user's eyes.


1. The context

In this section, we are opening a program from a PowerShell session, for instance, Notepad. We describe why it is not visible on the remote desktop by default.

The commands below open an interactive session with the remote computer identified on the network as inonw-svr. You will notice the chevron arrow pointing to the properties of the current session, for instance, the services session. Under the STATE header, we see Disc for Disconnected.

PS> $Session = New-PSSession -ComputerName 'inonw-svr' -Credential (Get-Credential)
PS> Enter-PSSession -Session $Session
[inonw-svr]: PS> query.exe session
 SESSIONNAME       USERNAME                 ID  STATE   TYPE        DEVICE
>services                                    0  Disc
 console           Administrator             1  Active

We start the Notepad application from the services session.

[inonw-svr]: PS> Start-Process notepad.exe

Notepad is open as a background process. The desktop does not display the usual signs that a particular GUI application is open: no icon on the taskbar or the system tray.

Invisible Notepad window

The red arrows in the image above show the taskbar and the system tray. The green arrow shows that Notepad is running as a service.

[inonw-svr]: PS> tasklist.exe /fi 'imagename eq notepad.exe'
Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
notepad.exe                   7100 Services                   0     10,540 K

What the Task Manager shows.

Task Manager Display


2. Using Task Scheduler

The Task Scheduler utility is built into Windows and allows us to schedule executions of programs. A scheduled task is set to Run only when the user is logged on by default. This property specifies that the defined action executes in the active or connected session with which the user can interact. We can thus use this utility to achieve our goal.

Task Scheduler

PowerShell provides a set of cmdlets to help us manage scheduled tasks without opening the Task Scheduler window. We can obtain the list of them by running the below command. I narrowed the list down to the cmdlets used to open the Notepad window in this tutorial.

[inonw-svr]: PS> Get-Command -Noun ScheduledTask* | Select-Object Name
Name
----
New-ScheduledTaskAction
Register-ScheduledTask
Start-ScheduledTask
Unregister-ScheduledTask

We now run commands to open Notepad using the Task Scheduler. We will open a file whose name is Message.txt that contains the text "Hello World!" and it is located in the current directory.

[inonw-svr]: PS> $FileName = "$PWD\Message.txt"
[inonw-svr]: PS> 'Hello World!' | Out-File $FileName

We create the RemoteExec scheduled task that will be used to open Notepad. The above image of the Task Scheduler shows it after creation.

[inonw-svr]: PS> $TaskAction = New-ScheduledTaskAction -Execute notepad.exe -Argument $FileName
[inonw-svr]: PS> $Task = Register-ScheduledTask -TaskName RemoteExec -Action $TaskAction

Note that this task has no trigger. It is not an issue since we start the task ourselves. This time the application window opens on the active desktop as the image below shows.

[inonw-svr]: PS> Start-ScheduledTask -InputObject $Task

Notepad

We run the Tasklist.exe utility and see that Notepad is open in the Console session which is in the Active state.

[inonw-svr]: PS> tasklist.exe /fi 'imagename eq notepad.exe'
Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
notepad.exe                    220 Console                    1      5,480 K

Our task is complete. Let us now unregister the task; leave and remove the Services session.

[inonw-svr]: PS> Unregister-ScheduledTask -InputObject $Task -Confirm:$false
[inonw-svr]: PS> Exit-PSSession
PS> Remove-PSSession $Session

3. The Send-NotepadMessage ScriptSend-NotepadMessage Github Project

The Send-NotepadMessage script is a tiny project I developed based on the solution described in this tutorial. I wrapped it in a simple Windows form, as the below image shows.

Task Scheduler

Below is the solution written as a function that is called when the Send button is clicked. The command Invoke-Command is used in place of the PSSession cmdlets of the previous sections. They are not required since we are not opening interactive sessions.

using namespace System.Management.Automation

function Send-NotepadMessage {
  [CmdletBinding()]
  param(
    [ValidateNotNullOrWhiteSpace()]
    [string] $ComputerName,
    [ValidateNotNull()]
    [Credential()]
    [PSCredential] $Credential = [PSCredential]::Empty
    [string] $Message,
  )
  $SenderFilename = ".\Message from  $Env:USERNAME.txt"
  Invoke-Command -Scriptblock {
    $Using:Message | Out-File $Using:SenderFilename
    $TaskAction = New-ScheduledTaskAction -Execute notepad.exe -Argument $Using:SenderFilename -WorkingDirectory $PWD
    $Task = Register-ScheduledTask -TaskName RemoteExec -Action $TaskAction -Force
    Start-ScheduledTask -InputObject $Task
    Unregister-ScheduledTask -InputObject $Task -Confirm:$false
    Remove-Item $Using:SenderFilename -Force
  } -ComputerName $ComputerName -Credential $Credential
}

The demo of the project.

Comments

Popular posts from this blog

Git-Log or Git Graph in Visual Studio Code

Git-Commit: Reuse Commit Messages