11<#
22. SYNOPSIS
3- This script retrieves and processes error events from the Windows Event Log within the last 48 and 12 hours .
3+ Monitors Windows 'System' event logs and reports error events with configurable thresholds .
44
55. DESCRIPTION
6- This script is useful for monitoring and alerting on error events in the Windows Event Log.
7- The script processes error logs from the 'System' log only, only critical errors are counted and displayed.
8-
9- 1. Retrieves the last 20 error events from the 'System' log in the last 48 hours, excluding specified event IDs.
10- 2. Counts and displays the number of error events found in the last 48 hours (after filtering out ignored events).
11- 3. Retrieves error events from the last 12 hours and checks if there are 4 or more errors.
12- 4. If 4 or more errors are found in the last 12 hours, the script exits with an error code (1).
13- 5. If fewer than 4 errors are found, the script exits with a success code (0).
14-
15- .EXEMPLE
16- debug=true
6+ This script retrieves error events from the Windows 'System' log over a configurable lookback period
7+ (default 48 hours) and evaluates them within a configurable evaluation window
8+ (default 12 hours). It filters out events by specified Event IDs and keywords.
9+
10+ The script supports three severity thresholds (set unrealistic threshold to disable):
11+ - INFO: default 1 event
12+ - WARN: default 2 events
13+ - ERROR: default 4 events
14+
15+ Behavior:
16+ 1. Retrieves error events (Level=2) from the 'System' log within the lookback period.
17+ 2. Filters out ignored Event IDs and keywords (defaults or via environment variables).
18+ 3. Evaluates events within the evaluation window:
19+ - If count >= ERROR threshold, exits with ERROR exit code (3).
20+ - If count >= WARN threshold, exits with WARN exit code (2).
21+ - Otherwise, exits with INFO exit code (1).
22+ 4. Debug mode outputs filtered events and thresholds.
23+
24+ . EXAMPLE
25+ DEBUG=true
1726 FILTER_ID=1111,22222,3333
1827 FILTER_KEYWORD=keyword1,keyword2
28+ INFO_THRESHOLD=1
29+ WARN_THRESHOLD=2
30+ ERROR_THRESHOLD=4
31+ LOOKBACK_HOURS=72
32+ EVALUATION_WINDOW_HOURS=24
1933
2034. NOTES
2135 Author: SAN
2236 Date: 24.10.2024
23- #public
24-
25- 10016 safe to ignore
26- https://learn.microsoft.com/en-us/troubleshoot/windows-client/application-management/event-10016-logged-when-accessing-dcom
27- 36874 to ignore
28- Fixing the issue would be more dangerous than leaving it be it would require blocking tls 1.2 and forcing 1.1 with unsafe cyphers and loosing connection to devices that do not support 1.1
37+ #PUBLIC
38+ Default ignored Event IDs:
39+ 10016 - safe to ignore, see:
40+ https://learn.microsoft.com/en-us/troubleshoot/windows-client/application-management/event-10016-logged-when-accessing-dcom
41+ 36874 - ignored due to TLS/connection constraints
2942
3043.CHANGELOG
31- 04.12.24 SAN added id to ignore in comma separeted variable
32- 12.12.24 SAN adding keyword filters, added filter addition via env var
33-
34- .TODO
35- Set 20 Error Events and 48 hours in vars same for 4 and 12
36- Re-thing the thresholds to add info warn error limits
44+ 04.12.24 SAN: Added environment variable support for ignored Event IDs.
45+ 12.12.24 SAN: Added keyword filters and support for dynamic filter addition via environment variables.
46+ 02.04.26 SAN: Added configurable info/warn/error thresholds with exit codes.
3747
3848#>
3949
40- $defaultEventIds = @ (10016 , 36874 )
41- $defaultKeywords = @ (" gupdate" , " anotherkeyword" )
50+ $defaultEventIds = @ (10016 , 36874 )
51+ $defaultKeywords = @ (" gupdate" , " anotherkeyword" )
4252
43- $debug = [ System.Environment ]::GetEnvironmentVariable( " DEBUG " )
44- $filterIdEnv = [ System.Environment ]::GetEnvironmentVariable( " FILTER_ID " )
45- $filterKeywordEnv = [ System.Environment ]::GetEnvironmentVariable( " FILTER_KEYWORD " )
53+ $defaultInfoThreshold = 1
54+ $defaultWarnThreshold = 2
55+ $defaultErrorThreshold = 4
4656
47- $ignoredEventIds = if ($filterIdEnv ) {
48- $filterIdEnv.Split (" ," ) + $defaultEventIds
49- } else {
50- $defaultEventIds
51- }
57+ $defaultLookbackHours = 48
58+ $defaultEvaluationWindowHours = 12
5259
53- $ignoredKeywords = if ($filterKeywordEnv ) {
54- $filterKeywordEnv.Split (" ," ) + $defaultKeywords
55- } else {
56- $defaultKeywords
57- }
60+ $infoExitCode = 1
61+ $warnExitCode = 2
62+ $errorExitCode = 3
5863
59- $start48h = (Get-Date ).AddHours(-48 )
60- $start12h = (Get-Date ).AddHours(-12 )
64+ $debug = $env: DEBUG
65+ $filterIdEnv = $env: FILTER_ID
66+ $filterKeywordEnv = $env: FILTER_KEYWORD
6167
62- $allErrors48h = Get-WinEvent - FilterHashtable @ {LogName = ' System' ; Level = 2 ; StartTime = $start48h } - ErrorAction SilentlyContinue
68+ $infoThresholdEnv = $env: INFO_THRESHOLD
69+ $warnThresholdEnv = $env: WARN_THRESHOLD
70+ $errorThresholdEnv = $env: ERROR_THRESHOLD
6371
64- $eventsWithIdFilter = $allErrors48h | Where-Object { $ignoredEventIds -contains $_.Id.ToString () }
72+ $lookbackEnv = $env: LOOKBACK_HOURS
73+ $evaluationEnv = $env: EVALUATION_WINDOW_HOURS
6574
66- $eventsWithKeywordFilter = $allErrors48h | Where-Object {
67- $eventData = $_.Properties -join " "
68- $eventData += " " + $_.Message
69- $keywordMatches = $false
70- $ignoredKeywords | ForEach-Object {
71- $keyword = $_.Trim ()
72- if ($eventData -match " (?i)\b$ ( $keyword ) \b" ) {
73- $keywordMatches = $true
74- }
75- }
76- $keywordMatches
75+ $ignoredEventIds = if ($filterIdEnv ) { ($filterIdEnv.Split (" ," ) | ForEach-Object { $_.Trim () }) + $defaultEventIds } else { $defaultEventIds }
76+ $ignoredKeywords = if ($filterKeywordEnv ) { ($filterKeywordEnv.Split (" ," ) | ForEach-Object { $_.Trim () }) + $defaultKeywords } else { $defaultKeywords }
77+
78+ $infoThreshold = if ($infoThresholdEnv ) { [int ]$infoThresholdEnv } else { $defaultInfoThreshold }
79+ $warnThreshold = if ($warnThresholdEnv ) { [int ]$warnThresholdEnv } else { $defaultWarnThreshold }
80+ $errorThreshold = if ($errorThresholdEnv ) { [int ]$errorThresholdEnv } else { $defaultErrorThreshold }
81+
82+ $lookbackHours = if ($lookbackEnv ) { [int ]$lookbackEnv } else { $defaultLookbackHours }
83+ $evaluationWindowHours = if ($evaluationEnv ) { [int ]$evaluationEnv } else { $defaultEvaluationWindowHours }
84+
85+ $lookbackStartTime = (Get-Date ).AddHours(- $lookbackHours )
86+ $evaluationStartTime = (Get-Date ).AddHours(- $evaluationWindowHours )
87+
88+ $allErrors = Get-WinEvent - FilterHashtable @ { LogName = ' System' ; Level = 2 ; StartTime = $lookbackStartTime } - ErrorAction SilentlyContinue
89+
90+ function Test-KeywordMatch {
91+ param ($event , $keywords )
92+ $eventData = $event.Properties -join " " + " " + $event.Message
93+ foreach ($keyword in $keywords ) { if ($eventData -match " (?i)\b$ ( $keyword ) \b" ) { return $true } }
94+ return $false
95+ }
96+
97+ $filteredErrors = $allErrors | Where-Object {
98+ $eventIdMatches = $ignoredEventIds -contains $_.Id.ToString ()
99+ $keywordMatches = Test-KeywordMatch $_ $ignoredKeywords
100+ -not ($eventIdMatches -or $keywordMatches )
101+ }
102+
103+ if ($debug -eq " true" ) {
104+ Write-Output " DEBUG MODE ENABLED"
105+ Write-Output " Ignored Event IDs: $ignoredEventIds "
106+ Write-Output " Ignored Keywords: $ignoredKeywords "
107+ Write-Output " INFO Threshold: $infoThreshold "
108+ Write-Output " WARN Threshold: $warnThreshold "
109+ Write-Output " ERROR Threshold: $errorThreshold "
110+ Write-Output " Lookback Hours: $lookbackHours "
111+ Write-Output " Evaluation Window Hours: $evaluationWindowHours "
77112}
78113
79114if ($debug -eq " true" ) {
@@ -82,75 +117,49 @@ if ($debug -eq "true") {
82117 Write-Output " DEBUG: Filtered Keywords: $ignoredKeywords "
83118
84119 if ($eventsWithIdFilter.Count -gt 0 ) {
85- Write-Output " Filtered Events by Event ID in the last 48 hours:"
86- $eventsWithIdFilter | ForEach-Object {
87- Write-Output " TimeCreated: $ ( $_.TimeCreated ) "
88- Write-Output " Event ID: $ ( $_.Id ) "
89- Write-Output " Message: $ ( $_.Message ) "
90- Write-Output " ----------------------------------------"
91- }
92- } else {
93- Write-Output " No events found matching the specified Event IDs in the last 48 hours."
94- }
120+ Write-Output " Filtered Events by Event ID in the last $lookbackHours hours:"
121+ $eventsWithIdFilter | ForEach-Object { Write-Output " TimeCreated: $ ( $_.TimeCreated ) " ; Write-Output " Event ID: $ ( $_.Id ) " ; Write-Output " Message: $ ( $_.Message ) " ; Write-Output " ----------------------------------------" }
122+ } else { Write-Output " No events found matching the specified Event IDs in the last $lookbackHours hours." }
95123
96124 if ($eventsWithKeywordFilter.Count -gt 0 ) {
97- Write-Output " Filtered Events by Keyword in the last 48 hours:"
98- $eventsWithKeywordFilter | ForEach-Object {
99- Write-Output " TimeCreated: $ ( $_.TimeCreated ) "
100- Write-Output " Event ID: $ ( $_.Id ) "
101- Write-Output " Message: $ ( $_.Message ) "
102- Write-Output " ----------------------------------------"
103- }
104- } else {
105- Write-Output " No events found matching the specified Keywords in the last 48 hours."
106- }
125+ Write-Output " Filtered Events by Keyword in the last $lookbackHours hours:"
126+ $eventsWithKeywordFilter | ForEach-Object { Write-Output " TimeCreated: $ ( $_.TimeCreated ) " ; Write-Output " Event ID: $ ( $_.Id ) " ; Write-Output " Message: $ ( $_.Message ) " ; Write-Output " ----------------------------------------" }
127+ } else { Write-Output " No events found matching the specified Keywords in the last $lookbackHours hours." }
107128}
108129
109- $remainingErrors48h = $allErrors48h | Where-Object {
110- $eventIdMatches = $ignoredEventIds -contains $_.Id.ToString ()
111- $eventData = $_.Properties -join " "
112- $eventData += " " + $_.Message
113- $keywordMatches = $false
114- $ignoredKeywords | ForEach-Object {
115- $keyword = $_.Trim ()
116- if ($eventData -match " (?i)\b$ ( $keyword ) \b" ) {
117- $keywordMatches = $true
118- }
119- }
120- if ($eventIdMatches -or $keywordMatches ) {
121- $false
122- } else {
123- $true
124- }
125- }
130+ $errorsInEvaluationWindow = $filteredErrors | Where-Object { $_.TimeCreated -gt $evaluationStartTime }
131+ $count = $errorsInEvaluationWindow.Count
126132
127- if ($remainingErrors48h .Count -gt 0 ) {
128- Write-Output " Remaining Error Events in the last 48 hours (after filtering out ignored Event IDs and Keywords): "
129- $remainingErrors48h | ForEach-Object {
133+ if ($count -ge $errorThreshold ) {
134+ Write-Output " CRITICAL: $count error events in last $evaluationWindowHours hours (threshold: $errorThreshold ). "
135+ $errorsInEvaluationWindow | ForEach-Object {
130136 Write-Output " TimeCreated: $ ( $_.TimeCreated ) "
131137 Write-Output " Event ID: $ ( $_.Id ) "
132138 Write-Output " Message: $ ( $_.Message ) "
133139 Write-Output " ----------------------------------------"
134140 }
141+ exit $errorExitCode
135142}
136-
137- $errors12h = $remainingErrors48h | Where-Object { $_.TimeCreated -gt $start12h }
138-
139- if ($errors12h.Count -ge 4 ) {
140- Write-Output " Error: 4 or more error events found in the last 12 hours."
141- Write-Output " Error Events in the last 12 hours (excluding ignored event IDs and keywords):"
142- $errors12h | ForEach-Object {
143+ elseif ($count -ge $warnThreshold ) {
144+ Write-Output " WARNING: $count error events in last $evaluationWindowHours hours (threshold: $warnThreshold )."
145+ $errorsInEvaluationWindow | ForEach-Object {
143146 Write-Output " TimeCreated: $ ( $_.TimeCreated ) "
144147 Write-Output " Event ID: $ ( $_.Id ) "
145148 Write-Output " Message: $ ( $_.Message ) "
146149 Write-Output " ----------------------------------------"
147150 }
148- exit 1
149- } else {
150- if ($errors12h.Count -eq 0 ) {
151- Write-Output " OK: No error events found in the last 12 hours."
152- } else {
153- Write-Output " OK: Less than 4 error events found in the last 12 hours."
154- }
155- exit 0
151+ exit $warnExitCode
156152}
153+ else {
154+ if ($count -eq 0 ) { Write-Output " INFO: No error events in last $evaluationWindowHours hours." }
155+ else {
156+ Write-Output " INFO: $count error event(s) in last $evaluationWindowHours hours (below warning threshold: $warnThreshold )."
157+ $errorsInEvaluationWindow | ForEach-Object {
158+ Write-Output " TimeCreated: $ ( $_.TimeCreated ) "
159+ Write-Output " Event ID: $ ( $_.Id ) "
160+ Write-Output " Message: $ ( $_.Message ) "
161+ Write-Output " ----------------------------------------"
162+ }
163+ }
164+ exit $infoExitCode
165+ }
0 commit comments