Powershell to Monitor and Email Windows EventLog Errors

You can set this up in Task Scheduler to run every x minutes and send you an email when there are any errors in the Application Event Log or the System Event Log.

There is a section in the code where you can create more user-friendly messages for complex or confusing error messages. It also allows you to filter out certain known errors that you get over-and-over, but do not want to fix. (For example, we saw an error from a tool called “Monitis” every hour or so, so we didn’t want emails to go out on those types of known and common errors. We were primarily interested in BizTalk errors. (Look for the code: “Where-Object {$_.Message -notlike ‘Monitis*'” to set up your own filters to exclude certain types of errors).

#########################################################
#
# EventLog Monitoring and reporting script
# Neal Walters - 06/18/2013 
# Modeled after DiskMonitor.ps1 
#
#  11/08/2013 - added Select-Object -Propert to only return desired columns (originally 10/17/2013)
#
#########################################################

cls
[string[]] $users = "test@abc.com","test2@abc.com","admin@xyz.com" # List of users to email your report to (separate by comma) or use a distribution list 

$TraceFilename = "E:\Scripts\EventLogExtract_Trace.txt"
$fromemail = "FromEmail@abc.com"
$server = "your.emailRelayServer.com" #enter your own SMTP or Relay server DNS name / IP address here
$minutes = $args[0]
$environment = $args[1] 
$skipmail = $args[2] 

#allow this to be run 
if ($minutes -eq $null)
   {
     $minutes = 10
   }
   
if ($environment -eq $null)
   {
     $environment = "New-BT-Cluster" 
   }

$activeClusterName = (Get-WMIObject Win32_ComputerSystem -ComputerName BIZTALK2013).Name
Write-Host "`$activeClusterName=$activeClusterName"
$computerName = get-content env:computername
if ($activeClusterName -eq $computerName)
 {
   $activeNodeMessage = "(Is-Active-Node)" 
 }
else 
 {
   $activeNodeMessage = "(Is-NOT-Active-Node)" 
 }

#Write-Host $minutes

$traceDateTime = get-date
Write-Host "$traceDateTime Starting Application EventLog"

#$tableFragment = Get-Eventlog -log application -after ((get-date).addMinutes(-120)) -EntryType Error, Warning | ConvertTo-Html -fragment
#multiple $minutes by negative 1 to go back x minutes 
$getEventLog = Get-Eventlog -log application -after ((get-date).addMinutes($minutes*-1)) -EntryType Error 
$traceDateTime = get-date
Write-Host "$traceDateTime Completed Application EventLog count=$($getEventLog.Count)"
Add-Content $TraceFilename "$traceDateTime TRC20 Completed Application EventLog count=$($getEventLog.Count)"
Write-Host "$traceDateTime Starting System EventLog"
$getEventLogSys = Get-Eventlog -log system -after ((get-date).addMinutes($minutes*-1)) -EntryType Error 
$traceDateTime = get-date
Write-Host "$traceDateTime Completed System EventLog count=$($getEventLogSys.Count)"
Add-Content $TraceFilename "$traceDateTime TRC21 Completed System EventLog count=$($getEventLogSys.Count)"

# list of events to exclude (based on text found in the message 
# NOTE: Powershell uses grave-accent mark as line continuation character, but when I split onto 3 lines 
#       I seemed to get emails that had the excluded phrases. 
$getEventLogFiltered = $getEventLog | Where-Object {$_.Message -notlike 'Monitis*' -and $_.Message -notlike '*MQQueueDepthMonitor.exe*' -and $_.Message -notlike '*The local computer may not have the necessary registry*' }
Add-Content $TraceFilename "$traceDateTime TRC22 Filtered EventLog count=$($getEventLog.Count)"
#to only select certain columns, use Select-Object -Property and list the property/columns                      
$getEventLogColumns =   $getEventLogFiltered  | Select-Object -Property TimeGenerated,Source,Message,EntryType,EventID

$getEventLogWithSelectedColumns =   $getEventLogFiltered |   
    Select-Object -Property TimeGenerated,Source,@{name='FriendlyDescr';expression={Message}},Message,EntryType,EventID
  
#$getEventLogSysWithSelectedColumns =   $getEventLogSys |   
#    Select-Object -Property TimeGenerated,Source,@{name='FriendlyDescr';expression={Message}},Message,EntryType,EventID

if ($getEventLogFiltered -ne $null -or $getEventLogSys -ne $null) 
   {
        $needToSendEmail = "Y" 
  
  
      #
    # This is where you can put short friendly Messages that the average guy can understand. 
    #
    foreach ($item in $getEventLogWithSelectedColumns) 
      {
         $item.FriendlyDescr = "N.A."
       #switch -wildcard ($item.Message) 
       #  {
       #     "*The file exists*" {$Item.FriendlyDescr="Duplicate File";break}
       #  }
         if ($item.Message.Contains("SQL Timeout")) 
          {
           $item.FriendlyDescr = "SQL-Timeout"
        }
       
      }
   }
   else 
   {
      Write-Host "No EventLog Rows selected" 
   }

#see http://foxdeploy.com/2014/05/23/using-html-formatting-to-create-useful-webpage-reports-from-powershell/
$head = @"
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Frameset//EN” “http://www.w3.org/TR/html4/frameset.dtd”&gt;
<html><head><title>Unmigrated Systems Report</title><meta http-equiv=”refresh” content=”120? />
<style type=”text/css”>
<!–
body {
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
}

#report { width: 1400px; }

table{
border-collapse: collapse;
border: none;
font: 10pt Verdana, Geneva, Arial, Helvetica, sans-serif;
color: black;
margin-bottom: 10px;
width: 1400px;
}

table td{
font-size: 12px;
padding-left: 0px;
padding-right: 20px;
text-align: left;
}

table th {
font-size: 12px;
font-weight: bold;
padding-left: 0px;
padding-right: 20px;
text-align: left;
}

h2{ clear: both; font-size: 130%;color:#354B5E; }

h3{
clear: both;
font-size: 75%;
margin-left: 20px;
margin-top: 30px;
color:#475F77;
}

p{ margin-left: 20px; font-size: 12px; }

table.list{ float: left; }

table.list td:nth-child(1){
font-weight: bold;
border-right: 1px grey solid;
text-align: right;
}

table.list td:nth-child(2){ padding-left: 7px; }
table tr:nth-child(even) td:nth-child(even){ background: #BBBBBB; }
table tr:nth-child(odd) td:nth-child(odd){ background: #F2F2F2; }
table tr:nth-child(even) td:nth-child(odd){ background: #DDDDDD; }
table tr:nth-child(odd) td:nth-child(even){ background: #E5E5E5; }
div.column { width: 320px; float: left; }
div.first{ padding-right: 20px; border-right: 1px grey solid; }
div.second{ margin-left: 30px; }
table{ margin-left: 20px; }
–>
</style>
</head>

"@

#seems like we cannot put -head and -fragment on the same ConvertTo-Html statemnet 
#$tableFragment = $getEventLogWithSelectedColumns | 
#    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 
  
$tableFragmentTemp = $getEventLogWithSelectedColumns | 
    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 
  
$tableTempFragments = "<h2><font color='yellow'>Application Event Log<font></h2>" + $tableFragmentTemp 

if ($getEventLogSys -ne $null) 
{
$tableFragmentTempSys = $getEventLogSys | 
    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 

$tableTempFragments = $tableTempFragments + "<h2><font color='yellow'>System Event Log<font></h2>" + $tableFragmentTempSys
}

$tableFragment = ConvertTo-HTML -head $head -PostContent $tableTempFragments 

# Production red-alert colors 
if ($environment -eq "PROD")
{
  $background = "Red" 
  $backgroundSolid = "DarkRed"
  $foreground = "White"
}
else
{
    # QA  colors 
  $background = "White" 
  $backgroundSolid = "Blue"
  $foreground = "Blue"
}

Write-Host "`$activeClusterName=$activeClusterName"

# assemble the HTML for our body of the email report.
$HTMLmessage = @"
<font color=""black"" face=""Arial, Verdana"" size=""3"">
<u><b>$Environment (Machine=$computerName $activeNodeMessage EventLog Error Report</b></u>
<br>This report was generated because of Errors found in the EventLog in the last reporting interval of $minutes minutes. 
<br>ActiveNode=$activeClusterName
<br>
<br>
<style type=""text/css"">body{font: .8em ""Lucida Grande"", Tahoma, Arial, Helvetica, sans-serif;}
ol{margin:0;padding: 0 1.5em;}
table{color:$foreground;background:$background;border-collapse:collapse;width:647px;border:5px solid $backgroundSolid;}
thead{}
thead th{padding:1em 1em .5em;border-bottom:1px dotted #FFF;font-size:120%;text-align:left;}
thead tr{}
td{padding:.5em 1em;}
tfoot{}
tfoot td{padding-bottom:1.5em;}
tfoot tr{}
#middle{background-color:$background;}
</style>
<body BGCOLOR="$foreground">
$tableFragment
</body>
"@

# Set up a regex search and match to look for any <td> tags in our body. These would only be present if the script above found disks below the threshold of free space.
# We use this regex matching method to determine whether or not we should send the email and report.
$regexsubject = $HTMLmessage
$regex = [regex] '(?im)<td>'
$traceDateTime = get-date

# if there was any row at all, send the email
if ($skipmail -ne "Y")
{
  if ($regex.IsMatch($regexsubject)) 
  {
              $emailSubject = "$environment Event Log Error Report - Computer=$computerName"
                send-mailmessage -from $fromemail -to $users -subject $emailSubject  -BodyAsHTML -body $HTMLmessage -priority High -smtpServer $server
             Write-Host "$traceDateTime Email sent" 
                Add-Content $TraceFilename "$traceDateTime TRC95 Email Sent "
  }
        else
        {
                Write-Host "$traceDateTime No email was needed because no Errors in Application EventLog in the last $minutes minutes" 
                Add-Content $TraceFilename "$traceDateTime TRC96 No Email Sent "
        }
}
 
# End of Script

What I do is set up a .cmd or .bat file like this, and that is what I put in the scheduler. It simpley runs the Powershell and passes the necessary parms.
Make sure the first parm (10 in the example below) matches exactly how often you run the job in the task scheduler.

powershell -command "& 'E:\Scripts\EventLogExtract.ps1'" 10 NEW-BT-PROD

Uncategorized  

Leave a Reply