EventMonitor/LogonIndicators.ps1

# ── Logon Indicators ──────────────────────────────────────────────────────────
# Functions that read Windows events indicating a user connection or activity.
# Each function queries a specific Event ID, extracts relevant properties,
# and forwards the enriched data to Application Insights via Send-LogAnalyticsConnectEvents.

<#
.SYNOPSIS
    Orchestrates all logon-indicator event readers for a given user and time window.
.DESCRIPTION
    Calls each logon event reader in sequence: OpenSSH connect, 4624, 4801, 5140, 4648.
    Any individual reader failure is caught and logged without stopping the others.
#>

function Get-UserInteractionEvents {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$sessionId,

        [Parameter(Mandatory)]
        [DateTime]$TimeBefore,

        [Parameter(Mandatory)]
        [string]$User
    )

    $commonParams = @{
        sessionId  = $sessionId
        User       = $User
        TimeBefore = $TimeBefore
    }

    Get-OpenSSHApplication_Event_Connect  @commonParams
    Get-Event_4624                        @commonParams
    Get-Event_4801_MachineUnlocked        @commonParams
    Get-Event_5140_NetworkShareAccess     @commonParams
    Get-Event_4648                        @commonParams
}

# ── OpenSSH Connect ───────────────────────────────────────────────────────────

<#
.SYNOPSIS
    Reads OpenSSH/Operational events for successful SSH public-key authentication.
.NOTES
    Requires the OpenSSH Server feature to be installed. The event log may not
    exist on machines without OpenSSH, which is handled gracefully.
#>

function Get-OpenSSHApplication_Event_Connect {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string]$sessionId,
        [Parameter(Mandatory)] [string]$User,
        [Parameter(Mandatory)] [DateTime]$TimeBefore
    )

    try {
        $events = Get-WinEvent -FilterHashtable @{ logname = $script:EventLogType.OpenSSHOperational } -ErrorAction Stop |
            Where-Object { $_.properties[1].Value -like 'Accepted publickey*' -and $_.TimeCreated -ge $TimeBefore }

        foreach ($evt in $events) {
            $message  = $evt.Message
            $parts    = $message -split '\s+'
            $userName = if ($parts.Count -ge 5) { $parts[4] -split '@' | Select-Object -First 1 } else { '' }

            if ($userName -ne $User) { continue }

            $evProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $evProps['SessionId'] = $sessionId
            $evProps['EventType'] = 'Connect'
            $evProps['UserName']  = $User
            $evProps['Process']   = if ($parts.Count -ge 1) { $parts[0] } else { '' }
            $evProps['IPAddress'] = if ($parts.Count -ge 7) { $parts[6] } else { '' }
            $evProps['IPPort']    = if ($parts.Count -ge 9) { $parts[8] } else { '' }
            $evProps['UserSID']   = "$($evt.UserId)"

            Send-LogAnalyticsConnectEvents -eventName 'OpenSSH/Operational Connect Event' `
                -Properties $evProps -sendEvent $evt
        }
    }
    catch {
        if ($_.Exception.Message -notlike '*No events were found*') {
            Write-EMLog -Message "Get-OpenSSHApplication_Event_Connect failed: $($_.Exception.Message)" -Level Error
            $errorProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $errorProps['SessionId'] = $sessionId
            $errorProps['User']      = $User
            $errorProps['Function']  = 'Get-OpenSSHApplication_Event_Connect'
            TrackException -ErrorRecord $_ -Properties $errorProps
        }
    }
}

# ── Event 4648: Explicit Credential Logon ─────────────────────────────────────

<#
.SYNOPSIS
    Reads Security event 4648 — explicit credential logon attempts via sshd.exe or svchost.exe.
#>

function Get-Event_4648 {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string]$sessionId,
        [Parameter(Mandatory)] [string]$User,
        [Parameter(Mandatory)] [DateTime]$TimeBefore
    )

    try {
        $allEvents = Get-WinEvent -FilterHashtable @{ logname = $script:EventLogType.Security; id = 4648 } -ErrorAction Stop |
            Where-Object { ($_.properties[5].Value -eq $User -or $_.properties[5].Value -like 'ssh_*') -and $_.TimeCreated -ge $TimeBefore }

        $events = $allEvents | Where-Object { $_.properties[11].Value -like '*sshd.exe' -or $_.properties[11].Value -like '*svchost.exe' }

        foreach ($evt in $events) {
            $evProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $evProps['SessionId']     = $sessionId
            $evProps['EventType']     = 'Connect'
            $evProps['AccountDomain'] = "$($evt.properties[2].Value)"
            $evProps['UserName']      = $User
            $evProps['Process']       = "$($evt.properties[11].Value)"

            Send-LogAnalyticsConnectEvents -eventName "$($evt.Id) Connect Event" `
                -Properties $evProps -sendEvent $evt
        }
    }
    catch {
        if ($_.Exception.Message -notlike '*No events were found*') {
            Write-EMLog -Message "Get-Event_4648 failed: $($_.Exception.Message)" -Level Error
            $errorProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $errorProps['SessionId'] = $sessionId
            $errorProps['User']      = $User
            $errorProps['Function']  = 'Get-Event_4648'
            TrackException -ErrorRecord $_ -Properties $errorProps
        }
    }
}

# ── Event 4624: Logon Session Created ─────────────────────────────────────────

