0

I am currently stuck at a problem involving a WMI Event subscriber on a remote session running in a background runspace.

Below is the representative code that reproduces the issue (the real code is too long to share here) but, essentially, through a PS script I remotely install advertised WSUS updates with reboots when necessary. It takes about 90 minutes end to end.

The issue I am trying to solve at the moment is that during the course of patching, the support staff inadvertently log in to the the server being remotely patched and do their admin activities. To remind them, I want to display a pop up message as soon as the user logs in when my remote script is running. I am trying to do it using a background runspace plugged into the main patching script. It makes use of WMI eventing on the target server (which is being patched) to monitor user logons and display message as soon as it detects one. The below code is working as I expect. It even survives target server reboots.

$RemoteServerName = 'Server1.contoso.com'

$UserLogonAlertScriptBlock = {
    param ($SyncedHashTable)

    $RemoteServerName = $SyncedHashTable.target
    try {
        $Session = New-PSSession -ComputerName $RemoteServerName -ErrorAction Stop
    } catch {}

    while($true){
        if($Session.State -eq 'Opened') {
            $RemoteMonitoringJob = Invoke-Command -Session $Session -AsJob -ScriptBlock {
                $null = Register-WMIEvent -Query "SELECT * FROM __InstanceCreationEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_LogonSession'" -SourceIdentifier 'User.Logon'
                Wait-Event -SourceIdentifier "User.Logon" -Timeout 7200 | ForEach-Object {
                    msg * /TIME:7200 /V "User logon detected" | Out-Null
                    $_ | Remove-Event
                }
            }
            while($RemoteMonitoringJob.State -in @('NotStarted','Running')) {
                Start-Sleep -Seconds 1
            }
        } else {
            while($true) {
                try {
                    $Session = New-PSSession -ComputerName $RemoteServerName -ErrorAction Stop
                } catch {}
                if($Session.State -eq 'Opened') {
                    break
                }
                Start-Sleep -Seconds 1
            }
        }
    }
}

$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.runspace = $Runspace
$SyncedHashTable = [hashtable]::Synchronized(@{})
$SyncedHashTable.host = $host
$SyncedHashTable.target = $RemoteServerName
$Runspace.Open()
$handle = $PowerShell.AddScript($UserLogonAlertScriptBlock).AddArgument($SyncedHashTable).BeginInvoke()

Write-Host '$(Get-Date): Long running script execution targeting $RemoteServerName has started'
Start-Sleep -Seconds 120 # it usually runs for upto 90 minutes, with remote reboots of $RemoteServerName
Write-Host "$(Get-Date): The script execution has completed"
### The code that cleans up the sticky event subscriber on the target server needs to be added here

The part I am stuck at is after the script completes its execution. The wsmanprovhost.exe running on the target server continues to stick around and shows alert messages when new users log on. I think it's because of the WMI event listener still being active on the box, not releasing the remote PS session.

In the above code, I need help close that remote listener so wsmanprovhost.exe disappears.

Could you please help?

PS. I have referred to @mklement0 's response in the following post but still no joy: The RunSpace and its closure

Update:

I have managed to address the challenge by adding a Boolean flag into the SyncedHashtable which is passed to the background runspace. When I want to stop the remote logon monitoring, in the main script I flip the flag. Since it's inside a synced hashtable, I can monitor that inside the runspace and terminate the remote invoke command job in the run space. But I still had to forcibly kill the remote wsmprovhost.exe as it refuses to go. I could do it by getting the pid of the remote PS session in advance. Not the most elegant way to close a remote PS session but it does the job for me. It's just that since the remote session is continuously monitoring for user logon event, there does not appear to be a way to run a piece of code in that session to unsubscribe the WMI event source. Will do more testing to see if there is any side effect.

Steve
  • 337
  • 4
  • 11

0 Answers0