Managing WSMan Remoting Sessions
In this issue:
Overview
A demonstration of using Get-WSManInstance to get information on open PowerShell remoting sessions using the WSMan protocol. The information is returned as XML so you'll need to perform a few custom conversions to make the data more user-friendly. Finally, I share a function that you can use to summarize the status of a PowerShell remoting session.
Introduction
A few newsletters ago we looked at ways to manage WSMan remoting connections in PowerShell. We wrapped up last time by exploring how to use the WSManInstance cmdlets. I showed you earlier that you can use Get-Process to look for instances of WSManProvHost. This is the process that indicates a remote PowerShell connection. You can glean some information such as the username and when the process started. However, that's not the only way you can discover information about the remote connection.
Get-WSManInstance
Last time, I demonstrated how to use Get-WSManInstance to retrieve WSMan information. Here's a very neat way to look at open sessions on a remote computer.
PS C:\> Get-WSManInstance -ResourceURI Shell -Enumerate -ComputerName SRV1
rsp : http://schemas.microsoft.com/wbem/wsman/1/windows/shell
lang : en-US
ShellId : A0D3ACBB-2A84-429B-866A-22498DC3BF43
Name : Runspace1
ResourceUri : http://schemas.microsoft.com/powershell/Microsoft.PowerShell
Owner : company\artd
ClientIP : fe80::f36d:fd64:6c68:6b57%5
ProcessId : 2844
IdleTimeOut : PT7200.000S
InputStreams : stdin pr
OutputStreams : stdout
MaxIdleTimeOut : PT2147483.647S
Locale : en-US
DataLocale : en-US
CompressionMode : XpressCompression
ProfileLoaded : Yes
Encoding : UTF8
BufferMode : Block
State : Connected
ShellRunTime : P0DT0H39M42S
ShellInactivity : P0DT0H0M35S
MemoryUsed : 73MB
ChildProcesses : 2
I am running this command from a client computer with an account that has admin rights on SRV1. Enumerating the shells shows me open connections.
PS C:\> Invoke-Command { Get-Process -id 2844 } -ComputerName srv1
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName PSComputerName
------- ------ ----- ----- ------ -- -- ----------- --------------
525 28 47260 58572 0.23 2844 0 wsmprovhost srv1
> Remember, Get-WSManInstance doesn't open a WSMan session like Invoke-Command.
Parsing XML Data
The data you see are based on XML values. It might be nice to make some of these values more user-friendly. For example, the detected session is using 73MB of memory. That string value isn't very useful if I'm trying to sort this property on multiple objects. I really need the raw value in bytes so that 73MB is returned as 76546048. I wrote a helper function using regular expressions to do just that.
function _parseMemoryString {
#convert values like 11MB to 11534336
[cmdletbinding()]
param([string]$StringValue)
switch -Regex ($StringValue ) {
'\d+KB' {
$val = 1KB
}
'\d+MB' {
$val = 1MB
}
'\d+GB' {
$val = 1GB
}
} #switch
if ($val) {
[int]$i = ([regex]'\d+').Match($StringValue).value
$i * $val
}
else {
Write-Warning "Failed to parse $StringValue"
$stringValue
}
} #close function
I'm assuming a remoting session won't be using terabytes of memory, but you could easily modify the function.
PS C:\> _parseMemoryString 73MB
76546048
The other formatting is related to how datetime values are stored in XML. The output includes a value like P0DT0H39M42S. You can translate this once you understand that 0D means 0 days, 0H means 0 hours, 39M means 39 minutes and so on. I could use regular expressions to parse this string and create a TimeSpan object from the values, but there's an even easier way.
The .NET Framework includes a class, [System.Xml.XmlConvert] that is designed for this very problem. We can use the static ToTimeSpan() method to convert the string into a TimeSpan object.
PS C:\> [System.Xml.XmlConvert]::ToTimeSpan('P0DT0H39M42S')
Days : 0
Hours : 0
Minutes : 39
Seconds : 42
Milliseconds : 0
Ticks : 23820000000
TotalDays : 0.0275694444444444
TotalHours : 0.661666666666667
TotalMinutes : 39.7
TotalSeconds : 2382
TotalMilliseconds : 2382000
This also works for values that you see with IdleTimeout.
PS C:\> [System.Xml.XmlConvert]::ToTimeSpan('PT7200.000S')
Days : 0
Hours : 2
Minutes : 0
Seconds : 0
Milliseconds : 0
Ticks : 72000000000
TotalDays : 0.0833333333333333
TotalHours : 2
TotalMinutes : 120
TotalSeconds : 7200
TotalMilliseconds : 7200000
If I know how long the shell has been running, I can calculate when the shell started.
(Get-Date).AddSeconds(-([System.Xml.XmlConvert]::ToTimeSpan($_.ShellRunTime).TotalSeconds))
The "trick" is to add a negative number of seconds.