Scripts/Get-EventLogSummary.ps1
|
<#
.SYNOPSIS Collects and summarises recent errors and warnings from Windows Event Logs for IT support diagnostics. .DESCRIPTION Queries the Application and System event logs for recent Error and Warning level events, groups them by source to highlight recurring patterns, and flags specific Event IDs that commonly correspond to documented playbook scenarios (application crashes, disk faults, network adapter failures). Supports diagnostic Step 3 in playbooks/application-not-launching.md and general incident evidence gathering per incidents/incident-response-checklist.md. This script is read-only. It makes no changes to the system. Standard users can read the Application and System logs by default on most Windows configurations; if access is denied, the script reports this clearly rather than failing silently. .NOTES Author: it-support-ops repository Run as: Standard user (no elevation required on most configurations) Compatibility: Windows 10, Windows 11, PowerShell 5.1+ Output: Console output + log file in user's Documents folder .EXAMPLE .\Get-EventLogSummary.ps1 Collects errors and warnings from the last 24 hours and saves output to: $env:USERPROFILE\Documents\IT-Diagnostics\EventLogSummary_<timestamp>.txt .EXAMPLE .\Get-EventLogSummary.ps1 -HoursBack 72 -FilterSource "Application Error" Collects events from the last 72 hours, filtered to sources matching "Application Error". #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Interactive console tool by design: report content goes through Write-ReportLine (pipe-safe); Write-Host is used only for colored status messages meant for a human reading the console.')] [CmdletBinding()] param( # Optional custom output path. Defaults to Documents\IT-Diagnostics. [Parameter(Mandatory = $false)] [string]$OutputPath, # How many hours back to search. Default of 24 covers "it happened # earlier today" tickets without producing an overwhelming volume of # historical noise for a standard triage pass. [Parameter(Mandatory = $false)] [ValidateRange(1, 720)] [int]$HoursBack = 24, # Optional filter to narrow results to a specific event source # (e.g. "Application Error", "Disk", "e1dexpress" for a NIC driver). # Useful when following up on a specific symptom rather than doing # a general sweep. [Parameter(Mandatory = $false)] [string]$FilterSource, # Maximum number of events to display per log, to keep the report # readable. The full count is still reported even if truncated. [Parameter(Mandatory = $false)] [ValidateRange(5, 200)] [int]$MaxEventsPerLog = 30 ) # --------------------------------------------------------------------------- # SETUP # --------------------------------------------------------------------------- $ErrorActionPreference = "Continue" $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" $startTime = (Get-Date).AddHours(-$HoursBack) if (-not $OutputPath) { $diagnosticsFolder = Join-Path $env:USERPROFILE "Documents\IT-Diagnostics" if (-not (Test-Path $diagnosticsFolder)) { try { New-Item -ItemType Directory -Path $diagnosticsFolder -Force | Out-Null } catch { Write-Warning "Could not create diagnostics folder. Output will be console-only." $diagnosticsFolder = $null } } if ($diagnosticsFolder) { $OutputPath = Join-Path $diagnosticsFolder "EventLogSummary_$timestamp.txt" } } $reportLines = New-Object System.Collections.Generic.List[string] function Write-ReportLine { param([string]$Text = "") Write-Host $Text $reportLines.Add($Text) } function Write-SectionHeader { param([string]$Title) Write-ReportLine "" Write-ReportLine "=" * 70 Write-ReportLine " $Title" Write-ReportLine "=" * 70 } # Event IDs with known, documented significance in this repository's # playbooks. Surfacing these explicitly saves the technician from having # to recognise them from memory and points directly to the relevant # troubleshooting document. $knownEventIds = @{ 1000 = "Application Error (crash) - see playbooks/application-not-launching.md Step 3" 1001 = "Windows Error Reporting - additional crash detail may be available" 1002 = "Application Hang - see playbooks/application-not-launching.md Step 2" 6008 = "Unexpected shutdown (previous session did not close cleanly)" 41 = "Kernel-Power: system rebooted without a clean shutdown - possible power/hardware issue" 7000 = "Service failed to start - check the named service status" 7001 = "Service dependency failure" 5719 = "Domain trust/logon issue - see playbooks/user-cannot-login.md Step 5" 4740 = "Account lockout event - see playbooks/user-cannot-login.md Step 3 (requires DC access for full detail)" } # --------------------------------------------------------------------------- # REPORT HEADER # --------------------------------------------------------------------------- Write-ReportLine "=" * 70 Write-ReportLine " IT SUPPORT - EVENT LOG SUMMARY" Write-ReportLine "=" * 70 Write-ReportLine "Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Write-ReportLine "Computer Name: $env:COMPUTERNAME" Write-ReportLine "Current User: $env:USERNAME" Write-ReportLine "Time Window: Last $HoursBack hour(s) (since $($startTime.ToString('yyyy-MM-dd HH:mm:ss')))" if ($FilterSource) { Write-ReportLine "Source Filter: $FilterSource" } # --------------------------------------------------------------------------- # FUNCTION: Process a single log # --------------------------------------------------------------------------- function Get-LogSummary { param( [string]$LogName ) Write-SectionHeader "$LogName LOG - ERRORS AND WARNINGS" try { $filterHash = @{ LogName = $LogName Level = 2, 3 # 2 = Error, 3 = Warning StartTime = $startTime } $events = Get-WinEvent -FilterHashtable $filterHash -ErrorAction Stop if ($FilterSource) { $events = $events | Where-Object { $_.ProviderName -like "*$FilterSource*" } } if (-not $events -or $events.Count -eq 0) { Write-ReportLine "No Error or Warning events found in this window." return } $totalCount = $events.Count Write-ReportLine "Total Error/Warning events in window: $totalCount" Write-ReportLine "" # Group by source to highlight recurring patterns — a single source # generating dozens of events is a stronger diagnostic signal than # a long flat list of unique one-off events. Write-ReportLine "Events grouped by source (most frequent first):" $events | Group-Object -Property ProviderName | Sort-Object Count -Descending | Select-Object -First 10 | ForEach-Object { Write-ReportLine (" {0,-40} {1} event(s)" -f $_.Name, $_.Count) } # Flag any known significant Event IDs found in this window Write-ReportLine "" Write-ReportLine "Known significant Event IDs found in this window:" $foundKnown = $false foreach ($id in $knownEventIds.Keys) { $matchez = $events | Where-Object { $_.Id -eq $id } if ($matchez) { $foundKnown = $true Write-ReportLine (" ID {0}: {1} occurrence(s) - {2}" -f $id, $matchez.Count, $knownEventIds[$id]) } } if (-not $foundKnown) { Write-ReportLine " None of the documented significant Event IDs were found." } # Detailed listing, capped to keep the report readable Write-ReportLine "" $displayCount = [math]::Min($totalCount, $MaxEventsPerLog) Write-ReportLine "Detailed listing (most recent $displayCount of $totalCount):" Write-ReportLine ("-" * 70) $events | Select-Object -First $MaxEventsPerLog | ForEach-Object { $levelLabel = if ($_.LevelDisplayName) { $_.LevelDisplayName } else { "Unknown" } Write-ReportLine "" Write-ReportLine ("[{0}] {1} - {2}" -f $_.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss'), ` $levelLabel, $_.ProviderName) Write-ReportLine (" Event ID: {0}" -f $_.Id) # Truncate long messages for readability in the summary report — # the full event remains available via Event Viewer using the # timestamp and Event ID recorded here if deeper detail is needed. $message = $_.Message if ($message) { $shortMessage = if ($message.Length -gt 200) { $message.Substring(0, 200) + "... (truncated)" } else { $message } $shortMessage = $shortMessage -replace "`r`n", " " -replace "`n", " " Write-ReportLine (" Message: {0}" -f $shortMessage) } } if ($totalCount -gt $MaxEventsPerLog) { Write-ReportLine "" Write-ReportLine "($($totalCount - $MaxEventsPerLog) additional event(s) not shown — increase" Write-ReportLine " -MaxEventsPerLog or review the full log in Event Viewer for complete detail.)" } } catch [System.Exception] { if ($_.Exception.Message -like "*No events were found*") { Write-ReportLine "No Error or Warning events found in this window." } else { Write-ReportLine "ERROR: Could not query $LogName log - $($_.Exception.Message)" Write-ReportLine "This may require administrator privileges depending on local" Write-ReportLine "Group Policy configuration of event log access." } } } # --------------------------------------------------------------------------- # RUN AGAINST BOTH LOGS # --------------------------------------------------------------------------- Get-LogSummary -LogName "Application" Get-LogSummary -LogName "System" # --------------------------------------------------------------------------- # UNEXPECTED SHUTDOWN CHECK # --------------------------------------------------------------------------- # Specifically surfaces unclean shutdowns (Event ID 41 and 6008), since # these are a common root cause of profile corruption and application # config corruption documented elsewhere in the repository, and are easy # to miss in a general scan if not called out directly. Write-SectionHeader "UNEXPECTED SHUTDOWN CHECK" try { $shutdownEvents = Get-WinEvent -FilterHashtable @{ LogName = "System" Id = 41, 6008 StartTime = $startTime } -ErrorAction Stop if ($shutdownEvents) { Write-ReportLine "FLAG: $($shutdownEvents.Count) unexpected shutdown event(s) found in this window." foreach ($evt in $shutdownEvents | Select-Object -First 5) { Write-ReportLine (" [{0}] Event ID {1}" -f $evt.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss'), $evt.Id) } Write-ReportLine "" Write-ReportLine "Unexpected shutdowns are a common cause of profile or application" Write-ReportLine "configuration corruption. See playbooks/user-cannot-login.md Step 6" Write-ReportLine "and playbooks/application-not-launching.md Step 6 if related symptoms" Write-ReportLine "are also present." } else { Write-ReportLine "No unexpected shutdown events found in this window." } } catch [System.Exception] { if ($_.Exception.Message -like "*No events were found*") { Write-ReportLine "No unexpected shutdown events found in this window." } else { Write-ReportLine "Could not check for unexpected shutdown events - $($_.Exception.Message)" } } # --------------------------------------------------------------------------- # FOOTER AND FILE OUTPUT # --------------------------------------------------------------------------- Write-SectionHeader "REPORT COMPLETE" Write-ReportLine "Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Write-ReportLine "" Write-ReportLine "Reminder: This report is a triage starting point. For full event detail," Write-ReportLine "use Event Viewer with the timestamps and Event IDs noted above." if ($OutputPath) { try { $reportLines | Out-File -FilePath $OutputPath -Encoding UTF8 -ErrorAction Stop Write-Host "" Write-Host "Report saved to: $OutputPath" -ForegroundColor Green Write-Host "Attach this file to the ticket per diagnostic-report-template.md" -ForegroundColor Green } catch { Write-Warning "Could not save report to file: $($_.Exception.Message)" Write-Warning "Report was displayed in console only — copy manually if needed." } } else { Write-Host "" Write-Host "No output file path available — report displayed in console only." -ForegroundColor Yellow } |