# Windows 10 Event Log Triage Script

> Purpose: Quickly collect and review high-value Windows 10 event log evidence for incident detection and validation.
>
> Safe by default: this script only reads event logs and exports results. It does not modify system settings.

## What this script does

- Queries common security-relevant Windows logs
- Filters by a time window you specify
- Highlights events related to:
  - Logon activity
  - Process creation
  - Account changes
  - Service creation or change
  - Scheduled task creation or modification
  - Audit and policy changes
  - PowerShell and Defender activity where available
- Exports findings to CSV files for review
- Produces a concise summary on screen

## How to use

1. Save this file as `windows-10-event-log-triage.ps1`
2. Open PowerShell as a standard user or administrator, depending on the logs you can access
3. Run the script with a time window that matches the incident

### Example

```powershell
.\windows-10-event-log-triage.ps1 -StartTime (Get-Date).AddHours(-24) -EndTime (Get-Date) -OutputDirectory .\triage-output
```

## Script

```powershell
[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [datetime]$StartTime = (Get-Date).AddHours(-24),

    [Parameter(Mandatory = $false)]
    [datetime]$EndTime = (Get-Date),

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$OutputDirectory = (Join-Path -Path $PSScriptRoot -ChildPath 'triage-output'),

    [Parameter(Mandatory = $false)]
    [switch]$IncludePowerShellLogs,

    [Parameter(Mandatory = $false)]
    [switch]$IncludeDefenderLogs
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-EventLogExists {
    param(
        [Parameter(Mandatory = $true)]
        [string]$LogName
    )

    try {
        $null = Get-WinEvent -ListLog $LogName -ErrorAction Stop
        return $true
    }
    catch {
        return $false
    }
}

function Get-EventsByFilter {
    param(
        [Parameter(Mandatory = $true)]
        [string]$LogName,

        [Parameter(Mandatory = $false)]
        [int[]]$EventId,

        [Parameter(Mandatory = $true)]
        [datetime]$StartTime,

        [Parameter(Mandatory = $true)]
        [datetime]$EndTime
    )

    $filter = @{
        LogName   = $LogName
        StartTime = $StartTime
        EndTime   = $EndTime
    }

    if ($EventId) {
        $filter.EventId = $EventId
    }

    try {
        Get-WinEvent -FilterHashtable $filter -ErrorAction Stop | ForEach-Object {
            [pscustomobject]@{
                TimeCreated = $_.TimeCreated
                LogName     = $_.LogName
                Id          = $_.Id
                Level       = $_.LevelDisplayName
                Provider    = $_.ProviderName
                MachineName  = $_.MachineName
                Message     = ($_.Message -replace '\s+', ' ').Trim()
            }
        }
    }
    catch {
        Write-Warning "Unable to query log '$LogName': $($_.Exception.Message)"
    }
}

function Export-Results {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [object[]]$Data
    )

    if ($null -eq $Data -or $Data.Count -eq 0) {
        return
    }

    $Data | Export-Csv -Path $Path -NoTypeInformation -Encoding UTF8
}

Write-Host "Windows 10 Event Log Triage" -ForegroundColor Cyan
Write-Host "StartTime: $StartTime"
Write-Host "EndTime  : $EndTime"
Write-Host "Output   : $OutputDirectory"

if ($StartTime -ge $EndTime) {
    throw 'StartTime must be earlier than EndTime.'
}

if (-not (Test-Path -Path $OutputDirectory)) {
    New-Item -Path $OutputDirectory -ItemType Directory -Force | Out-Null
}

$results = [ordered]@{}

# Security log: authentication, process creation, account activity, service/task-related events
if (Test-EventLogExists -LogName 'Security') {
    $results.Security = @()
    $results.Security += Get-EventsByFilter -LogName 'Security' -StartTime $StartTime -EndTime $EndTime -EventId @(4624, 4625, 4634, 4648, 4672)
    $results.Security += Get-EventsByFilter -LogName 'Security' -StartTime $StartTime -EndTime $EndTime -EventId @(4688, 4697, 4698, 4699, 4702)
    $results.Security += Get-EventsByFilter -LogName 'Security' -StartTime $StartTime -EndTime $EndTime -EventId @(4720, 4722, 4723, 4724, 4728, 4732, 4738)
    $results.Security += Get-EventsByFilter -LogName 'Security' -StartTime $StartTime -EndTime $EndTime -EventId @(4719, 4739, 1102)
}
else {
    Write-Warning 'Security log not available.'
}

# System log: services, drivers, shutdowns, general anomalies
if (Test-EventLogExists -LogName 'System') {
    $results.System = @()
    $results.System += Get-EventsByFilter -LogName 'System' -StartTime $StartTime -EndTime $EndTime -EventId @(7040, 7045, 7036, 6005, 6006, 1074)
}
else {
    Write-Warning 'System log not available.'
}

# Optional PowerShell logs
if ($IncludePowerShellLogs) {
    $psLogs = @(
        'Microsoft-Windows-PowerShell/Operational',
        'Windows PowerShell'
    )

    foreach ($log in $psLogs) {
        if (Test-EventLogExists -LogName $log) {
            if (-not $results.Contains('PowerShell')) {
                $results.PowerShell = @()
            }
            $results.PowerShell += Get-EventsByFilter -LogName $log -StartTime $StartTime -EndTime $EndTime -EventId @(4103, 4104, 4105, 4106, 400, 403, 600)
        }
    }
}

# Optional Defender logs
if ($IncludeDefenderLogs) {
    $defenderLogs = @(
        'Microsoft-Windows-Windows Defender/Operational'
    )

    foreach ($log in $defenderLogs) {
        if (Test-EventLogExists -LogName $log) {
            if (-not $results.Contains('Defender')) {
                $results.Defender = @()
            }
            $results.Defender += Get-EventsByFilter -LogName $log -StartTime $StartTime -EndTime $EndTime -EventId @(1116, 1117, 5001, 5007)
        }
    }
}

# Write exports and summary
foreach ($key in $results.Keys) {
    $data = $results[$key]
    $fileName = '{0}-{1}.csv' -f $key.ToLower(), (Get-Date -Format 'yyyyMMdd-HHmmss')
    $path = Join-Path -Path $OutputDirectory -ChildPath $fileName
    Export-Results -Path $path -Data $data

    $count = if ($null -eq $data) { 0 } else { $data.Count }
    Write-Host "[$key] Events found: $count"
    if ($count -gt 0) {
        Write-Host "  Exported to: $path"
    }
}

# Compact on-screen summary for quick triage
Write-Host ''
Write-Host 'Triage notes:' -ForegroundColor Yellow
Write-Host '- Look for event sequences, not isolated IDs.'
Write-Host '- Validate logon type, account, source host, and timing.'
Write-Host '- Correlate process creation with service or scheduled task changes.'
Write-Host '- Investigate audit or policy changes near suspicious activity.'
Write-Host '- Treat gaps in logs as a finding if retention or logging may have been altered.'

Write-Host ''
Write-Host 'Completed.' -ForegroundColor Green
```

## Review guidance

Use the exported CSVs to build a timeline:

- Start with the first suspicious logon or process event
- Check whether the source, account, and time match normal administration
- Look for persistence indicators such as services or scheduled tasks
- Verify whether audit settings or log retention changed around the same time
- Compare findings with endpoint, identity, and network telemetry before escalating

## Notes

- This script assumes the host has relevant auditing enabled.
- If process creation or PowerShell logging was not configured, visibility will be limited.
- A lack of events can be informative if logging was expected but absent.
- Always confirm suspicious activity against the approved operational baseline before acting.