Okay, I have now made additional modifications, including refactoring the script to get around PRTG's issue of passing array parameters using an @ sign. The array parameters can now take in a comma delimited string and I added code to turn the string into an array later on. The only caveat is, obviously, you can't use commas in the strings provided to ErrorStrings or WarningStrings. Well, you can, but you may not get the results you want :-)
If anybody wants this code, here it is. Note, I change some of the parameter names as well because it was bothering me if they are lists, but not plural and/or didn't match the naming convention MS is using (like LogNames instead of Channels).
#___ ___ _____ ___
#| _ \ _ \_ _/ __|
#| _/ / | || (_ |
#|_| |_|_\ |_| \___|
# NETWORK MONITOR
#-------------------
#(c) 2015 Stephan Linke, Paessler AG
#
# Name: PRTG-Read-WinEventLog
# Description: Reads the windows eventlog and filters for the specific events
#
# Version History
# ----------------------------
# Version Date Who Description
#---------------------------------------------------
# 1.2 11/13/2019 Todd Piket Made ProviderName optional. Removed truncation of message to 50 characters
# so I can see the full message. Made the switch parameters function properly
# by removing default value of $true. Changed the AlwaysShowMessage switch to
# more specific ShowLastMessage. Added the ShowAllMessages switch so you can
# return all the messages that match the provided criteria if you want. Added
# a TruncateMessage switch and TruncateMessageLenght parameter so the user can
# specify the truncation and length of messages, if desired. Changed Channel
# parameter to LogNames. Pluralized all parameter namess that can take in a
# list of items. Change the item list for array parameters to be a comma
# delimited string and then turn the string into a Powershell array later.
# This works around the PRTG problem of using the @ sign when passing parameters
# to Powershell.
# 1.1 07/12/2016 Stephan Linke [Fixed] If only one event existed, the sensor showed no events
# 1.0 19/05/2014 Stephan Linke Initial Release
<#
.SYNOPSIS
Reads the windows eventlog and filters for the specified events.
.DESCRIPTION
This custom sensor for PRTG will read the given EventLog file and search it
for the defined events. It also allows to error if the last event found has a certain ID or
Message.
.PARAMETER ComputerName
The computer whose event log you want to check
.PARAMETER Channel
The log name that is used by the application
.PARAMETER ProviderName
The application that you want to watch
.PARAMETER EventID
The event IDs you want to filter. Seperate multiple IDs with comma
.PARAMETER WarningEvents
The event IDs you want to raise a warning when found. Those IDs also have to be included in the event ids
.PARAMETER ErrorEvents
The event IDs you want to raise a error when found. Those IDs also have to be included in the event ids
.Parameter Levels
The Loglevels you want to include in the search
.PARAMETER MaxAge
The age of the Logfile in minutes
.PARAMETER LimitEntries
Maximum number of log entries to be checked (order is new -> old)
.PARAMETER WarningStrings
Put the sensor into a warning state when a certain string is found within the message
.PARAMETER ErrorStrings
Put the sensor into a error state when a certain string is found within the message
.PARAMETER ShowLastMessage
If this parameter is set, only the last (most recent) message from the event log will be returned.
.PARAMETER ShowAllMessages
If this parameter is set, all messages from the eventlog matching the specified parameters will be returned.
.PARAMETER TruncateMessage
If this parameter is set, all messages from the eventlog will be truncated to the specfied length. If ShowAllMessages
is specified, then EACH MESSAGE FOUND will be truncated to the length specified.
.PARAMETER StateBasedOnLastID
If this parameter is set, not the sheer number of events will decide if the sensor will go into error or warning state,
but only the event id of the last entry found. This is useful for RAID controllers, etc.
.PARAMETER StateBasedOnLastMessage
If this parameter is set, not the sheer number of events will decide if the sensor will go into error or warning state,
but only the message of the last entry found. This is useful if messages have the same event ID for errors and information events.
.PARAMETER Username and Password
The username and password that the script should use to create the credential object.
Format -Username "domain\username" -Password 'yourpass'
.OUTPUTS
<number of entries found>:<entries> found in the event log. Last message: <last entry message>
.EXAMPLE
C:\PS> .\PRTG-Read-WinEventLog.ps1 -ComputerName %host -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -ProviderNames "Microsoft-Windows-Immersive-Shell" -LogNames "Microsoft-Windows-TWinUI/Operational" -LimitEntries 1 -MaxAge 1 -EventIDs 1719 -Levels 4
.EXAMPLE
C:\PS> .\PRTG-Read-WinEventLog.ps1 -ComputerName %host -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -ProviderNames "Microsoft-Windows-Immersive-Shell" -LogNames "Microsoft-Windows-TWinUI/Operational" -LimitEntries 1 -MaxAge 1 -EventIDs 1719 -Levels 4 -StateBasedOnLastEntry
#>
param(
[string]$ComputerName = "",
# [string[]]$Channels = @(),
[string]$LogNames = "",
# [string[]]$ProviderNames = @(),
[string]$ProviderNames = "",
# [int[]]$EventIDs = @(2),
# [int[]]$WarningEvents = @(),
[string]$WarningEvents = "",
# [int[]]$ErrorEvents = @(),
[string]$ErrorEvents = "",
# [string[]]$ErrorStrings = @(),
# [string[]]$WarningStrings = @(),
[string]$ErrorStrings = "",
[string]$WarningStrings = "",
# [int[]]$Levels = @(),
[string]$Levels = "",
[string]$EventIDs = "",
[float]$MaxAge = 60,
[int]$LimitEntries = 100,
# empty credentials, in case we run at localhost.
[string]$Username = '',
[string]$Password = '',
[int]$TruncateMessageLength = 50,
[switch]$ShowLastMessage,
[switch]$ShowAllMessages,
[switch]$TruncateMessage,
[switch]$StateBasedOnLastMessage,
[switch]$StateBasedOnLastEventID,
[switch]$Verbose
)
[System.Threading.Thread]::CurrentThread.CurrentCulture = New-Object "System.Globalization.CultureInfo" "en-US"
#
# Process passed in strings that were destined for array parameters by
# now turning them into an appropriate array.
#
if (! [string]::IsNullOrEmpty($EventIDs))
{
$arEventIDs = foreach ($number in $EventIDs.split(",")) {([int]::parse($number))}
}
#$arEventIDs
if (! [string]::IsNullOrEmpty($LogNames))
{
$arLogNames = $LogNames.split(",")
}
#$arLogNames
if (! [string]::IsNullOrEmpty($ProviderNames))
{
$arProviders = $ProviderNames.split(",")
}
#$arProviders
if (! [string]::IsNullOrEmpty($ErrorEvents))
{
$arErrorIDs = foreach ($number in $ErrorEvents.split(",")) {([int]::parse($number))}
}
#$arErrorIDs
if (! [string]::IsNullOrEmpty($WarningEvents))
{
$arWarnIDs = foreach ($number in $WarningEvents.split(",")) {([int]::parse($number))}
}
#$arWarnIDs
if (! [string]::IsNullOrEmpty($Levels))
{
$arLevels = foreach ($number in $Levels.split(",")) {([int]::parse($number))}
}
if (! [string]::IsNullOrEmpty($ErrorStrings))
{
$arErrorStrings = $ErrorStrings.split(",")
}
if (! [string]::IsNullOrEmpty($WarningStrings))
{
$arWarnStrings = $WarningStrings.split(",")
}
# This will return an error and exit accordingly
# If there's an error, only this will be outputted
#######################################
function This-PrtgResult([int]$Value = 0, [string]$Message,[int]$ExitCode){
if(!($verbose)){
Write-Host ([string]::Format("{0}:{1}",$Value,$Message));
exit $ExitCode;
}
else
{ Write-Host ([string]::Format("{0}:{1}",$Value,$Message)); }
}
# This will create the credential
# object that is used to get the events
#######################################
function This-GenerateCredentials(){
# Generate Credentials if we're not checking localhost
if((($env:COMPUTERNAME) -ne $ComputerName)){
# Generate Credentials Object first
$SecPasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$Credentials= New-Object System.Management.Automation.PSCredential ($Username, $secpasswd)
return $Credentials
}
# otherwise return false
else{ return "false" }
}
# This will retrieve the event log entries
# based on channel, provider and events ID.
#######################################
function This-ReadEventLog(){
$Credentials = (This-GenerateCredentials)
$EventFilter = @{};
# Filter the objects according to their timestamp
$EventFilter.Add("StartTime",(get-date).AddMinutes(-$MaxAge))
# We need a provider, otherwise the script will error if none is given.
# If it's set, it'll be added to the filter
if($arProviders.Count -gt 0)
{
$EventFilter.Add('ProviderName',$arProviders)
}
# We need a Channel, otherwise the script will error if none is given.
# If it's set, it'll be added to the filter
if($arLogNames.Count -eq 0)
{ This-PrtgResult -Message "No Channel given. Please enter a valid Provider." -ExitCode 1; }
$EventFilter.Add("LogName",$arLogNames)
# If there are event IDs, add them to the filter
if($arEventIDs.Count -gt 0)
{ $EventFilter.Add("ID",$arEventIDs) }
# if there are levels, add them to the filter
if($arLevels.Count -gt 0)
{ $EventFilter.Add("Level",$arLevels) }
try{
if($Credentials -ne "false"){ $Events = (Get-WinEvent -ComputerName $ComputerName -FilterHashTable $EventFilter -MaxEvents $LimitEntries -Credential $Credentials) }
else { $Events = (Get-WinEvent -ComputerName $ComputerName -FilterHashTable $EventFilter -MaxEvents $LimitEntries) }
return $Events
}
catch [Exception]
{ This-PrtgResult -Message ([string]::Format("Can't find anything for {0} in your {1} eventlog. Please check Log name, Provider, Log ID, EventID, ComputerName and Credentials",$arProviders -join " or ",$arLogNames -join " or ")) -ExitCode 1 }
# Additional Debugging, replace above line with the one below.
#{ This-PrtgResult -Message ([string]::Format("Exception: $($_.Exception.Message) - Can't find anything for {0} in your {1} eventlog. Please check Log name, Provider, Log ID, EventID, ComputerName and Credentials",$arProviders -join " or ",$arLogNames -join " or ")) -ExitCode 1 }
}
# This will evaluate the results from the above
# function and return the sensor value
#######################################
function This-EvaluateLogResults(){
$Events = (This-ReadEventLog);
$Counter = $Events.Count
# We need the events to be in an array, even if we only have one value
# This makes iterating easier.
$Events = @($Events);
if($Counter -gt 0){
$EventList = [System.Collections.ArrayList]$Events
# Show the last message when enabled
if(($ShowLastMessage) -and ($Counter -ne 0))
{
if(!([string]::IsNullOrEmpty($EventList[0].Message)))
{
# $LastMessage = ($EventList[0].Message.Remove(50)+"..." -replace "`n|`r")
# $LastMessage = ($EventList[0].Message+ "" -replace "`n|`r")
if ($TruncateMessage)
{
$LastMessage = ($EventList[0].Message.substring(0, [System.Math]::Min($TruncateMessageLength, $EventList[0].Message.Length)))
}
else
{
$LastMessage = ($EventList[0].Message)
}
$Message = "(Last entry: "+$LastMessage+")"
}
else
{
$Message = "(Latest event has no message)"
}
}
# Show all messages matching criteria when enabled.
if(($ShowAllMessages) -and ($Counter -ne 0))
{
$i = 0
foreach($event in $EventList)
{
if(!([string]::IsNullOrEmpty($EventList[$i].Message)))
{
# $Message += ($EventList[$i].Message+ "" -replace "`n|`r")
if ($TruncateMessage)
{
$Message += ($EventList[$i].Message.substring(0, [System.Math]::Min($TruncateMessageLength, $EventList[$i].Message.Length)))
}
else
{
$Message += ($EventList[$i].Message)
}
}
$i++
}
}
# Search for error and warning IDs
if((($StateBasedOnLastEventID)) -and (-not($StateBasedOnLastMessage))){
switch ($EventList[0].Id){
{$arErrorIDs -contains $EventList[0].Id} { This-PrtgResult -Message "Critical Event found: $($Message)" -Value $Counter -ExitCode 1 }
{$arWarnIDs -contains $EventList[0].Id}{ This-PrtgResult -Message "Warning Event found: $($Message)" -Value $Counter -ExitCode 1 }
}
}
# Search for messages that contain the error and warning strings
elseif(($Counter -ne 0) -and ($StateBasedOnLastMessage)){
foreach($String in $arErrorStrings){
if($EventList[0].Message -match "($String)"){ This-PrtgResult -Message "Critical Event found: $($Message)" -Value $Counter -ExitCode 1 } }
foreach($String in $arWarnStrings){
if($EventList[0].Message -match "($String)"){ This-PrtgResult -Message "Warning Event found: $($Message)" -Value $Counter -ExitCode 1 } }
}
# Search for messages that contain the error and warning strings and the messages
elseif(($Counter -ne 0) -and (($StateBasedOnLastMessage) -and ($StateBasedOnLastID))){
switch ($EventList[0].Id){
{($arErrorIDs -contains $EventList[0].Id) -and ($EventList[0].Message -match $arErrorStrings)} { This-PrtgResult -Message "Critical Event found: $($Message)" -Value $Counter -ExitCode 1}
{($arWarnIDs -contains $EventList[0].Id) -and ($EventList[0].Message -match $arWarnStrings)} { This-PrtgResult -Message "Warning Event found: $($Message)" -Value $Counter -ExitCode 1}
}
}
}
switch ($Counter){
{$Counter -eq 0}{ This-PrtgResult -Message "No events found" -ExitCode 0 }
{$Counter -ge 1}{ This-PrtgResult -Message "$($Counter) event(s) found $($Message)" -Value $Counter -ExitCode 0 }
}
}
This-EvaluateLogResults;
Add comment