Site icon KrookedSkull

PowerShell – Building Session Durations from Event Logs

Most of this script comes from AdamTheAutomator’s website, but I did make some improvements for my own needs that included a custom class and some adjustments to the PowerShell object. I have plans to modify this further so I can use it for larger automation activities.

class eventLogObject{
    [string]$AccountName
    [string]$DomainName
    [string]$LogonID
    [string]$SessionName
    [string]$ClientName
    [string]$ClientAddress
    [string]$EventID
    [string]$EventDescription
    [string]$TimeStamp
}

function Get-ActivityLog {
    <#	
    .SYNOPSIS
        This script finds all PowerShell last logon, logoff and total active session times of all users on all computers specified. For this script
        to function as expected, the advanced AD policies; Audit Logon, Audit Logoff and Audit Other Logon/Logoff Events must be
        enabled and targeted to the appropriate computers via GPO or local policy.
    .EXAMPLE
        
    .PARAMETER ComputerName
        An array of computer names to search for events on. If this is not provided, the script will search the local computer.
    .INPUTS
        None. You cannot pipe objects to Get-ActiveDirectoryUserActivity.ps1.
    .OUTPUTS
        None. If successful, this script does not output anything.
    #>
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerName = $Env:COMPUTERNAME
    )
    
    try {
        
        #region Defie all of the events to indicate session start or top
        $sessionEvents = @(
            @{ 'Label' = 'Logon'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4624 } ## Advanced Audit Policy --> Audit Logon
            @{ 'Label' = 'Logoff'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4647 } ## Advanced Audit Policy --> Audit Logoff
            @{ 'Label' = 'Startup'; 'EventType' = 'SessionStop'; 'LogName' = 'System'; 'ID' = 6005 }
            @{ 'Label' = 'RdpSessionReconnect'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4778 } ## Advanced Audit Policy --> Audit Other Logon/Logoff Events
            @{ 'Label' = 'RdpSessionDisconnect'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4779 } ## Advanced Audit Policy --> Audit Other Logon/Logoff Events
            @{ 'Label' = 'Locked'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4800 } ## Advanced Audit Policy --> Audit Other Logon/Logoff Events
            @{ 'Label' = 'Unlocked'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4801 } ## Advanced Audit Policy --> Audit Other Logon/Logoff Events
        )
        
        ## All of the IDs that designate when user activity starts
        $sessionStartIds = ($sessionEvents | where { $_.EventType -eq 'SessionStart' }).ID
        ## All of the IDs that designate when user activity stops
        $sessionStopIds = ($sessionEvents | where { $_.EventType -eq 'SessionStop' }).ID
        #endregion
        
        ## Define all of the log names we'll be querying
        $logNames = ($sessionEvents.LogName | select -Unique)
        ## Grab all of the interesting IDs we'll be looking for
        $ids = $sessionEvents.Id
            
        ## Build the insane XPath query for the security event log in order to query PowerShell last logon events and others as fast as possible
        $logonXPath = "Event[System[EventID=4624]] and Event[EventData[Data[@Name='TargetDomainName'] != 'Window Manager']] and Event[EventData[Data[@Name='TargetDomainName'] != 'NT AUTHORITY']] and (Event[EventData[Data[@Name='LogonType'] = '2']] or Event[EventData[Data[@Name='LogonType'] = '10']])"
        $otherXpath = 'Event[System[({0})]]' -f "EventID=$(($ids.where({ $_ -ne '4624' })) -join ' or EventID=')"
        $xPath = '({0}) or ({1})' -f $logonXPath, $otherXpath
    
        foreach ($computer in $ComputerName) {
            ## Query each computer's event logs using the Xpath filter
            $events = Get-WinEvent -ComputerName $computer -LogName $logNames -FilterXPath $xPath
            Write-Verbose -Message "Found [$($events.Count)] events to look through"
    
            ## Set up the output object
            $output = [ordered]@{
                'ComputerName'          = $computer
                'Username'              = $null
                'StartTime'             = $null
                'StartAction'           = $null
                'StopTime'              = $null
                'StopAction'            = $null
                'Session Active (Days)' = $null
                'Session Active (Min)'  = $null
            }
            
            ## Need current users because if no stop time, they're still probably logged in
            $getGimInstanceParams = @{
                ClassName = 'Win32_ComputerSystem'
            }
            if ($computer -ne $Env:COMPUTERNAME) {
                $getGimInstanceParams.ComputerName = $computer
            }
            $loggedInUsers = Get-CimInstance @getGimInstanceParams | Select-Object -ExpandProperty UserName | foreach { $_.split('\')[1] }
            
            $eventLogObject = New-Object System.Collections.ArrayList($null)
            Foreach ($event in $events) {
                $xmlEvent = [xml]$event.ToXml()
                #$xmlevent.event.eventdata.data
                If (($event.ID -eq '4778') -or ($event.ID -eq '4779')) {
                    $eventObject = New-Object eventLogObject
                    $eventObject.AccountName = ($xmlevent.event.eventdata.data | where { $_.Name -eq "AccountName"}).'#text'
                    $eventObject.DomainName = ($xmlevent.event.eventdata.data | where { $_.Name -eq "AccountDomain"}).'#text'
                    $eventObject.LogonID = ($xmlevent.event.eventdata.data | where { $_.Name -eq "LogonID"}).'#text'
                    $eventObject.SessionName = ($xmlevent.event.eventdata.data | where { $_.Name -eq "SessionName"}).'#text'
                    $eventObject.ClientName = ($xmlevent.event.eventdata.data | where { $_.Name -eq "ClientName"}).'#text'
                    $eventObject.ClientAddress = ($xmlevent.event.eventdata.data | where { $_.Name -eq "ClientAddress"}).'#text'
                    $eventObject.EventID = $event.ID
                    $eventObject.EventDescription = $sessionEvents.Where({ $_.ID -eq $event.ID}).Label
                    $eventObject.TimeStamp = $event.TimeCreated
                    $eventLogObject.Add($eventObject)
                } else {
                    $eventObject = New-Object eventLogObject
                    $eventObject.AccountName = ($xmlevent.event.eventdata.data | where { $_.Name -eq "TargetUserName"}).'#text'
                    $eventObject.DomainName = ($xmlevent.event.eventdata.data | where { $_.Name -eq "TargetDomainName"}).'#text'
                    $eventObject.LogonID = ($xmlevent.event.eventdata.data | where { $_.Name -eq "TargetLogonId"}).'#text'
                    $eventObject.SessionName = ($xmlevent.event.eventdata.data | where { $_.Name -eq "SessionId"}).'#text'
                    $eventObject.ClientName = 'na'
                    $eventObject.ClientAddress = 'na'
                    $eventObject.EventID = $event.ID
                    $eventObject.EventDescription = $sessionEvents.Where({ $_.ID -eq $event.ID}).Label
                    $eventLogObject.Add($eventObject)
                    $eventObject.TimeStamp = $event.TimeCreated
                }
            }
           #return $eventLogObject
        }
    } catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
    }
    
    Get-ActivityLog | Format-Table
Exit mobile version