Public/Imported/Get-UserSession.ps1
#Author: Warren Frame # https://github.com/RamblingCookieMonster/PowerShell/blob/master/Get-UserSession.ps1 function Get-UserSession { <# .SYNOPSIS Retrieves all user sessions from local or remote computers(s) .DESCRIPTION Retrieves all user sessions from local or remote computer(s). Note: Requires query.exe in order to run Note: This works against Windows Vista and later systems provided the following registry value is in place HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\AllowRemoteRPC = 1 Note: If query.exe takes longer than 15 seconds to return, an error is thrown and the next computername is processed. Suppress this with -erroraction silentlycontinue Note: If $sessions is empty, we return a warning saying no users. Suppress this with -warningaction silentlycontinue .PARAMETER computername Name of computer(s) to run session query against .parameter parseIdleTime Parse idle time into a timespan object .parameter timeout Seconds to wait before ending query.exe process. Helpful in situations where query.exe hangs due to the state of the remote system. .FUNCTIONALITY Computers .EXAMPLE Get-usersession -computername "server1" Query all current user sessions on 'server1' .EXAMPLE Get-UserSession -computername $servers -parseIdleTime | ?{$_.idletime -gt [timespan]"1:00"} | ft -AutoSize Query all servers in the array $servers, parse idle time, check for idle time greater than 1 hour. .NOTES Thanks to Boe Prox for the ideas - http://learn-powershell.net/2010/11/01/quick-hit-find-currently-logged-on-users/ .LINK http://gallery.technet.microsoft.com/Get-UserSessions-Parse-b4c97837 #> [cmdletbinding()] Param( [Parameter( Position = 0, ValueFromPipeline = $True)] [string[]]$computername = "localhost", [switch]$parseIdleTime, [validaterange(0, 120)]$timeout = 15 ) ForEach ($computer in $computername) { #start query.exe using .net and cmd /c. We do this to avoid cases where query.exe hangs #build temp file to store results. Loop until this works Do { $tempFile = [System.IO.Path]::GetTempFileName() start-sleep -Milliseconds 300 } Until(test-path $tempfile) #Record date. Start process to run query in cmd. I use starttime independently of process starttime due to a few issues we ran into $startTime = Get-Date $p = Start-Process -FilePath C:\windows\system32\cmd.exe -ArgumentList "/c query user /server:$computer > $tempfile" -WindowStyle hidden -passthru #we can't read in info or else it will freeze. We cant run waitforexit until we read the standard output, or we run into issues... #handle timeouts on our own by watching hasexited $stopprocessing = $false do { #check if process has exited $hasExited = $p.HasExited #check if there is still a record of the process Try { $proc = get-process -id $p.id -ErrorAction stop } Catch { $proc = $null } #sleep a bit start-sleep -seconds .5 #check if we have timed out, unless the process has exited if ( ( (Get-Date) - $startTime ).totalseconds -gt $timeout -and -not $hasExited -and $proc) { $p.kill() $stopprocessing = $true remove-item $tempfile -force Write-Error "$computer`: Query.exe took longer than $timeout seconds to execute" } } until($hasexited -or $stopProcessing -or -not $proc) if ($stopprocessing) { Continue } #if we are still processing, read the output! $sessions = get-content $tempfile remove-item $tempfile -force #handle no results if ($sessions) { 1..($sessions.count - 1) | ForEach-Object { #Start to build the custom object $temp = "" | Select-Object ComputerName, Username, SessionName, Id, State, IdleTime, LogonTime $temp.ComputerName = $computer #The output of query.exe is dynamic. #strings should be 82 chars by default, but could reach higher depending on idle time. #we use arrays to handle the latter. if ($sessions[$_].length -gt 5) { #if the length is normal, parse substrings if ($sessions[$_].length -le 82) { $temp.Username = $sessions[$_].Substring(1, 22).trim() $temp.SessionName = $sessions[$_].Substring(23, 19).trim() $temp.Id = $sessions[$_].Substring(42, 4).trim() $temp.State = $sessions[$_].Substring(46, 8).trim() $temp.IdleTime = $sessions[$_].Substring(54, 11).trim() $logonTimeLength = $sessions[$_].length - 65 try { $temp.LogonTime = get-date $sessions[$_].Substring(65, $logonTimeLength).trim() } catch { $temp.LogonTime = $sessions[$_].Substring(65, $logonTimeLength).trim() | out-null } } #Otherwise, create array and parse else { $array = $sessions[$_] -replace "\s+", " " -split " " $temp.Username = $array[1] #in some cases the array will be missing the session name. array indices change if ($array.count -lt 9) { $temp.SessionName = "" $temp.Id = $array[2] $temp.State = $array[3] $temp.IdleTime = $array[4] $temp.LogonTime = get-date $($array[5] + " " + $array[6] + " " + $array[7]) } else { $temp.SessionName = $array[2] $temp.Id = $array[3] $temp.State = $array[4] $temp.IdleTime = $array[5] $temp.LogonTime = get-date $($array[6] + " " + $array[7] + " " + $array[8]) } } #if specified, parse idle time to timespan if ($parseIdleTime) { $string = $temp.idletime #quick function to handle minutes or hours:minutes function convert-shortIdle { param($string) if ($string -match "\:") { [timespan]$string } else { New-TimeSpan -minutes $string } } #to the left of + is days if ($string -match "\+") { $days = new-timespan -days ($string -split "\+")[0] $hourMin = convert-shortIdle ($string -split "\+")[1] $temp.idletime = $days + $hourMin } #. means less than a minute elseif ($string -like "." -or $string -like "none") { $temp.idletime = [timespan]"0:00" } #hours and minutes else { $temp.idletime = convert-shortIdle $string } } #Output the result $temp } } } else { Write-warning "$computer`: No sessions found" } } } |