powershellautomationsymantec

Powershell interaction between SYSTEM user and logged on users


I want a powershell script running as SYSTEM user to display a Windowsform on another users session and have interaction with the controls of it.

I am trying to automate the installation/repair of Symantec Endpoint Protection with Solarwinds N-Able. This platform uses agent software which is installed on clients to monitor and execute tasks on them.

The agent uses the NT AUTHORITY\SYSTEM user to execute tasks on the machine. The installation of SEP works fine so far, but the reboots in between the deinstall/install phases are still uncontrollable as a regular user on the machine. I want the currently active user be able to control this reboot cycles. Something like the Windows update reboot prompt.

My idea is to display a windowsform on logged on user's desktop with controls on it to execute or delay the reboot. My question now is how do I display a windowsform defined in powershell on another user's session, and how am I going to get the actions of the controls back in the script that is running on the SYSTEM user.

I've already tried the msg command to send a message to all the users on the system. But this is only one-way communication and isn't really meant to be used in situations like this is guess.


Solution

  • I found the solution for my problem. I used the WTSSendMessage function which boxdog suggested in the comments. I combined this with a script that gets the sessionID's of the logged on users. I minimized this script to only get the "Active" user's sessionID. This is then used to send the message to user. I tested it in Solarwinds and so far this works flawless.

    My coding skills are pretty basic, but this is the end result.

    function Send-MessageBox
    {
    [CmdletBinding()]
    [OutputType([string])]
    Param
    (        
        [Parameter(Mandatory=$true, Position=0)]
        [string]$title,
        [Parameter(Mandatory=$true, Position=1)]
        [string]$message,
        [Parameter(Mandatory=$true, Position=2)]
        [int]$duration,
        [Parameter(Mandatory=$true, Position=3)]
        [int]$style
    )
    
    Begin
    {
        $typeDefinition = @"
            using System;
            using System.Runtime.InteropServices;
    
            public class WTSMessage {
                [DllImport("wtsapi32.dll", SetLastError = true)]
                public static extern bool WTSSendMessage(
                    IntPtr hServer,
                    [MarshalAs(UnmanagedType.I4)] int SessionId,
                    String pTitle,
                    [MarshalAs(UnmanagedType.U4)] int TitleLength,
                    String pMessage,
                    [MarshalAs(UnmanagedType.U4)] int MessageLength,
                    [MarshalAs(UnmanagedType.U4)] int Style,
                    [MarshalAs(UnmanagedType.U4)] int Timeout,
                    [MarshalAs(UnmanagedType.U4)] out int pResponse,
                    bool bWait
                );
    
                static int response = 0;
    
                public static int SendMessage(int SessionID, String Title, String Message, int Timeout, int MessageBoxType) {
                    WTSSendMessage(IntPtr.Zero, SessionID, Title, Title.Length, Message, Message.Length, MessageBoxType, Timeout, out response, true);
    
                    return response;
                }
            }
    "@
    }
    
    Process
    {
        if (-not ([System.Management.Automation.PSTypeName]'WTSMessage').Type)
        {
            Add-Type -TypeDefinition $typeDefinition
        }
    
        $RawOuput = (quser) -replace '\s{2,}', ',' | ConvertFrom-Csv
        $sessionID = $null
    
        Foreach ($session in $RawOuput) {  
            if(($session.sessionname -notlike "console") -AND ($session.sessionname -notlike "rdp-tcp*")) {
                if($session.ID -eq "Active"){    
                    $sessionID = $session.SESSIONNAME
                }                        
            }else{
                if($session.STATE -eq "Active"){      
                    $sessionID = $session.ID
                }        
            }   
        }
        $response = [WTSMessage]::SendMessage($sessionID, $title, $message, $duration, $style )
    }
    End
    {
        Return $response
    }
    }
    
    Send-MessageBox -title "Title" -message "Message" -duration 60 -style 0x00001034L