Advertisement
Lee_Dailey

function Get-LogOnOffTimeSpanInfo

Oct 8th, 2018
299
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #requires -RunAsAdministrator
  2. #requires -Version 4
  3.  
  4.  
  5.  
  6. function Get-LogOnOffTimeSpanInfo
  7.     {
  8.     <#
  9.     .SYNOPSIS
  10.     Find all longon/logoff & total active session times.
  11.  
  12.     .DESCRIPTION
  13.     This script finds all logon, logoff and total active session times of users on the computer specified.
  14.         For this script to function as expected, the advanced AD policies ...
  15.         - Audit Logon
  16.         - Audit Logoff
  17.         - Audit Other Logon/Logoff Events
  18.         ... must be enabled and targeted to the appropriate computers via GPO.
  19.  
  20.     One thing that it does NOT do on win7ps5.1 is get the current logged in user.
  21.         I don't know why, tho.
  22.  
  23.     .PARAMETER ComputerName
  24.     The name of the computer to check. Defaults to "$env:COMPUTERNAME".
  25.  
  26.     .PARAMETER IncludeOrphanLogons
  27.     This will include logon entries that do NOT have matching logoff entries.
  28.         The result will be some VERY long timespans.
  29.  
  30.     .EXAMPLE
  31.     No examples at this time.
  32.  
  33.     .NOTES
  34.     Source = Random-PowerShell-Work/Get-UserLogonSessionHistory.ps1 at master
  35.         adbertram/Random-PowerShell-Work
  36.         — https://github.com/adbertram/Random-PowerShell-Work/blob/master/ActiveDirectory/Get-UserLogonSessionHistory.ps1
  37.  
  38.     Version info ..
  39.     - 2018-10-06 = changes made by Lee_Dailey
  40.         renamed from "Get-EventLogs" to "Get-LogOnOffTimeSpanInfo"
  41.         = the name was far too similar to the builtin "Get-EventLog" cmdlet
  42.         = original source name was "Get-UserLogonSessionHistory"
  43.         modified to make it more readable [to me] & fewer long lines
  44.         moved CBH into the function instead of before it
  45.         renamed .SYNOPSIS to .DESCRIPTION since it is a LONG description
  46.         added a less wordy .SYSNOPSIS
  47.         reformatted to my sometimes odd preferences
  48.         added 4-space indents instead of two
  49.         removed entirely unneeded TRY @ 31 & matching CATCH @ 119
  50.         also removed unneeded TRY @ 50 & matching CATCH @ 111
  51.         removed credentials code
  52.         = use an elevated session
  53.         tried to fix test for orphaned start time [no stop time to go with it]
  54.         = old version left that user out
  55.         = this will give _really_ odd timespans for anyone other than the current user [*grin*]
  56.         replaced Write-Verbose calls with Write-Information
  57.         = avoids unwanted verbose output of the Get-WinEvent cmdlet
  58.         set default $ComputerName = $env:COMPUTERNAME
  59.         removed "Mandatory" for $ComputerName parameter
  60.         added $IncludeOrphanLogons switch, code to use it, & ".PARAMETER" info
  61.  
  62.     #>
  63.  
  64.     Param
  65.         (
  66.         [Parameter (
  67.             Position = 0
  68.             )]
  69.         [ValidateNotNullOrEmpty ()]
  70.         [string]
  71.         $ComputerName = $env:COMPUTERNAME,
  72.  
  73.         [Parameter ()]
  74.         [switch]
  75.         $IncludeOrphanLogons
  76.  
  77.         )
  78.  
  79.     begin
  80.         {
  81.         $NewSessionStartMsg = @'
  82. New session start event found:
  83.    event ID = {0}
  84.    username = {1}
  85.    logonID  = {2}
  86.    time     = {3}
  87. '@
  88.  
  89.         #region >> define events to indicate session start or stop
  90.         $SessionEvents = @(
  91.             ## Advanced Audit Policy --> Audit Logon
  92.             @{
  93.                 Label = 'Logon'
  94.                 EventType = 'SessionStart'
  95.                 LogName = 'Security'
  96.                 ID = 4624
  97.                 }
  98.             # Advanced Audit Policy --> Audit Logoff
  99.             @{
  100.                 Label = 'Logoff'
  101.                 EventType = 'SessionStop'
  102.                 LogName = 'Security'
  103.                 ID = 4647
  104.                 }
  105.             @{
  106.                 Label = 'Startup'
  107.                 EventType = 'SessionStop'
  108.                 LogName = 'System'
  109.                 ID = 6005
  110.                 }
  111.             # Advanced Audit Policy --> Audit Other Logon/Logoff Events  
  112.             @{
  113.                 Label = 'RdpSessionReconnect'
  114.                 EventType = 'SessionStart'
  115.                 LogName = 'Security'
  116.                 ID = 4778
  117.                 }
  118.             # Advanced Audit Policy --> Audit Other Logon/Logoff Events
  119.             @{
  120.                 Label = 'RdpSessionDisconnect'
  121.                 EventType = 'SessionStop'
  122.                 LogName = 'Security'
  123.                 ID = 4779
  124.                 }
  125.             # Advanced Audit Policy --> Audit Other Logon/Logoff Events
  126.             @{
  127.                 Label = 'Locked'
  128.                 EventType = 'SessionStop'
  129.                 LogName = 'Security'
  130.                 ID = 4800
  131.                 }
  132.             # Advanced Audit Policy --> Audit Other Logon/Logoff Events
  133.             @{
  134.                 Label = 'Unlocked'
  135.                 EventType = 'SessionStart'
  136.                 LogName = 'Security'
  137.                 ID = 4801
  138.                 }
  139.             ) # end >> $SessionEvents = @(
  140.      
  141.         $SessionStartIds = ($SessionEvents.Where({
  142.             $_.EventType -eq 'SessionStart'
  143.             })).ID
  144.         # from LD - i dunno what line the next comment really otta go with
  145.         #    it was at the end of the "$SessionStopIDs" line
  146.         # Startup ID will be used for events where the computer was powered off abruptly or crashes
  147.         #    not a great measurement
  148.         $SessionStopIds = ($SessionEvents.Where({
  149.             $_.EventType -eq 'SessionStop'
  150.             })).ID
  151.        
  152.         #endregion >> define events to indicate session start or stop
  153.  
  154.         $logNames = $SessionEvents.LogName |
  155.             Select-Object -Unique
  156.         $ids = $SessionEvents.Id
  157.  
  158.         # Build the insane XPath query for the security event log in order to query events as fast as possible
  159.         $logonXPath =
  160.             'Event[System[EventID=4624]] and ' +
  161.             "Event[EventData[Data[@Name='TargetDomainName'] != 'Window Manager']] and " +
  162.             "Event[EventData[Data[@Name='TargetDomainName'] != 'NT AUTHORITY']] and " +
  163.             "(Event[EventData[Data[@Name='LogonType'] = '2']] or " +
  164.             "Event[EventData[Data[@Name='LogonType'] = '11']])"
  165.         $otherXpath = 'Event[System[({0})]]' -f "EventID=$(($ids.
  166.            where({$_ -ne '4624' })) -join ' or EventID=')"
  167.         $xPath = '({0}) or ({1})' -f $logonXPath, $otherXpath
  168.  
  169.         } # end >> begin block
  170.  
  171.     process
  172.         {
  173.         Write-Information ('Starting to gather event log info for {0} ...' -f $ComputerName)
  174.         $events = Get-WinEvent -ComputerName $ComputerName -LogName $logNames -FilterXPath $xPath
  175.         Write-Information ('    Found [ {0} ] events to look through.' -f $events.Count)
  176.        
  177.         $events.foreach({
  178.             if ($_.Id -in $SessionStartIds)
  179.                 {
  180.                 $logonEvtId = $_.Id
  181.                 $xEvt = [xml]$_.ToXml()
  182.                 $Username = ($xEvt.Event.EventData.Data |
  183.                     Where-Object {
  184.                         $_.Name -eq 'TargetUserName'
  185.                         }).'#text'
  186.                 $LogonId = ($xEvt.Event.EventData.Data |
  187.                     Where-Object {
  188.                         $_.Name -eq 'TargetLogonId'
  189.                         }).'#text'
  190.  
  191.                 if (-not $LogonId)
  192.                     {
  193.                     $LogonId = ($xEvt.Event.EventData.Data |
  194.                         Where-Object {
  195.                             $_.Name -eq 'LogonId'
  196.                             }).'#text'
  197.                     }
  198.                 $LogonTime = $_.TimeCreated
  199.  
  200.                 Write-Information ($NewSessionStartMsg -f $logonEvtId, $Username, $LogonId, $LogonTime)
  201.  
  202.                 $SessionEndEvent = $Events.where({
  203.                     $_.TimeCreated -gt $LogonTime -and
  204.                     $_.ID -in $SessionStopIds -and
  205.                     (([xml]$_.ToXml()).Event.EventData.Data |
  206.                         Where-Object {
  207.                             $_.Name -eq 'TargetLogonId'
  208.                             }).'#text' -eq $LogonId
  209.                     }) |
  210.                     Select-Object -First 1
  211.  
  212.                 # this handles orphaned StartTimes
  213.                 #    this gives a _large_ number of orphans - currently 91 of 170 on my system
  214.                 if (-not $SessionEndEvent)
  215.                     {
  216.                     Write-Information ('    Could not find a session end event for logon ID [ {0} ].' -f $LogonId)
  217.                     Write-Information '        Using current date & time.'
  218.                     if ($IncludeOrphanLogons)
  219.                         {
  220.                         $LogoffTime = Get-Date
  221.                         }
  222.                         else
  223.                         {
  224.                         $LogoffTime = ''
  225.                         }
  226.                     }
  227.                     else
  228.                     {
  229.                     $LogoffTime = $SessionEndEvent.TimeCreated
  230.                     }
  231.  
  232.                 # if the $LogoffTime is blank, then we want to skip this one
  233.                 if (-not [string]::IsNullOrEmpty($LogoffTime))
  234.                     {
  235.                     Write-Information ('    Session stop ID is [ {0} ]' -f $SessionEndEvent.Id)
  236.                     $LogoffId = $SessionEndEvent.Id
  237.                     $SessionActiveTime = $LogoffTime - $LogonTime
  238.  
  239.                     [PSCustomObject]@{
  240.                         ComputerName = $_.MachineName
  241.                         Username = $Username
  242.                         StartTime = $LogonTime
  243.                         StartAction = $SessionEvents.where({ $_.ID -eq $logonEvtId }).Label
  244.                         StopTime = $LogoffTime
  245.                         StopAction = $SessionEvents.where({ $_.ID -eq $LogoffID }).Label
  246.                         SessionActive_Days = '{0:N2}' -f $SessionActiveTime.TotalDays
  247.                         SessionActive_Minutes = '{0}' -f ([int]$SessionActiveTime.TotalMinutes)
  248.                         }
  249.                     }
  250.                 } # end >> if ($_.Id -in $SessionStartIds)
  251.             }) # end >> $events.foreach({
  252.    
  253.     } # end >> process block
  254.  
  255.     end {}
  256.  
  257.     } # end >> function Get-LogOnOffTimeSpanInfo
  258.  
  259.  
  260. # grab your list from a text file OR via Get-ADComputer [*grin*]
  261. $ComputerList = @(
  262.     'LocalHost'
  263.     '10.0.0.1'
  264.     '127.0.0.1'
  265.     'BetterNotBeThere'
  266.     $env:COMPUTERNAME
  267.     )
  268.  
  269. $IC_Params = @{
  270.     ComputerName = $ComputerList
  271.     ScriptBlock = ${Function:Get-LogOnOffTimeSpanInfo}
  272.     # comment out the next line if you want to see the errors
  273.     ErrorAction = 'SilentlyContinue'
  274.     }
  275. # run the code on each system and only send back the filtered info
  276. $RespondingSystems = Invoke-Command @IC_Params
  277.  
  278. $NON_RespondingSystems = $ComputerList.Where({
  279.     $_ -notin $RespondingSystems.PSComputerName
  280.     })
  281.  
  282. $ReportDir = $env:TEMP
  283.  
  284. $GroupedRS = $RespondingSystems |
  285.     # strip out the unwanted properties from Invoke-Command
  286.     Select-Object -Property '*' -ExcludeProperty PSComputerName, RunspaceId, PSShowComputerName |
  287.     Group-Object -Property UserName
  288. foreach ($GRS_Item in $GroupedRS)
  289.     {
  290.     $FileName = ('{0}.csv' -f $GRS_Item.Name)
  291.     $FullFileName = Join-Path -Path $ReportDir -ChildPath $FileName
  292.     $GRS_Item.Group |
  293.         Export-Csv -LiteralPath $FullFileName -NoTypeInformation -Append
  294.     }
  295.  
  296. $NON_RespondingSystems |
  297.     Set-Content -LiteralPath (Join-Path -Path $ReportDir -ChildPath 'NON_RespondingSystems.txt')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement