'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 1:[1]

To expand on the very good accepted answer:

Alternative solutions to run as another user

The following scripts can be used as alternatives to psexec and ServiceUI.exe mentioned in said answer, in a similar fashion, when called from SYSTEM:

Note that I haven't extensively tested these options and there may be restrictions, e.g. wrt executing user (if other than SYSTEM) or execution policies, depending on what context you use them in. I nevertheless think they are worth mentioning, as they can be good solutions for certain use cases.

Using scheduled tasks

The accepted answer mentioned the possibility of using scheduled tasks, but didn't expand on it.

This solution has the following advantages (some shared with other solutions):

  • it doesn't require the installation/import of any external tool or script
  • it can be run outside of SYSTEM (e.g. by a different user with administrative rights)
  • it can be automatized (it isn't needed for the user to manually register a dedicated task)
  • it can be atomized (only the toast-displaying code can be deferred to a dedicated task, leaving the main script to run separately on its own terms).

The idea is to register a scheduled task that will execute PowerShell, telling it to run a toast-displaying script. The task is registered so as to run as the currently logged-in user, is manually and immediately triggered (by the script-running, not-logged-in user -- e.g. SYSTEM), and finally deleted.

This is the basic code to achieve this, supposing the toast display logic is contained in a toast-showing-script.ps1 file1 which, as an example, expects one ToastScriptParam parameter:

$LoggedInUser = Get-CimInstance –ClassName Win32_ComputerSystem | Select-Object -expand UserName

$TaskAction = New-ScheduledTaskAction -Execute “powershell.exe” -Argument "-NoLogo -NonInteractive -WindowStyle Hidden -ExecutionPolicy RemoteSigned -File .\path\to\toast-showing-script.ps1 -ToastScriptParam ""$ToastContent"""  # clearly, you will want to adapt the arguments to fit your use case
$TaskPrincipal = New-ScheduledTaskPrincipal -UserId $LoggedInUser
$Task = New-ScheduledTask -Action $TaskAction -Principal $TaskPrincipal

$ScheduledTask = $null
try {
    $ScheduledTask = Register-ScheduledTask -TaskName 'TempToast' -TaskPath '\TempToast' -InputObject $Task
    Start-ScheduledTask -InputObject $ScheduledTask
} finally {
    if ($ScheduledTask) {
        Unregister-ScheduledTask -InputObject $ScheduledTask -Confirm:$false
    }
}

As a further example, you can refer to (or use) this ToastNotification PS module I have written that does just that, but offers a few more options. It allows any user to simply call Show-NotificationToLoggedInUser -Title "A title" -Message "Longer message" to display a toast notification to the user currently logged in. It is part of another unrelated project but the module itself can be used as stand-alone.


1 You could, of course, just as well put this logic in a script block instead. Just keep in mind that in the case of a scheduled task, powershell.exe isn't "run from another PowerShell host" so you can't pass it a ScriptBlock, only a string. Refer to Microsoft documentation on the -Command option for details.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1