Powershell run as system display popup to user?

I am writing a script to update VPN software on client systems, and I need to be able to interact with the user via a popup to let them know. The script itself just needs to inform the user, stop the VPN processes, install several MSIs, and then inform the user upon completion. When I run this as the user, then the software cannot install. When I run it as system, then the software installs and the user does not get the popups.
With a batch file, I had access to a startasuser.exe (syntax example: &cmd /c startasuser.exe ///silent reg import %RegFile%
), which would let me run something in the user context. I have not found that ability in Powershell, and I cannot seem to execute that from Powershell since it calls another instance of command.exe by itself and then runs the arguments. Is there a Powershell solution to this? Any assistance is greatly appreciated.

How are you prompting your users? Are you using the .NET MessageBox class?

I’m embarrassed to say I think so. I don’t know what calls .NET and what doesn’t. I use the following:

if ($VPNActive) { (new-object -ComObject wscript.shell).Popup(“Your Cisco AnyConnect VPN software needs updating. Please save any work and then click ‘OK’.” ,1800 ,“Cisco AnyConnect”,0x30 + 0x0) }

Don’t know if this’ll work, but try this instead of the New-Object

[System.Windows.Forms.MessageBox]::Show('Your Cisco AnyConnect VPN softare needs updating. Please save any work and then click OK.')

I’m going to fumble through this with you, this definitely isn’t my strong spot with PowerShell lol

No luck there, but thanks for the suggestion. While I was digging into the error log, I found it is not happy with one of my stop process commands. If it got to that point though, it blew past the message box and didn’t show it.

So basically the question now becomes one of the following:
How do I enable an interactive PowerShell session through a Scheduled Task?
What System processes can PowerShell take advantage of to push a prompt/message?

I’ll start looking into these as well. I’m really curious now.

“When I run this as the user, then the software cannot install.

That is because PowerShell will use the session identity of the user using PoSH, not the user logged on to the machine.
This is a security boundary in Windows not a PoSH issue.

“With a batch file, I had access to a “

    startasuser.exe (syntax example: &cmd /c startasuser.exe ///silent reg import %RegFile%) 

“I have not found that ability in Powershell”

You can use MS SysInternals tools, psexec to do this same thing, this way…

    psexec \\computer -u user -i -d command

See these resources:
https://docs.microsoft.com/en-us/sysinternals
https://docs.microsoft.com/en-us/sysinternals/downloads/psexec

Utilities like Telnet and remote control programs like Symantec’s PC Anywhere let you execute programs on remote systems, but they can be a pain to set up and require that you install client software on the remote systems that you wish to access. PsExec is a light-weight telnet-replacement that lets you execute processes on other systems, complete with full interactivity for console applications, without having to manually install client software. PsExec’s most powerful uses include launching interactive command-prompts on remote systems and remote-enabling tools like IpConfig that otherwise do not have the ability to show information about remote systems.

Note: some anti-virus scanners report that one or more of the tools are infected with a “remote admin” virus. None of the PsTools contain viruses, but they have been used by viruses, which is why they trigger virus notifications.

Try something like this example. Maybe a bit more that you want, but an idea for you.

    function Show-BalloonTip
    {
        param
        (
            [Parameter(Mandatory=$true)][string]$Text,
            [string]$Title = "Message from PowerShell",
            [ValidateSet('Info','Warning','Error','None')][string]$Icon = 'Info'
        )

        Add-Type -AssemblyName  System.Windows.Forms

        # we use private variables only. No need for global scope
        $balloon = New-Object System.Windows.Forms.NotifyIcon 
        $cleanup = 
        {    
            # this gets executed when the user clicks the balloon tip dialog

            # take the balloon from the event arguments, and dispose it
            $event.Sender.Dispose()
            # take the event handler names also from the event arguments,
            # and clean up
            Unregister-Event  -SourceIdentifier $event.SourceIdentifier
            Remove-Job -Name $event.SourceIdentifier
            $name2 = "M" + $event.SourceIdentifier
            Unregister-Event  -SourceIdentifier $name2
            Remove-Job -Name $name2
        }
        $showBalloon = 
        {
            # this gets executed when the user clicks the tray icon
            $event.Sender.ShowBalloonTip(5000) 
        }

        # use unique names for event handlers so you can open multiple balloon tips
        $name = [Guid]::NewGuid().Guid

        # subscribe to the balloon events
        $null = Register-ObjectEvent -InputObject $balloon -EventName BalloonTipClicked -Source $name -Action $cleanup
        $null = Register-ObjectEvent -InputObject $balloon -EventName MouseClick -Source "M$name" -Action $showBalloon

        # use the current application icon as tray icon
        $path = (Get-Process -id $pid).Path
        $balloon.Icon  = [System.Drawing.Icon]::ExtractAssociatedIcon($path) 

        # configure the balloon tip
        $balloon.BalloonTipIcon  = $Icon
        $balloon.BalloonTipText  = $Text
        $balloon.BalloonTipTitle  = $Title

        # make the tray icon visible
        $balloon.Visible  = $true 
        # show the balloon tip
        $balloon.ShowBalloonTip(5000) 
    }

If you’re running as System user, it usually goes to the Session 0 window in Windows 10. I’m not sure how to put it on the console window except with psexec.

Try msg.exe. I used this in an old VBS script that ran as a scheduled task to notify anyone logged into the server
strCmd = "c:\WINDOWS\system32\msg.exe * Starting the nightly shutdown of service: " & vServiceName
oWShell.run(strCmd)

You should be able to use the same command and concept from powershell.

I looked at msg.exe, it worked in testing. Once I got it to my environment at work and ran it as system, it would give me an error 5 indicating lack of permissions. It’s looking like I may just have to go to a command file with this one.

I ended up going with a mixture of PoSH and batch.

  1. A PoSH script runs as system, figures out if the adapter is active and sets a flag file.
  2. A command file runs as the user, picks up the flag and displays a popup warning that the install will disconnect them.
  3. A PoSH script installs the new software after the box is dismissed or times out.
  4. A command file runs as the user and displays the finish popup.

It feels like I could have done a more compact job, but the restrictions on my environment got in the way, it does work. Thanks for all the help.