Search code examples
powershelltoastlocal-system-account

Powershell: Send a toast notification to logged user when running as Local System


I have a script running as Local System which does some stuff, including checking if it's a user logged on, and if yes, it runs a PowerShell snippet to show a toast notification, such as below.

If the PS runs as current user, it works ok. If it runs as LocalSystem, the current user does not see the toast because the output is sent Session 0 (for local system account).

Is it possible to display a toast notification to logged on user, if running as Local System and without requesting user's credentials?

Add-Type -AssemblyName System.Windows.Forms 
$global:balloon = New-Object System.Windows.Forms.NotifyIcon
$path = (Get-Process -id $pid).Path
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) 
$balloon.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]::Info 
$balloon.BalloonTipText = "$Text"
$balloon.BalloonTipTitle = "$Title" 
$balloon.Visible = $true 
$balloon.ShowBalloonTip($Miliseconds)

Solution

  • Intro

    It is possible to display a toast notification to the current active logged on user using PowerShell if running as the SYSTEM user in Session 0 without requesting the user's credentials.

    Two solutions to do this follow the "Background Notes"

    Background Notes

    Please note that this section is written for all viewers of this post, rather than just the original questioner.

    SYSTEM refers to the synonyms NT Authority\SYSTEM and Local System.

    Many Windows services run as the SYSTEM user although others run as users with less privileges such as LOCAL SERVICE and NETWORK SERVICE.

    For each logged in user a Windows session is created numbered from 1 which contains the user's windows.

    An additional background session called Session 0 is also created which the Windows services and User Mode Drivers run in. More info at the following link.

    Sessions, Desktops, and Windows Stations

    All services except the Per-User Services run in Session 0.

    If you are using a service for such a script I recommend looking at using one of the two alternatives below:

    1. Create a Session 1 sub-process as already done by some services.

    2. Use the Windows Task Scheduler instead to run your main script or notification script in the same session as the current active user. This schedule task can be set to trigger on an event.

    Note the following security caveat. Scripts in Powershell 5 can be interrupted giving control to a user. For Powershell 6 and later this behaviour is disabled by use of the noninteractive option.

    Solution Notes

    Two solutions are presented below to solve the original problem. Both use an intervening program to move from Session 0 to Session 1.

    Both will flash briefly a PowerShell window which is disconcerting for users and is hard to hide. Some hiding tips are provided in the link below.

    How To Run A PowerShell Script Without Display a Window

    The following solutions as typed require paths without spaces. Full paths must be used if given. You will have to edit these paths to suit.

    Test methodology is included.

    The first solution requires the PSExec.exe program. It is part of PSTools available at the following link. It is also used to test both solutions.

    PSTools

    The second solution requires the ServiceUI.exe program. It is part of the Microsoft Deployment Toolkit (MDT) available at the following link.

    Microsoft Deployment Toolkit (MDT)

    The ServiceUI.exe program is buried in the MDT install directory as follows.

    Microsoft Deployment Toolkit\Templates\Distribution\Tools\x64\ServiceUI.exe

    I copied it to E:\Programs\MDT\ServiceUI.exe to make it simpler to use in PowerShell

    Scripts needed

    BalloonTest.ps1

    $Miliseconds=50000
    $Text="Hi"
    $Title="Test"
    
    Add-Type -AssemblyName System.Windows.Forms 
    $global:balloon = New-Object System.Windows.Forms.NotifyIcon
    $path = (Get-Process -id $pid).Path
    $balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) 
    $balloon.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]::Info 
    $balloon.BalloonTipText = "$Text"
    $balloon.BalloonTipTitle = "$Title" 
    $balloon.Visible = $true 
    $balloon.ShowBalloonTip($Miliseconds)
    

    WhoAmISession.ps1

    whoami
    $Session=(Get-Process -PID $pid).SessionID
    echo "Session=$Session"
    

    Test Setup

    Start a cmd.exe window as the Administrator.

    The following command will provide a SYSTEM user Session 0 PowerShell execution environment as shown in the image.

    E:\Programs\PSTools\psexec -s powershell.exe -file e:\test\WhoAmISession.ps1
    

    PSExec Session

    Solution 1: PSExec.exe

    To show the Toast notification in PowerShell if running as the SYSTEM user in Session 0 use the following command. This will show the Toast notification if the current active logged in user is using Session 1. Some modification will be needed for other sessions.

    E:\Programs\PSTools\psexec -s -i 1 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -file e:\test\BalloonTest.ps1
    

    To test start a "cmd.exe" window as the "Administrator" and enter the following command. This will show the Toast notification if the current active logged in user is using Session 1 as shown in the image.

    E:\Programs\PSTools\psexec -s powershell.exe E:\Programs\PSTools\psexec -s -i 1 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -file e:\test\BalloonTest.ps1
    

    PSExec Balloon Test

    Solution 2: ServiceUI.exe

    To show the Toast notification in PowerShell if running as the SYSTEM user in Session 0 use the following command. This will show the Toast notification to the current active logged in user.

    E:\Programs\MDK\ServiceUI.exe C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -file e:\test\BalloonTest.ps1
    

    To test start a "cmd.exe" window as the "Administrator" and enter the following command. This will show the Toast notification to the current active logged in user as shown in the image.

    E:\Programs\PSTools\psexec -s powershell.exe E:\Programs\MDK\ServiceUI.exe C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -file e:\test\BalloonTest.ps1
    

    enter image description here