{"id":411,"date":"2017-05-16T12:54:16","date_gmt":"2017-05-16T09:54:16","guid":{"rendered":"https:\/\/www.it-earth.ru\/?p=411"},"modified":"2017-05-16T12:54:16","modified_gmt":"2017-05-16T09:54:16","slug":"powershell-%d1%81%d0%ba%d1%80%d0%b8%d0%bf%d1%82-get-usersession","status":"publish","type":"post","link":"https:\/\/www.it-earth.ru\/?p=411","title":{"rendered":"PowerShell \u0441\u043a\u0440\u0438\u043f\u0442 Get-UserSession"},"content":{"rendered":"<p>PowerShell \u0441\u043a\u0440\u0438\u043f\u0442 Get-UserSession<\/p>\n<p><!--more--><\/p>\n<p>function\u00a0 {<br \/>\n&lt;#<br \/>\n.SYNOPSIS<br \/>\nRetrieves all user sessions from local or remote computers(s)<\/p>\n<p>.DESCRIPTION<br \/>\nRetrieves all user sessions from local or remote computer(s).<\/p>\n<p>Note:\u00a0\u00a0 Requires query.exe in order to run<br \/>\nNote:\u00a0\u00a0 This works against Windows Vista and later systems provided the following registry value is in place<br \/>\nHKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\AllowRemoteRPC = 1<br \/>\nNote:\u00a0\u00a0 If query.exe takes longer than 15 seconds to return, an error is thrown and the next computername is processed.\u00a0 Suppress this with -erroraction silentlycontinue<br \/>\nNote:\u00a0\u00a0 If $sessions is empty, we return a warning saying no users.\u00a0 Suppress this with -warningaction silentlycontinue<\/p>\n<p>.PARAMETER computername<br \/>\nName of computer(s) to run session query against<\/p>\n<p>.parameter parseIdleTime<br \/>\nParse idle time into a timespan object<\/p>\n<p>.parameter timeout<br \/>\nSeconds to wait before ending query.exe process.\u00a0 Helpful in situations where query.exe hangs due to the state of the remote system.<\/p>\n<p>.FUNCTIONALITY<br \/>\nComputers<\/p>\n<p>.EXAMPLE<br \/>\nGet-usersession -computername &#8220;server1&#8243;<\/p>\n<p>Query all current user sessions on &#8216;server1&#8217;<\/p>\n<p>.EXAMPLE<br \/>\nGet-UserSession -computername $servers -parseIdleTime | ?{$_.idletime -gt [timespan]&#8221;1:00&#8221;} | ft -AutoSize<\/p>\n<p>Query all servers in the array $servers, parse idle time, check for idle time greater than 1 hour.<\/p>\n<p>.NOTES<br \/>\nThanks to Boe Prox for the ideas &#8211; http:\/\/learn-powershell.net\/2010\/11\/01\/quick-hit-find-currently-logged-on-users\/<\/p>\n<p>.LINK<br \/>\nhttp:\/\/gallery.technet.microsoft.com\/Get-UserSessions-Parse-b4c97837<\/p>\n<p>#&gt;<br \/>\n[cmdletbinding()]<br \/>\nParam(<br \/>\n[Parameter(<br \/>\nPosition = 0,<br \/>\nValueFromPipeline = $True)]<br \/>\n[string[]]$ComputerName = &#8220;localhost&#8221;,<\/p>\n<p>[switch]$ParseIdleTime,<\/p>\n<p>[validaterange(0,120)]<br \/>\n[int]$Timeout = 15<br \/>\n)<br \/>\nProcess<br \/>\n{<br \/>\nForEach($computer in $ComputerName)<br \/>\n{<\/p>\n<p>#start query.exe using .net and cmd \/c.\u00a0 We do this to avoid cases where query.exe hangs<\/p>\n<p>#build temp file to store results.\u00a0 Loop until we see the file<br \/>\nTry<br \/>\n{<br \/>\n$Started = Get-Date<br \/>\n$tempFile = [System.IO.Path]::GetTempFileName()<\/p>\n<p>Do{<br \/>\nstart-sleep -Milliseconds 300<\/p>\n<p>if( ((Get-Date) &#8211; $Started).totalseconds -gt 10)<br \/>\n{<br \/>\nThrow &#8220;Timed out waiting for temp file &#8216;$TempFile'&#8221;<br \/>\n}<br \/>\n}<br \/>\nUntil(Test-Path -Path $tempfile)<br \/>\n}<br \/>\nCatch<br \/>\n{<br \/>\nWrite-Error &#8220;Error for &#8216;$Computer&#8217;: $_&#8221;<br \/>\nContinue<br \/>\n}<\/p>\n<p>#Record date.\u00a0 Start process to run query in cmd.\u00a0 I use starttime independently of process starttime due to a few issues we ran into<br \/>\n$Started = Get-Date<br \/>\n$p = Start-Process -FilePath C:\\windows\\system32\\cmd.exe -ArgumentList &#8220;\/c query user \/server:$computer &gt; $tempfile&#8221; -WindowStyle hidden -passthru<\/p>\n<p>#we can&#8217;t read in info or else it will freeze.\u00a0 We cant run waitforexit until we read the standard output, or we run into issues&#8230;<br \/>\n#handle timeouts on our own by watching hasexited<br \/>\n$stopprocessing = $false<br \/>\ndo<br \/>\n{<\/p>\n<p>#check if process has exited<br \/>\n$hasExited = $p.HasExited<\/p>\n<p>#check if there is still a record of the process<br \/>\nTry<br \/>\n{<br \/>\n$proc = Get-Process -id $p.id -ErrorAction stop<br \/>\n}<br \/>\nCatch<br \/>\n{<br \/>\n$proc = $null<br \/>\n}<\/p>\n<p>#sleep a bit<br \/>\nstart-sleep -seconds .5<\/p>\n<p>#If we timed out and the process has not exited, kill the process<br \/>\nif( ( (Get-Date) &#8211; $Started ).totalseconds -gt $timeout -and -not $hasExited -and $proc)<br \/>\n{<br \/>\n$p.kill()<br \/>\n$stopprocessing = $true<br \/>\nRemove-Item $tempfile -force<br \/>\nWrite-Error &#8220;$computer`: Query.exe took longer than $timeout seconds to execute&#8221;<br \/>\n}<br \/>\n}<br \/>\nuntil($hasexited -or $stopProcessing -or -not $proc)<\/p>\n<p>if($stopprocessing)<br \/>\n{<br \/>\nContinue<br \/>\n}<\/p>\n<p>#if we are still processing, read the output!<br \/>\ntry<br \/>\n{<br \/>\n$sessions = Get-Content $tempfile -ErrorAction stop<br \/>\nRemove-Item $tempfile -force<br \/>\n}<br \/>\ncatch<br \/>\n{<br \/>\nWrite-Error &#8220;Could not process results for &#8216;$computer&#8217; in &#8216;$tempfile&#8217;: $_&#8221;<br \/>\ncontinue<br \/>\n}<\/p>\n<p>#handle no results<br \/>\nif($sessions){<\/p>\n<p>1..($sessions.count &#8211; 1) | Foreach-Object {<\/p>\n<p>#Start to build the custom object<br \/>\n$temp = &#8220;&#8221; | Select ComputerName, Username, SessionName, Id, State, IdleTime, LogonTime<br \/>\n$temp.ComputerName = $computer<\/p>\n<p>#The output of query.exe is dynamic.<br \/>\n#strings should be 82 chars by default, but could reach higher depending on idle time.<br \/>\n#we use arrays to handle the latter.<\/p>\n<p>if($sessions[$_].length -gt 5){<\/p>\n<p>#if the length is normal, parse substrings<br \/>\nif($sessions[$_].length -le 82){<\/p>\n<p>$temp.Username = $sessions[$_].Substring(1,22).trim()<br \/>\n$temp.SessionName = $sessions[$_].Substring(23,19).trim()<br \/>\n$temp.Id = $sessions[$_].Substring(42,4).trim()<br \/>\n$temp.State = $sessions[$_].Substring(46,8).trim()<br \/>\n$temp.IdleTime = $sessions[$_].Substring(54,11).trim()<br \/>\n$logonTimeLength = $sessions[$_].length &#8211; 65<br \/>\ntry{<br \/>\n$temp.LogonTime = Get-Date $sessions[$_].Substring(65,$logonTimeLength).trim() -ErrorAction stop<br \/>\n}<br \/>\ncatch{<br \/>\n#Cleaning up code, investigate reason behind this.\u00a0 Long way of saying $null&#8230;.<br \/>\n$temp.LogonTime = $sessions[$_].Substring(65,$logonTimeLength).trim() | Out-Null<br \/>\n}<\/p>\n<p>}<\/p>\n<p>#Otherwise, create array and parse<br \/>\nelse{<br \/>\n$array = $sessions[$_] -replace &#8220;\\s+&#8221;, &#8221; &#8221; -split &#8221; &#8221;<br \/>\n$temp.Username = $array[1]<\/p>\n<p>#in some cases the array will be missing the session name.\u00a0 array indices change<br \/>\nif($array.count -lt 9){<br \/>\n$temp.SessionName = &#8220;&#8221;<br \/>\n$temp.Id = $array[2]<br \/>\n$temp.State = $array[3]<br \/>\n$temp.IdleTime = $array[4]<br \/>\ntry<br \/>\n{<br \/>\n$temp.LogonTime = Get-Date $($array[5] + &#8221; &#8221; + $array[6] + &#8221; &#8221; + $array[7]) -ErrorAction stop<br \/>\n}<br \/>\ncatch<br \/>\n{<br \/>\n$temp.LogonTime = ($array[5] + &#8221; &#8221; + $array[6] + &#8221; &#8221; + $array[7]).trim()<br \/>\n}<br \/>\n}<br \/>\nelse{<br \/>\n$temp.SessionName = $array[2]<br \/>\n$temp.Id = $array[3]<br \/>\n$temp.State = $array[4]<br \/>\n$temp.IdleTime = $array[5]<br \/>\ntry<br \/>\n{<br \/>\n$temp.LogonTime = Get-Date $($array[6] + &#8221; &#8221; + $array[7] + &#8221; &#8221; + $array[8]) -ErrorAction stop<br \/>\n}<br \/>\ncatch<br \/>\n{<br \/>\n$temp.LogonTime = ($array[6] + &#8221; &#8221; + $array[7] + &#8221; &#8221; + $array[8]).trim()<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>#if specified, parse idle time to timespan<br \/>\nif($parseIdleTime){<br \/>\n$string = $temp.idletime<\/p>\n<p>#quick function to handle minutes or hours:minutes<br \/>\nfunction Convert-ShortIdle {<br \/>\nparam($string)<br \/>\nif($string -match &#8220;\\:&#8221;){<br \/>\n[timespan]$string<br \/>\n}<br \/>\nelse{<br \/>\nNew-TimeSpan -Minutes $string<br \/>\n}<br \/>\n}<\/p>\n<p>#to the left of + is days<br \/>\nif($string -match &#8220;\\+&#8221;){<br \/>\n$days = New-TimeSpan -days ($string -split &#8220;\\+&#8221;)[0]<br \/>\n$hourMin = Convert-ShortIdle ($string -split &#8220;\\+&#8221;)[1]<br \/>\n$temp.idletime = $days + $hourMin<br \/>\n}<br \/>\n#. means less than a minute<br \/>\nelseif($string -like &#8220;.&#8221; -or $string -like &#8220;none&#8221;){<br \/>\n$temp.idletime = [timespan]&#8221;0:00&#8243;<br \/>\n}<br \/>\n#hours and minutes<br \/>\nelse{<br \/>\n$temp.idletime = Convert-ShortIdle $string<br \/>\n}<br \/>\n}<\/p>\n<p>#Output the result<br \/>\n$temp<br \/>\n}<br \/>\n}<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\nWrite-Warning &#8220;&#8216;$computer&#8217;: No sessions found&#8221;<br \/>\n}<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u0432: 12<\/p>","protected":false},"excerpt":{"rendered":"<p>PowerShell \u0441\u043a\u0440\u0438\u043f\u0442 Get-UserSession \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u0432: 12<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[104],"class_list":["post-411","post","type-post","status-publish","format-standard","hentry","category-itmemo","tag-powershell--get-usersession"],"_links":{"self":[{"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=\/wp\/v2\/posts\/411","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=411"}],"version-history":[{"count":1,"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=\/wp\/v2\/posts\/411\/revisions"}],"predecessor-version":[{"id":414,"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=\/wp\/v2\/posts\/411\/revisions\/414"}],"wp:attachment":[{"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=411"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=411"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.it-earth.ru\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=411"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}