Functions/SystemAudit/Get-SystemAuditUserSession.ps1

<#
    .SYNOPSIS
        Get all audit events around the user sessions on the local system.
 
    .DESCRIPTION
        This function will show all user setting audit events by parsing the
        Security event log:
        - 4624: An account was successfully logged on
        - 4625: An account failed to log on
        - 4634: An account was logged off
        - 4647: User initiated logoff
 
    .INPUTS
        None.
 
    .OUTPUTS
        SecurityFever.SystemAudit.Event.
 
    .EXAMPLE
        PS C:\> Get-SystemAuditUserSession
        Get the local user session system audit events.
 
    .LINK
        https://github.com/claudiospizzi/SecurityFever
#>

function Get-SystemAuditUserSession
{
    [CmdletBinding()]
    param
    (
        # Period of days to cover.
        [Parameter(Mandatory = $false)]
        [System.Int32]
        $DayPeriod = 7,

        # Show extended events, not only the important ones.
        [Parameter(Mandatory = $false)]
        [Switch]
        $Extended,

        # Hide the warning messages, don't test the prerequisites.
        [Parameter(Mandatory = $false)]
        [Switch]
        $HideWarning
    )

    Test-AdministratorRole -Throw

    Show-SystemAuditEventLogWarning -LogName 'Security' -DayPeriod $DayPeriod -HideWarning:$HideWarning.IsPresent
    Show-SystemAuditPolicyWarning -Category 'Logon/Logoff' -Subcategory 'Logon' -Setting 'Success' -HideWarning:$HideWarning.IsPresent
    Show-SystemAuditPolicyWarning -Category 'Logon/Logoff' -Subcategory 'Logon' -Setting 'Failure' -HideWarning:$HideWarning.IsPresent
    Show-SystemAuditPolicyWarning -Category 'Logon/Logoff' -Subcategory 'Logoff' -Setting 'Success' -HideWarning:$HideWarning.IsPresent

    $configEventLog = Get-Content -Path "$Script:ConfigurationPath\SystemAudit.EventLog.json" | ConvertFrom-Json

    # Get all relevant event log records for user session events
    $records = Get-WinEventAdvanced -LogName 'Security' -EventId 4624, 4625, 4637, 4647 -ProviderName 'Microsoft-Windows-Security-Auditing' -DayPeriod $DayPeriod

    foreach ($record in $records)
    {
        $recordId = $record.Id

        # Skip records if not extended (part 1)
        if (-not $Extended.IsPresent -and $recordId -eq 4647)
        {
            continue
        }

        $auditEvent = [PSCustomObject] @{
            PSTypeName  = 'SecurityFever.SystemAudit.Event'
            Timestamp   = $record.TimeCreated
            Machine     = $record.MachineName
            User        = Get-WinEventRecordUser -Record $record
            Component   = 'User Session'
            Action      = $configEventLog.Events.Security.$recordId.Action
            Context     = ''
            Detail      = ''
            SourcePath  = '/EventLog/Security/Record[@Id={0}]' -f $recordId
            SourceEvent = $record
        }

        # Get record properties
        $recordProperties = Get-WinEventRecordProperty -Record $record -PropertyName $configEventLog.Events.Security.$recordId.Properties

        # Extract the subject to logon
        if ($recordProperties.PSObject.Properties.Name -contains 'TargetUserName' -and
            $recordProperties.TargetUserName -ne '-')
        {
            $auditEvent.User = ('{0}\{1}' -f $recordProperties.TargetDomainName, $recordProperties.TargetUserName).Trim('\-')
        }

        # Extract the logon type
        if ($recordProperties.PSObject.Properties.Name -contains 'LogonType' -and
            $recordProperties.LogonType -ne '-')
        {
            $logonType = $recordProperties.LogonType

            if ($configEventLog.Enumerations.LogonType.PSObject.Properties.Name -contains $logonType)
            {
                $auditEvent.Context = $configEventLog.Enumerations.LogonType.$logonType
            }
        }

        # Extract the logon requester
        if ($recordProperties.PSObject.Properties.Name -contains 'SubjectUserName' -and
            $recordProperties.SubjectUserName -ne '-')
        {
            $auditEvent.Detail += 'Requester: {0}, ' -f ('{0}\{1}' -f $recordProperties.SubjectDomainName, $recordProperties.SubjectUserName).Trim('\-')
        }

        # Extract the failure code message
        if ($recordProperties.PSObject.Properties.Name -contains 'Status')
        {
            $failureCode = '0x{0:X8}' -f $recordProperties.Status

            if ($configEventLog.Enumerations.FailureCode.PSObject.Properties.Name -contains $failureCode)
            {
                $auditEvent.Detail += 'Status: {0}, ' -f $configEventLog.Enumerations.FailureCode.$failureCode
            }
        }
        if ($recordProperties.PSObject.Properties.Name -contains 'SubStatus')
        {
            $failureCode = '0x{0:X8}' -f $recordProperties.SubStatus

            if ($configEventLog.Enumerations.FailureCode.PSObject.Properties.Name -contains $failureCode)
            {
                $auditEvent.Detail += 'SubStatus: {0}, ' -f $configEventLog.Enumerations.FailureCode.$failureCode
            }
        }

        # Fix the detail string
        $auditEvent.Detail += 'Auth: {0}, ' -f $recordProperties.AuthenticationPackageName

        # Extract the source process
        if ($recordProperties.PSObject.Properties.Name -contains 'ProcessName' -and
            $recordProperties.ProcessName -ne '-')
        {
            $auditEvent.Detail += 'Process: {0}, ' -f $recordProperties.ProcessName
        }

        # Extract the source pc name
        if ($recordProperties.PSObject.Properties.Name -contains 'WorkstationName' -and
            $recordProperties.WorkstationName -ne '-')
        {
            $auditEvent.Detail += 'Source PC: {0}, ' -f $recordProperties.WorkstationName
        }

        # Extract the source ip address
        if ($recordProperties.PSObject.Properties.Name -contains 'IpAddress' -and
            $recordProperties.IpAddress -ne '-')
        {
            $auditEvent.Detail += 'Source IP: {0}:{1}, ' -f $recordProperties.IpAddress, $recordProperties.IpPort
        }

        # Skip records if not extended (part 2)
        if (-not $Extended.IsPresent -and $recordId -eq 4624 -and $auditEvent.Context -notin 'Interactive', 'RemoteInteractive')
        {
            continue
        }

        # Optimize object
        $auditEvent.Detail = $auditEvent.Detail.TrimEnd(', ')

        Write-Output $auditEvent
    }
}