Show frequent error events

Version: 1.4.8
Creator Name: Guy Leech
Date Created: 2018-07-09
Date Modified: 2018-11-21
Scripting language: PS
Download Count: 498

Show all error events from all event logs on the selected computer, ordered on the most frequent, in the specified time period where there is more than one instance of that specific event. Arguments: Minutes Back - the number of minutes back from the current time to search (default is 60) Log Level - the event log level to search for events (default is Error) Excluded log names - an optional regular expression, e.g. "Application", which will exclude events from any event logs where the event log name matches the regular expression.
Tags: Error,Event log

The Script

#Requires -version 3.0

<#
    Find repeated event log entries in a given time window across all event logs and output the most frequent

    ControlUp SBA

    Guy Leech @guyrleech 2018

    Modification history:

    09/07/18   GL  Only display Level in results if more than one level was requested (e.g. Warning,Error,Critical)

    20/11/18   GL  Give message if no events found
#>

## Arguments
##   0  Minutes back from current time
##   1  Event log level(s)
##   2  Regex of event log names to exclude

[int]$ERROR_INVALID_PARAMETER = 87
[string]$multipleUsers = '<Multiple>' ## this is output when more than one user has generated the same event log id entry
[int]$outputWidth = 200 ## could expose this as a parameter

## see if we have been called via ControlUp or by ourself
if( ! $args.Count -or $args.Count -lt 2 )
{
    Write-Error "Incorrect number of arguments passed to script - was expecting minutes back , log levels and optional regex for excluding event log names"
    exit $ERROR_INVALID_PARAMETER
}

# Altering the size of the PS Buffer
if( $PSWindow = (Get-Host).UI.RawUI )
{
    if( $WideDimensions = $PSWindow.BufferSize ) 
    {
        $WideDimensions.Width = $outputWidth
        $PSWindow.BufferSize = $WideDimensions
    }
}

[int]$minutes = $args[0]
[string]$excludedLognames = $args[2]

[hashtable]$eventLevelConversions = @{
        'LogAlways' = 0 ;
        'Critical' = 1 ;
        'Error' = 2;
        'Warning' = 3 ;
        'Informational' = 4;
        'Verbose' = 5;
}

[string]$logname = '*' ## could expose this as a parameter
[datetime]$end = Get-Date
[datetime]$start = $end.AddMinutes( -$minutes )
[array]$results = @()
[int]$eventlogs = 0
[int[]]$eventLevels = @()

ForEach( $level in ( $args[1] -split ',' ) )
{
    $eventLevels += $eventLevelConversions[ $level ]
}

[hashtable]$events = @{}

Get-WinEvent -ListLog $logname -EA silentlycontinue | Where-Object { $_.RecordCount -gt 0  } |  ForEach-Object `
{ 
    if( [string]::IsNullOrEmpty( $excludedLognames ) -or $_.LogName -notmatch $excludedLogNames )
    {
        Write-Verbose "$($_.LogName) $($_.recordcount) $($_.lastwritetime)"
        [hashtable]$filters = @{'Logname'=$_.LogName;StartTime=$start;EndTime=$end }
        if( $eventLevels.Count )
        {
            $filters.Add( 'Level' , $eventLevels )
        }

        [bool]$first = $true

        Get-WinEvent -FilterHashtable $filters -EA SilentlyContinue | ForEach-Object `
        {
            $event = $_
            [string]$key = "{0}:{1}:{2}" -f $event.id , $event.LogName , $event.ProviderName
            $existing = $events[ $key ]
            if( $existing )
            {
                $existing.Count++
                if( $event.TimeCreated -lt $existing.First )
                {
                    $existing.First = $event.TimeCreated
                }
                if( $event.TimeCreated -gt $existing.Last )
                {
                    $existing.Last = $event.TimeCreated
                }
                ## if different user then change to multiple
                if( $event.UserId ) 
                {
                    try
                    {
                        [string]$thisUser = ([System.Security.Principal.SecurityIdentifier]($event.UserId)).Translate([System.Security.Principal.NTAccount]).Value
                        if( $existing.User )
                        {
                            if( $existing.User -ne $multipleUsers )
                            {
                                if( $existing.user -ne $thisUser )
                                {
                                    $existing.user = $multipleUsers
                                }
                            }
                            ## else already recorded it as a multipl euser event
                        }
                        else
                        {
                            $existing.User = $thisUser
                        }
                    }
                    catch {}
                }
            }
            else
            {
                $events.Add( $key , [pscustomobject]@{ 'Count' = [int]1 ; 'First' = $event.TimeCreated ; 'Last' = $event.TimeCreated ; 'Message' = $event.Message ; 'LogName' = $event.LogName ; 'Id' = $event.id ; 'Level' = $event.LevelDisplayName ;
                    'User' = $( if( $event.Userid ) {([System.Security.Principal.SecurityIdentifier]($event.UserId)).Translate([System.Security.Principal.NTAccount]).Value }); 'Task' = $event.TaskDisplayName } )
            }
            if( $first )
            {
                $eventlogs++
                $first = $false
            }
        }
    }
}

Write-Verbose "Found $($events.Count) $($args[1]) events in $eventlogs event logs on $env:COMPUTERNAME between $(Get-Date $start -Format G) and $(Get-Date $end -Format G)"

[string]$dateFormat = 'T'
if( $start.DayOfYear -ne $end.DayOfYear )
{
    $dateFormat = 'G' ## put date in too since covers more than one day
}

[array]$fields = @( 'Count',@{n='First';e={Get-Date $_.First -Format $dateFormat}},@{n='Last';e={Get-Date $_.Last -Format $dateFormat}},@{n='Log Name';e={($_.LogName -split '/')[0] -replace '^Microsoft-Windows-',''}} )
if( $eventLevels.Count -gt 1 )
{
    $fields += 'Level' ## only put the level in if there is more than one, e.g. error and warning
}
$fields += @( 'Id','User','Message','Task' )

if( $events -and $events.Count )
{
    ## Only show those with at least one repetition
    [array]$repeated = @( $events.GetEnumerator() | Select -ExpandProperty Value | Where-Object { $_.Count -gt 1 }  )
    "Found $($repeated.Count) $($args[1]) repeated events out of $($events.Count) events found in event logs between $(Get-Date $start -Format G) and $(Get-Date $end -Format G)"
    if( $repeated -and $repeated.Count )
    {
        $repeated |Sort Count -Descending | Select $fields | Format-Table -Wrap
    }
}
else
{
    "Found no $($args[1]) events in event logs between $(Get-Date $start -Format G) and $(Get-Date $end -Format G)"
}