<#
.SYNOPSIS
    Reads Security event 4624 — logon session created on destination machine (via SSH or RDP).
#>

function Get-Event_4624 {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string]$sessionId,
        [Parameter(Mandatory)] [string]$User,
        [Parameter(Mandatory)] [DateTime]$TimeBefore
    )

    try {
        $allEvents = Get-WinEvent -FilterHashtable @{ logname = $script:EventLogType.Security; id = 4624 } -ErrorAction Stop |
            Where-Object { ($_.properties[5].Value -eq $User -or $_.properties[5].Value -like 'ssh_*') -and $_.TimeCreated -ge $TimeBefore }

        $events = $allEvents | Where-Object { $_.properties[17].Value -like '*sshd.exe' -or $_.properties[17].Value -like '*svchost.exe' }

        foreach ($evt in $events) {
            $evProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $evProps['SessionId']     = $sessionId
            $evProps['EventType']     = 'Connect'
            $evProps['AccountDomain'] = "$($evt.properties[2].Value)"
            $evProps['LogonSID']      = "$($evt.properties[4].Value)"
            $evProps['UserName']      = "$($evt.properties[5].Value)"
            $evProps['LogonType']     = "$($evt.properties[8].Value)"
            $evProps['LogonProcess']  = "$($evt.properties[9].Value)"
            $evProps['Process']       = "$($evt.properties[17].Value)"

            Send-LogAnalyticsConnectEvents -eventName "$($evt.Id) Connect Event" `
                -Properties $evProps -sendEvent $evt
        }
    }
    catch {
        if ($_.Exception.Message -notlike '*No events were found*') {
            Write-EMLog -Message "Get-Event_4624 failed: $($_.Exception.Message)" -Level Error
            $errorProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $errorProps['SessionId'] = $sessionId
            $errorProps['User']      = $User
            $errorProps['Function']  = 'Get-Event_4624'
            TrackException -ErrorRecord $_ -Properties $errorProps
        }
    }
}

# ── Event 5140: Network Share Access ──────────────────────────────────────────

<#
.SYNOPSIS
    Reads Security event 5140 — network share object was accessed.
#>

function Get-Event_5140_NetworkShareAccess {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string]$sessionId,
        [Parameter(Mandatory)] [string]$User,
        [Parameter(Mandatory)] [DateTime]$TimeBefore
    )

    try {
        $events = Get-WinEvent -FilterHashtable @{ logname = $script:EventLogType.Security; id = 5140 } -ErrorAction Stop |
            Where-Object { $_.properties[1].Value -eq $User -and $_.TimeCreated -ge $TimeBefore }

        foreach ($evt in $events) {
            $evProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $evProps['SessionId']     = $sessionId
            $evProps['EventType']     = 'Connect'
            $evProps['LogonSID']      = "$($evt.properties[0].Value)"
            $evProps['UserName']      = "$($evt.properties[1].Value)"
            $evProps['AccountDomain'] = "$($evt.properties[3].Value)"
            $evProps['SourceAddress'] = "$($evt.properties[5].Value)"

            Send-LogAnalyticsConnectEvents -eventName "$($evt.Id) Connect Event" `
                -Properties $evProps -sendEvent $evt
        }
    }
    catch {
        if ($_.Exception.Message -notlike '*No events were found*') {
            Write-EMLog -Message "Get-Event_5140 failed: $($_.Exception.Message)" -Level Error
            $errorProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $errorProps['SessionId'] = $sessionId
            $errorProps['User']      = $User
            $errorProps['Function']  = 'Get-Event_5140_NetworkShareAccess'
            TrackException -ErrorRecord $_ -Properties $errorProps
        }
    }
}

# ── Event 4801: Workstation Unlocked ──────────────────────────────────────────

<#
.SYNOPSIS
    Reads Security event 4801 — workstation was unlocked.
#>

function Get-Event_4801_MachineUnlocked {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string]$sessionId,
        [Parameter(Mandatory)] [string]$User,
        [Parameter(Mandatory)] [DateTime]$TimeBefore
    )

    try {
        $events = Get-WinEvent -FilterHashtable @{ logname = $script:EventLogType.Security; id = 4801 } -ErrorAction Stop |
            Where-Object { $_.properties[1].Value -eq $User -and $_.TimeCreated -ge $TimeBefore }

        foreach ($evt in $events) {
            $evProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $evProps['SessionId']     = $sessionId
            $evProps['EventType']     = 'Connect'
            $evProps['LogonSID']      = "$($evt.properties[0].Value)"
            $evProps['UserName']      = "$($evt.properties[1].Value)"
            $evProps['AccountDomain'] = "$($evt.properties[3].Value)"

            Send-LogAnalyticsConnectEvents -eventName "$($evt.Id) Connect Event" `
                -Properties $evProps -sendEvent $evt
        }
    }
    catch {
        if ($_.Exception.Message -notlike '*No events were found*') {
            Write-EMLog -Message "Get-Event_4801 failed: $($_.Exception.Message)" -Level Error
            $errorProps = [System.Collections.Generic.Dictionary[string, string]]::new()
            $errorProps['SessionId'] = $sessionId
            $errorProps['User']      = $User
            $errorProps['Function']  = 'Get-Event_4801_MachineUnlocked'
            TrackException -ErrorRecord $_ -Properties $errorProps
        }
    }
}