During a recent PowerShell training class we naturally covered PowerShell remoting. During the discussion I explained that a remote PSSession is essentially transparent to any currently logged on user. Unless of course you reboot the computer! One way you can identify a remote session is by the presence of the wsmprovhost process. You should see this process whenever there is a remote PSSession to the computer. So then the discussion turned to tracking who might have sessions across multiple computers which is especially helpful when dealing with disconnected sessions since you can only see your own sessions. I don't have a perfect solution, but let's see if this helps. Here is Get-PSRemoteSession.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
#requires -version 3.0 Function Get-PSRemoteSession { <# .SYNOPSIS Get remote PSSession processes. .DESCRIPTION This command uses CIM to retrieve the wsmprovhost process that might be running on a remote computer. You can use this information to determine how long a session has been running and by what user. By default the command shows a summary. If you want to see the full detail, use the -Full parameter. The process owner and runtime will be added to the object in either case. .PARAMETER Computername This parameter has aliases of CN, Name and PSComputername .PARAMETER Full Write the full process object to the pipeline instead of the summary. .EXAMPLE PS C:\> Get-PSRemoteSession lon-dc1 ProcessID : 7016 CreationDate : 4/3/2014 1:25:32 PM Runtime : 23:36:38.0799372 Owner : MYDOMAIN\Jeff PSComputername : lon-dc1 ProcessID : 4676 CreationDate : 4/4/2014 12:23:41 PM Runtime : 00:38:28.9568735 Owner : MYDOMAIN\Administrator PSComputername : lon-dc1 .EXAMPLE PS C:\> Get-PSRemoteSession lon-dc1 -full | select Owner,ProcessID,VM,WS,runtime Owner : MYDOMAIN\Jeff ProcessID : 7016 VM : 163262464 WS : 44392448 Runtime : 23:47:39.0140240 Owner : MYDOMAIN\Administrator ProcessID : 4676 VM : 180445184 WS : 46551040 Runtime : 00:49:29.8899602 Get full process information and select some key properties .EXAMPLE PS C:\> get-remotesession chi-dc04,chi-dc01 | Group Owner -NoElement Count Name ----- ---- 1 GLOBOMANTICS\jeff 2 GLOBOMANTICS\Administr... Display what user accounts are using remote sessions on the specified computers. .EXAMPLE PS C:\> Get-Content c:\work\computers.txt | Get-PSRemoteSession | where {$_.Runtime -gt "16:00:00"} For a list of computers, get remote PSSessions that have been running longer than 16 hours. .EXAMPLE PS C:\> get-remotesession chi-dc04,chi-dc01,chi-app01 | sort PSComputername | format-table -GroupBy PSComputername -Property CreationDate,Runtime,Owner PSComputerName: chi-app01 CreationDate Runtime Owner ------------ ------- ----- 4/11/2014 9:58:16 AM 00:04:29.8719959 GLOBOMANTICS\Administrator PSComputerName: chi-dc01 CreationDate Runtime Owner ------------ ------- ----- 4/11/2014 9:46:24 AM 00:16:21.2699348 GLOBOMANTICS\Administrator PSComputerName: chi-dc04 CreationDate Runtime Owner ------------ ------- ----- 4/11/2014 9:43:06 AM 00:19:36.8797428 GLOBOMANTICS\jeff 4/11/2014 9:47:25 AM 00:15:18.0563043 GLOBOMANTICS\Administrator .NOTES Version : 1.0 Last Updated : April 11, 2014 Learn more: PowerShell in Depth: An Administrator's Guide (http://www.manning.com/jones2/) PowerShell Deep Dives (http://manning.com/hicks/) Learn PowerShell 3 in a Month of Lunches (http://manning.com/jones3/) Learn PowerShell Toolmaking in a Month of Lunches (http://manning.com/jones4/) **************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .LINK Get-CimInstance about_PSSessions #> [cmdletbinding()] Param( [Parameter(Position=0, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] [ValidateNotNullorEmpty()] [Alias("CN","Name","PSComputername")] [string[]]$Computername, [switch]$Full ) Begin { Write-Verbose "Starting Get-PSRemoteSession" } #begin Process { foreach ($computer in $computername) { #test connection Write-Verbose "Testing if computer $computer is pingable" #do a single ping to verify computer is up If (Test-Connection -ComputerName $computer -count 1 -Quiet) { Write-Verbose "Querying $computer" Try { #use CIM to remotely query the computer $data = Get-CimInstance win32_process -filter "name='wsmprovhost.exe'" ` -ComputerName $computer -ErrorAction Stop -ErrorVariable MyErr if ($data) { Write-verbose "Found sessions on $computer" Write-verbose ($data[0] | out-string) #add some custom properties $data | Add-Member -membertype ScriptProperty -Name Runtime -value { (Get-Date) - $this.creationdate} $data | Add-Member -MemberType ScriptProperty -Name Owner -Value { $owner = $this | Invoke-CimMethod -MethodName GetOwner #write the owner information "$($owner.domain)\$($owner.user)" } if ($Full) { #write the full process object with additions to the pipeline $data } else { #get process summary $data | Select ProcessID,CreationDate,RunTime,Owner,PSComputername } } #if $data } #try Catch { Write-Warning "Could not query $computer" Write-Warning $myErr.ErrorRecord.Exception.Message Write-Debug "Suspend script to debug `$myErr exception" } #catch } #if test-connection works else { Write-Warning "Failed to ping $computer" } } #foreach computer } #process End { Write-verbose "Ending Get-PSRemoteSession" } #end } #end function
This function takes a computername as a parameter. It does a quick ping to verify the computer is running. I probably should have made that optional, but I needed it at the time. The function then queries the computer using Get-CimInstance for all instances of the wsmprovhost.exe process. I'm using CIM because datetime values are automatically formatted which makes it much easier to add a custom property indicating how long the process, and presumably the remote session, have been running. I also add a custom property to get the process Owner. Due to a quirk (bug?) in the CIM cmdlets, I can even query a remote computer running PowerShell 2.0. When using a filter, the CIM cmdlets work with a v2 computer. I won't question it but will take advantage of it.
By default, the function writes a summary object to the pipeline.
The one thing I have yet to figure out is a way to show what computer each session is connected from. Although even if I could make the correlation with an active network connection, I'm not sure that would help in the event of a disconnected session. Nor can I tell the state of the session from the process.
I included an option to get the full process object so you could run commands like this:
But I suspect for many of you the summary will suffice. Here are some examples.
Get-Content c:\work\chi.txt | get-psremotesession | out-gridview -title "Remote Sessions"
If you kill the wsmprovhost process, that will break the PSSession so be careful. But at least now you have a way of identifying what sessions might be open. I hope you'll let me know what you think. Enjoy!
Reading through the script, one thing confuses me… $this. I don’t see it set anywhere, so is it a special variable?
Good eyes. Probably should have mentioned that. I am using Add-Member to define a new property. But instead of a NoteProperty I am creating a ScriptProperty. The value is the result of a scriptblock. In this context you use $this to indicate the current object in the pipeline. I could have taken extra steps to create my own object and type extension, in which case I would have used this same type of code to define the property, but in an XML file. My script does it all on the fly using Add-Member. So yeah, this is a special case.
So, it sounds kind of like $_, but for scriptblocks? I found it searching through about_Automatic_Variables, but the description seems a little vague and hard to wrap my head around.
Sorry, I know this is a bit off topic for what you were posting; I’d just never seen that one yet.
That is kinda correct. But as I said this is a special use case. I may have to write an article on this.
When we use the -Filter the CIM Cmdlets it does work with the machine having PowerShell v2…..So does it fall back to using DCOM when the remote machine doesn’t has winrm configured?
Just wanted to get little insight on this one…..this bug is helpful 🙂
That is the only explanation I can think of.