What is this?

This knowledgebase contains questions and answers about PRTG Network Monitor and network monitoring in general.

Learn more

PRTG Network Monitor

Intuitive to Use. Easy to manage.
More than 500,000 users rely on Paessler PRTG every day. Find out how you can reduce cost, increase QoS and ease planning, as well.

Free Download

Top Tags


View all Tags

How can I monitor my historic windows events?

Votes:

1

I know that there's a sensor for the event log, but I want to know how many messages appeared in the last few hours and be alerted if a certain amount is reached. Additionally, I want to get the results from a Log that is not listed in the WMI EventLog sensor.

You guys have a solution for that?

custom-script-exe custom-sensor eventlog powershell wmi

Created on Nov 28, 2014 9:31:33 AM by  Stephan Linke [Paessler Support]

Last change on Mar 19, 2015 3:44:29 PM by  Martina Wittmann [Paessler Support]



43 Replies

Accepted Answer

Votes:

5

The following sensor will search multiple event logs from multiple providers. You can search for IDs and the message text. Make sure you read the synopsis of the sensor to get an idea of what the specific parameters do. Please use the following script with an EXE/Script sensor:


Note: Newer PRTG versions require a different parameter setup. Please check the following reply.


#___ ___ _____ ___
#| _ \ _ \_   _/ __|
#|  _/   / | || (_ |
#|_| |_|_\ |_| \___|
#    NETWORK MONITOR
#-------------------
#(c) 2015 Stephan Linke, Paessler AG
#
# Name: PRTG-Get-WinEvents
# Description: Reads the windows eventlog and filters for the specific events
# 
# Version History
# ----------------------------
# Version Date        Description 
# 1.1     07/12/2016  [Fixed] If only one event existed, the sensor showed no events
# 1.0     19/05/2014  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 hours
   .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 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> .\Get-Events.ps1  -ComputerName %host -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -ProviderName "Microsoft-Windows-Immersive-Shell" -Channel "Microsoft-Windows-TWinUI/Operational" -LimitEntries 1 -MaxAge 1 -EventID 1719 -Level 4
   .EXAMPLE
   C:\PS> .\Get-Events.ps1  -ComputerName %host -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -ProviderName "Microsoft-Windows-Immersive-Shell" -Channel "Microsoft-Windows-TWinUI/Operational" -LimitEntries 1 -MaxAge 1 -EventID 1719 -Level 4 -StateBasedOnLastEntry
#>

param(
    [string]$ComputerName     = "Percy",
    [string[]]$Channel        = @("PRTG Network Monitor"),
    [string[]]$ProviderName   = @("PRTG Network Monitor"),
    [int[]]$EventID           = @(2),
    [int[]]$WarningEvents     = @(),
    [int[]]$ErrorEvents       = @(),
    [string[]]$ErrorStrings   = @(),
    [string[]]$WarningStrings = @(),
    [int[]]$Levels            = @(),
    [float]$MaxAge            = 24,
    [int]$LimitEntries        = 100,
    # empty credentials, in case we run at localhost.
    [string]$Username       = '',
    [string]$Password       = '',
    [switch]$AlwaysShowMessage = $true,
    [switch]$StateBasedOnLastMessage = $true,
    [switch]$StateBasedOnLastEventID = $true

)
[System.Threading.Thread]::CurrentThread.CurrentCulture = New-Object "System.Globalization.CultureInfo" "en-US"
[switch]$Verbose = $false

# 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).AddHours(-$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($ProviderName.Count -eq 0)
    { This-PrtgResult -Message "No ProviderName given. Please enter a valid Provider." -ExitCode 1; }
    
    $EventFilter.Add('ProviderName',$ProviderName)
    
    # 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($Channel.Count -eq 0)
    { This-PrtgResult -Message "No Channel given. Please enter a valid Provider." -ExitCode 1;  }
    
    $EventFilter.Add("LogName",$Channel)  

    # If there are event IDs, add them to the filter
    if($EventID.Count -gt 0)
    { $EventFilter.Add("ID",$EventID); }

    # if there are levels, add them to the filter
    if($Levels.Count -gt 0)
    { $EventFilter.Add("Level",$Levels) }

    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",$ProviderName -join " or ",$Channel -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
           
            # Always show the last message when enabled
            if(($AlwaysShowMessage) -and ($Counter -ne 0)){
                if(!([string]::IsNullOrEmpty($EventList[0].Message))){
                 $LastMessage = ($EventList[0].Message.Remove(50)+"..." -replace "`n|`r")
                 $Message = "(Last entry: "+$LastMessage+")"
                }
            else
            { $Message = "(Latest event has no message)"; }
            }
                 
            # Search for error and warning IDs
            if((($StateBasedOnLastEventID)) -and (-not($StateBasedOnLastMessage))){
                switch ($EventList[0].Id){
                    {$ErrorEvents -contains $EventList[0].Id}  { This-PrtgResult -Message "Critical Event found: $($Message)" -Value $Counter -ExitCode 1 }
                    {$WarningEvents -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 $ErrorStrings){
                        if($EventList[0].Message -match "($String)"){ This-PrtgResult -Message "Critical Event found: $($Message)" -Value $Counter -ExitCode 1 } }
                    foreach($String in $WarningStrings){
                        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){
                        {($ErrorEvents -contains $EventList[0].Id) -and ($EventList[0].Message -match $ErrorStrings)}     { This-PrtgResult -Message "Critical Event found: $($Message)" -Value $Counter -ExitCode 1}
                        {($WarningEvents -contains $EventList[0].Id) -and ($EventList[0].Message -match $WarningStrings)} { 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;

In order to get detailed information about the event you want to monitor, please open the Windows Event Viewer application, search for the event and switch to the detail tab. Select the XML view to see all properties of the event.

Parameter example:

-ComputerName %host -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -MaxAge 8 -Channel 'Application' -ProviderName 'ESENT' -EventID @(105,200,301) -WarningEvents @(200) -ErrorEvents @(301) -AlwaysShowLastMessage

The % variables will be replaced automatically by PRTG. If you want to look for multiple event IDs, strings, logs and providers, simply add them to the array:
IDs @(101,102,103,104)
Events, Strings @("String 1","String 2")

If you want to work with channel limits instead of the hardcoded limits (0 = green, 1 = warning, 2 and above = error), edit the lines 155 and 156, specifically the exit code.

Important The user you use needs to have administrative privileges on the target device; he can't read the events as a normal user.

Created on Nov 28, 2014 9:55:25 AM by  Stephan Linke [Paessler Support]

Last change on Nov 14, 2019 5:47:42 PM by  Stephan Linke [Paessler Support]



Votes:

0

You guys really need to create a far better by default sensor for event logs. WE could really do with something that can check for ISO 27001:2013 compliance etc. So some reporting would also be very useful. I think it would make PRTG a stronger selling point if there was better support for sys-logs, eventlogs and IIS logs.

Created on Feb 27, 2015 3:02:09 PM



Votes:

1

Hi Lasse,

How exactly would one check for ISO 27001:2013 compliance? What sensor functionality would you like to see within the syslog and eventlog sensors? Do you rather want to analyze the IIS logs itself (which would work with a PowerShell script) or would it be possible for you to push the logs into a database? If so, you could use our various database sensors to get the information you want out of the logs :)

Best, Stephan

Created on Mar 2, 2015 1:21:12 PM by  Stephan Linke [Paessler Support]



Votes:

0

Just so you know, I updated the script to be more sophisticated, offering a better search function. It can also deal with multiple search filters at once :)

Created on May 6, 2015 9:23:34 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hi all,

could you please tell me, how to copy the script in a textfile, without loosing all the newlines?

Help would be appreciated....I tried to copy it via the html-source but there, the special characters are not right!

thx,

matthias

Created on Jul 7, 2015 11:43:08 AM



Votes:

0

Whoops, that's weird. I'll forward it to our developers. Meanwhile, you can get it here: http://pastebin.com/9Quaehke

Created on Jul 7, 2015 12:36:49 PM by  Stephan Linke [Paessler Support]



Votes:

0

Hi,

Many thanks for providing this.

I have no scripting background but as I have a dire need to make this work, I have been trying to implement this without any luck.

I need to monitor Applications and Services Logs > Microsoft > Windows > AppLocker > EXE and DLL (%SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-AppLocker%4EXE and DLL.evtx) for Event 8004 on any monitored PC.

I appreciate that the script is heavily remarked but I am getting nowhere with what I need to change to achieve the above.

Any assistance would be greatly appreciated.

Created on Nov 25, 2016 4:44:08 AM



Votes:

0

Could you provide me with the XML of one event within that log (can be obtained from the detail tab of it). Thanks!

Created on Nov 28, 2016 7:12:48 AM by  Stephan Linke [Paessler Support]



Votes:

0

Apologies for late response, I missed your post :(

Here is the XML you requested:

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="Microsoft-Windows-AppLocker" Guid="{CBDA4DBF-8D5D-4F69-9578-BE14AA540D22}" /> 
  <EventID>8006</EventID> 
  <Version>0</Version> 
  <Level>3</Level> 
  <Task>0</Task> 
  <Opcode>0</Opcode> 
  <Keywords>0x4000000000000000</Keywords> 
  <TimeCreated SystemTime="2016-11-06T03:27:05.276939600Z" /> 
  <EventRecordID>6</EventRecordID> 
  <Correlation /> 
  <Execution ProcessID="17568" ThreadID="17572" /> 
  <Channel>Microsoft-Windows-AppLocker/MSI and Script</Channel> 
  <Computer>##### REMOVED #####</Computer> 
  <Security UserID="##### REMOVED #####" /> 
  </System>
- <UserData>
- <RuleAndFileData xmlns:auto-ns2="http://schemas.microsoft.com/win/2004/08/events" xmlns="http://schemas.microsoft.com/schemas/event/Microsoft.Windows/1.0.0.0">
  <PolicyName>SCRIPT</PolicyName> 
  <RuleId>{00000000-0000-0000-0000-000000000000}</RuleId> 
  <RuleName>-</RuleName> 
  <RuleSddl>-</RuleSddl> 
  <TargetUser>##### REMOVED #####</TargetUser> 
  <TargetProcessId>17568</TargetProcessId> 
  <FilePath>##### REMOVED #####</FilePath> 
  <FileHash>##### REMOVED #####</FileHash> 
  <Fqbn>##### REMOVED #####</Fqbn> 
  </RuleAndFileData>
  </UserData>
  </Event>

Thanks!

Created on Dec 18, 2016 5:42:48 AM

Last change on Dec 20, 2016 12:19:37 PM by  Stephan Linke [Paessler Support]



Votes:

0

Since I updated the script above, the following parameters should do the trick: -Channel @(Microsoft-Windows-AppLocker) -Provider @("Microsoft-Windows-AppLocker") -EventID 8006 -username '%windowsdomain\%windowsuser' -password '%windowspassword'

...could you check that out? :)

Created on Dec 20, 2016 12:18:25 PM by  Stephan Linke [Paessler Support]



Votes:

0

Hi i'm following the above but i'm having issues. I have made the query simpler by just using the application event log. Heres the XML output

Log Name:      Application
Source:        Microsoft-Windows-WMI
Date:          31/01/2017 16:19:10
Event ID:      5605
Task Category: None
Level:         Information
Keywords:      
User:          SYSTEM
Computer:      RDSCB01.sachetpack.local
Description:
The root\cimv2\rdms namespace is marked with the RequiresEncryption flag. Access to this namespace might be denied if the script or application does not have the appropriate authentication level. Change the authentication level to Pkt_Privacy and run the script or application again.
Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Microsoft-Windows-WMI" Guid="{1EDEEE53-0AFE-4609-B846-D8C0B2075B1F}" />
    <EventID>5605</EventID>
    <Version>2</Version>
    <Level>4</Level>
    <Task>0</Task>
    <Opcode>0</Opcode>
    <Keywords>0x8000000000000000</Keywords>
    <TimeCreated SystemTime="2017-01-31T16:19:10.850939300Z" />
    <EventRecordID>168525</EventRecordID>
    <Correlation ActivityID="{27F2CD1B-4EFE-0000-5EF3-0F30FE4ED201}" />
    <Execution ProcessID="988" ThreadID="7084" />
    <Channel>Application</Channel>
    <Computer>RDSCB01.sachetpack.local</Computer>
    <Security UserID="S-1-5-18" />
  </System>
  <UserData>
    <data_0x8000003F xmlns="http://manifests.microsoft.com/win/2006/windows/WMI">
      <Namespace>root\cimv2\rdms</Namespace>
    </data_0x8000003F>
  </UserData>
</Event>

For the parameters i'm using

-Channel @(Application) -Provider @("Microsoft-Windows-WMI") -EventID 5605 -username '%windowsdomain\%windowsuser' -password '%windowspassword'

The warning states No Channel given. Please enter a valid Provider.

If i put quotes around the channel e.g.

-Channel @("Application") -Provider @("Microsoft-Windows-WMI") -EventID 5605 -username '%windowsdomain\%windowsuser' -password '%windowspassword'

the warning output states Can't find anything for Microsoft-Windows-WMI in your Application eventlog. Please check Log name, Provider, Log ID, EventID, ComputerName and Credentials

I can't see where i'm going wrong, any advice is appreciated.

Created on Jan 31, 2017 4:35:29 PM

Last change on Feb 1, 2017 6:36:33 AM by  Stephan Linke [Paessler Support]



Votes:

0

It's -ProviderName, not -Provider :)

Created on Feb 1, 2017 6:40:07 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hi, I still haven't been able to get this to work:

Response not wellformed:

"(File C:\Program Files (x86)\PRTG Network Monitor\custom sensors\EXE\Event Log.p s1 cannot be loaded because the execution of scripts is disabled on this system . Please see "get-help about_signing" for more details. At line:1 char:157 + if ($PSVersionTable.PSVersion -ge (new-object 'Version' 5,0)) { Import-Module Microsoft.PowerShell.Management; Import-Module Microsoft.PowerShell.Utility};& <<<< 'C:\Program Files (x86)\PRTG Network Monitor\custom sensors\EXE\Event Log .ps1' -Channel @(Microsoft-Windows-AppLocker) -ProviderName @('Microsoft-Window s-AppLocker') -EventID 8006 -username '*****\*****' -password '*****'; exit $LASTEXITCODE + CategoryInfo : NotSpecified: (:) [], PSSecurityException + FullyQualifiedErrorId : RuntimeException )" (code: PE132)

The target machine has an execution policy of Unrestricted on both x86/x64 versions of PS.

Created on Mar 14, 2017 6:41:00 AM

Last change on Mar 14, 2017 6:49:22 AM by  Sven Roggenhofer [Paessler Technical Support]



Votes:

0

Are you running the script on a remote probe by any chance? Then you'll have to set the execution policies there and not (exclusively) on the probe :)

Created on Mar 14, 2017 9:13:45 AM by  Stephan Linke [Paessler Support]



Votes:

0

Maybe too late but the command loine should look like this:

-Channel "Microsoft-Windows-AppLocker/EXE and DLL" -Provider "Microsoft-Windows-AppLocker" -EventID 8006 -username '%windowsdomain\%windowsuser' -password '%windowspassword'

Regards Oliver

Created on Dec 4, 2017 9:34:17 AM

Last change on Dec 4, 2017 10:15:01 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hi Oliver,

Thanks for sharing! :)


Kind regards,
Stephan Linke, Tech Support Team

Created on Dec 4, 2017 10:15:28 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hi

I have tried to get this to work without luck. I am able to run it locally on the server I want to monitor, but I can not get it to work from a probe device. I get this error: "0:Can't find anything for Microsoft-Windows-SyncShare in your Microsoft-Windows-SyncShare/Operational eventlog. Please check Log name, Provider, Log ID, EventID, ComputerName and Credentials"

This is the command I use: "PS C:\> .\windows_eventlog_monitor.ps1 -ComputerName %host -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -ProviderName "Microsoft-Windows-SyncShare" -Channel "Microsoft-Windows-SyncShare/Operational" -MaxAge 100 -EventID 4016 -Level 2"

I have tried with different variations of marking " ' and so on. Same thing happens, it does not find anything. When I test it on the local probe with the powershell command I replace the variables with the real hostname password and so on.

Hope you can help.

Best regards Jacob

Created on Mar 16, 2018 10:32:19 AM



Votes:

0

Hi Jacob,

That's weird - Did you get channel and Provider name from the actual event XML in the event viewer? When you use the following instead:

-ProviderName @("Microsoft-Windows-SyncShare") -Channel @("Microsoft-Windows-SyncShare/Operational")

...does that do the trick? The channel name (i.e. /Operational) seems a bit suspicious. If you like, I can take a peek at the actual event XML and let you know the correct parameters for the script.


Kind regards,
Stephan Linke, Tech Support Team

Created on Mar 19, 2018 9:55:14 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hi Stephan

It did not do the trick with the remote probe. I can run it locally but not from the probe device.

This is the XML maybe that can help?

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="Microsoft-Windows-SyncShare" Guid="{9E6153AD-A829-4B70-B997-8E463A7A111C}" /> 
  <EventID>4016</EventID> 
  <Version>0</Version> 
  <Level>2</Level> 
  <Task>0</Task> 
  <Opcode>0</Opcode> 
  <Keywords>0x8000000000000000</Keywords> 
  <TimeCreated SystemTime="2018-03-19T08:24:20.711096500Z" /> 
  <EventRecordID>17199</EventRecordID> 
  <Correlation /> 
  <Execution ProcessID="1372" ThreadID="1444" /> 
  <Channel>Microsoft-Windows-SyncShare/Operational</Channel> 
  <Computer><removed></Computer> 
  <Security UserID="S-1-5-21-2117862466-2125879483-944732636-28415" /> 
  </System>
- <EventData>
  <Data Name="ConfigName"><removed></Data> 
  <Data Name="HResultStr">(0x80c80037) You're not set up on the server. Email your organization's tech support and ask them if they can give you access to Work Folders.</Data> 
  <Data Name="HResult">-2134376393</Data> 
  </EventData>
  </Event>

Created on Mar 20, 2018 8:46:22 AM

Last change on Mar 20, 2018 9:58:29 AM by  Stephan Linke [Paessler Support]



Votes:

0

Thanks for testing that. Could you try to configure the parameters within the script (instead of configuring the parameter field in PRTG) and see if the result is correct when running on the probe itself?


Kind regards,
Stephan Linke

Created on Mar 20, 2018 9:59:59 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hi Stephan

I have tried to modify the script itself on the probe, and ran the same modified script on the host that should be monitored.

The result was that running the script locally on the server that should be monitored works, as it has before (10:10 log entries found). When I run the same script on the probe I still get the error "0:Can't find anything for Microsoft-Windows-SyncShare in your Microsoft-Windows-SyncShare/Operational eventlog. Please check Log name, Provider, Log ID, EventID, ComputerName and Credentials".

Can it be a Windows setting blocking outsider from reading the event log?

Created on Mar 21, 2018 10:24:50 AM



Votes:

0

Hi Jacob,

Nice to hear we're making progress - is the user you're using in PRTG configured as an administrator on the target host? Otherwise he can't read the events, i.e. has no access to them.


Kind regards,
Stephan Linke, Tech Support Team

Created on Mar 21, 2018 11:48:21 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hi Stephan

I found the solution. It was another thing blocking the script from running as suspected. It was the Windows Firewall with the rule "Remote Event Log Management (RPC)". If you enable this the script works.

Thanks for your help.

Br Jacob

Created on Mar 22, 2018 12:07:37 PM



Votes:

0

I've had a recent issue with this script. I was getting a translation error with the event variables which I corrected by changing the [int[]] to [array]. This is odd however because the script does run as is in powershell. It seems it was trying to convert the entire @(…,...,...) string into a single [int] when called from PRTG.

Created on Jun 10, 2018 2:08:14 PM



Votes:

0

Could you post the entire content of the parameter field?


Kind regards,
Stephan Linke, Tech Support Team

Created on Jun 11, 2018 6:55:34 AM by  Stephan Linke [Paessler Support]



Votes:

0

i am not able to do multiple eventid:

Antwort nicht wohlgeformt: "(C:\Program Files (x86)\PRTG Network Monitor\custom sensors\EXE\eventlog.ps1 : Die Argumenttransformation für den Parameter "EventID" kann nicht verarbeitet werden. Der Wert "@(70,71)" kann nicht in den Typ "System.Int32[]" konvertiert werden. Fehler: "Der Wert "@(70,71)" kann nicht in den Typ "System.Int32" konvertiert werden. Fehler: "Die Eingabezeichenfolge hat das falsche Format."" In Zeile:1 Zeichen:461 + ... indows-Server Infrastructure Licensing' -EventID `@`(70`,71`) -MaxAge ... + ~~~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [eventlog.ps1], ParameterBindingArgumentTransformationException + FullyQualifiedErrorId : ParameterArgumentTransformationError,eventlog.ps1 )" (Code: PE132)

Parameter is:

-ComputerName server -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -Channel 'Microsoft-Windows-Server Infrastructure Licensing/Operational' -ProviderName 'Microsoft-Windows-Server Infrastructure Licensing' -EventID @(70,71) -MaxAge 8

It is working on powershell console but not in PRTG!

I found also that this in results in an error if message shorter than 50: $LastMessage = ($EventList[0].Message.Remove(50)+"..." -replace "`n|`r")

Microsoft-Windows-Server Infrastructure Licensing has to be in '' its not working with "" because of the blank char

Created on Jun 14, 2019 5:44:49 PM

Last change on Jun 17, 2019 5:32:07 AM by  Stephan Linke [Paessler Support]



Votes:

0

Unfortunately, we have had a bug in PRTG for quite some time now that causes PRTG to trip when @ characters are passed via command line. It would require refactoring of the script to adapt it. The easiest way is to save the script once per host and enter the IDs manually.

The refactoring would require to turn the int[] parameters into strings and have them passed as "1,2,3" instead of @(1,2,3). They need to be converted into arrays within the script afterwards.

Sorry for the inconvenience regarding this :( Arne pushed the bugfix within our issue tracker, but it has a rather low priority.

Created on Jun 17, 2019 6:39:15 PM by  Stephan Linke [Paessler Support]



Votes:

0

I did try that powershell script but had a lot of trouble getting it to work. I tried to keep things simple by trying to get some results from it on just the PRTG server in powershell, but can't get it to work.

E.g.

.\Get-Events.ps1 -ComputerName StudySvr8 -Username administrator -Password xxxxxxxx -Channel Application -ProviderName 'Microsoft-Windows-CertificateServicesClient-AutoEnrollment' -EventID 64

Obviously I have replaced the password with X's in the above example. This produces the output :-

00:No log entries found

There are definitely entries for event 64 in the application log. I have tried it with other event ID's. Some give red error text, others just the same as the above. Some red error text, plus the No log entries found.

The only one I have managed to get to work in terms of an actual result is this :-

\Get-Events.ps1 -ComputerName StudySvr8 -Username administrator -Password xxxxxxx -Channel System -ProviderName 'Service Control Manager' -EventID 7036

This tells me :- 10:10 log entries found in the last 20 hours

Does this mean 10 with that event ID in the last 20 hours? If so, there are way more than 10 in that time period.

Running on a fully updated copy of Server 2012 R2.

Hopefully I'm making some kind of simple mistake and someone can point me in the right direction.

Created on Aug 6, 2019 4:43:44 PM



Votes:

0

Can you please try to replace line #158 in the script with the following:

{ 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",$ProviderName -join " or ",$Channel -join " or ")) -ExitCode 1 }

It should provide you with more debug information regarding the matter (i.e. not only no entries found). Let me know the output :)

Created on Aug 7, 2019 12:07:27 PM by  Stephan Linke [Paessler Support]



Votes:

0

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;

Created on Nov 13, 2019 8:49:02 PM

Last change on Nov 13, 2019 8:49:02 PM



Votes:

1

Sorry to send along an update the very next day, but I noticed some issues after running the script for a bit and I wanted to clean it up a bit. Also, I forgot to mention a BIG change where I changed the MaxAge parameter from hours to MINUTES. Also, I updated the comments / help output to match the new parameter names. Removed a bunch of commented out debug statements. Put back the stripping of the \n\r characters from the message(s) because Powershell 2.0 doesn't like them. Added the -ErrorAction Stop parameter to the Get-WinEvent call so I could trap the non-fatal, "No matching events" exception because it was making PRTG cranky, which may only be an issue with Powershell 2.0 (again), but it's probably a good idea to trap this exception anyway.

#___ ___ _____ ___
#| _ \ _ \_   _/ __|
#|  _/   / | || (_ |
#|_| |_|_\ |_| \___|
#    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.3     11/14/2019    Todd Piket      Updated the comments / help output to match the new parameter names.  Forgot
#                                       to mention I changed the MaxAge parameter from hours to MINUTES.  Removed a
#                                       bunch of commented out debug statements.  Put back the stripping of the
#                                       \n\r characters because Powershell 2.0 doesn't like them.
#                                       Add the -ErrorAction Stop parameter to the Get-WinEvent call so I could trap
#                                       the non-fataln "No matching events" exception because it was making PRTG cranky.
#                                       Maybe this is only an issue with Powershell 2.0 (again), but it's probably a good
#                                       idea to trap this exception anyway.
# 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 LogNames
   The log name that is used by the application 
   .PARAMETER ProviderNames
   The application(s) that you want to watch
   .PARAMETER EventIDs
   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.  See MS for Log Level enumeration.
   .PARAMETER MaxAge
   How far back to look in the log 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. Separate multiple strings with comma
   .PARAMETER ErrorStrings
   Put the sensor into a error state when a certain string is found within the message. Separate multiple strings with comma
   .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 length specified by the
   TruncateMessageLength parameter.  If ShowAllMessages is specified, then EACH MESSAGE FOUND will be truncated to the
   length specified.
   .PARAMETER TruncateMessageLength
   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]$LogNames         = "",
    [string]$ProviderNames    = "",
    [string]$EventIDs         = "",
    [string]$WarningEvents    = "",
    [string]$ErrorEvents      = "",
    [string]$ErrorStrings     = "",
    [string]$WarningStrings   = "",
    [string]$Levels           = "",
    [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))}
}
if (! [string]::IsNullOrEmpty($LogNames))
{
    $arLogNames = $LogNames.split(",")
}
if (! [string]::IsNullOrEmpty($ProviderNames))
{
    $arProviders = $ProviderNames.split(",")
}
if (! [string]::IsNullOrEmpty($ErrorEvents))
{
    $arErrorIDs = foreach ($number in $ErrorEvents.split(",")) {([int]::parse($number))}
}
if (! [string]::IsNullOrEmpty($WarningEvents))
{
    $arWarnIDs = foreach ($number in $WarningEvents.split(",")) {([int]::parse($number))}
}
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 -ErrorAction Stop) }
        else                        { $Events = (Get-WinEvent -ComputerName $ComputerName -FilterHashTable $EventFilter -MaxEvents $LimitEntries -ErrorAction Stop) }
     
        return $Events
    }
    catch [Exception]
    {
        if ($_.Exception -match "No events were found that match the specified selection criteria")
        {
            This-PrtgResult -Message ([string]::Format("No events were found that match the specified selection criteria")) -ExitCode 0
        }
        else
        {
            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)))
                {
                    if ($TruncateMessage)
                    {
                        $LastMessage = ($EventList[0].Message.substring(0, [System.Math]::Min($TruncateMessageLength, $EventList[0].Message.Length)) + "..." -replace "`n|`r")
                    }
                    else
                    {
                        $LastMessage = ($EventList[0].Message) -replace "`n|`r"
                    }
                    $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)))
                    {
                        if ($TruncateMessage)
                        {
                            $Message = "$Message" + ($EventList[$i].Message.substring(0, [System.Math]::Min($TruncateMessageLength, $EventList[$i].Message.Length)) + "..." -replace "`n|`r")
                        }
                        else
                        {
                            $Message = "$Message" + ($EventList[$i].Message) -replace "`n|`r"
                        }
                    }
                    $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;

Created on Nov 14, 2019 5:18:16 PM

Last change on Nov 14, 2019 5:46:36 PM by  Stephan Linke [Paessler Support]



Votes:

0

@toddpiket Updated, thanks! You might want to use {.and.} (wthout the dots instead of <pre> to format it properly :)

Created on Nov 14, 2019 5:48:36 PM by  Stephan Linke [Paessler Support]



Votes:

0

@Stephan You are very welcome! I realized that I could edit my previous reply after I sent a new reply. So there are now several revisions there. Should I send a new reply with, yet another, updated version? The Powershell 2.0 issues keep coming, but I think I have them all dealt with now. There was an issue converting the integer strings to arrays that I just fixed. I think that is the last one. I already updated the previous post with the final revision. Let me know if you want me to reply again with the final update.

Created on Nov 14, 2019 6:35:12 PM



Votes:

0

@toddpiket
Editing your latest code post should do the trick :) Thanks!

Created on Nov 15, 2019 5:58:43 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hello, I just implemented this script and it works. The only problem I have is that the sensor default state is warinng with the script result of 0. I query applocker events with ID 8004. So if no events with this id are found the result is 0 and this is warning in my console. So I would like #0 = green - ok and >0 warning or error - notok

How can I achieve this? Best regards

Created on Apr 30, 2020 12:00:05 PM



Votes:

0

You'd need to update the lines with This-PrtgResult and modify the -ExitCode at the end to match what you need :) You can of course also set the error code to 0 always and work with limits in the channel gauge, by clicking the small gear.

Created on May 1, 2020 8:56:11 AM by  Stephan Linke [Paessler Support]



Votes:

0

Thank you very much! Regards

Created on May 7, 2020 9:48:59 AM



Votes:

0

I'm trying to implement this, and I can get some output from the script, but it doesn't set the warning og error status as intented. I'm trying to monitor scheduled tasks on a server, and is looking for certain ID's. These are the parameters I use:

-ComputerName %host -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -ProviderNames "Microsoft-Windows-TaskScheduler" -LogNames "Microsoft-Windows-TaskScheduler/Operational" -LimitEntries 1 -MaxAge 120 -EventIDs "101,135,203,311,322,329" -Levels "0,1,2,3,4,5,6" -WarningEvents 322 -ErrorEvents "101,135,203,311,329" -ShowAllMessages

It finds the any ID's as expected, but when it finds an event 203 it doesn't set the alarm status, but just reports that an event has been found. I also tried -ErrorStrings "failed" but it also ignores that one. What am I doing wrong?

Created on Jun 8, 2020 11:31:53 AM

Last change on Jun 8, 2020 3:49:31 PM by  Birk Guttmann [Paessler Support]



Votes:

0

Does the Sensor message show any indications of an error being found, i.e. something like: Warning Event found: 203

Or does it simply state that an event has been found?

Created on Jun 9, 2020 7:25:14 AM by  Stephan Linke [Paessler Support]



Votes:

0

I'm having the same problem when trying to implement. I get a response from the script, but it doesn't change the sensor state to error or warning status as intended. I'm looking for certain ID's to monitor if one of our applications is successfully syncing or not. The script finds the ID's as expected and the sensors message shows:

32 event(s) found

which is the combined results from the IDs. When it finds an event 256 or 258 it doesn't set the alarm status and stays in an "OK" state. I'm not sure what I am doing wrong but I think the issue is with my parameters. These are the parameters I use:

-ComputerName "%host" -Username "%windowsdomain\%windowsuser" -Password "%windowspassword" -LogNames "Application" -MaxAge 120 -EventIDs "256,258,322" -Levels "0,1,2,3,4,5,6" -WarningEvents 258 -ErrorEvents "256"

Created on Aug 21, 2020 4:35:38 PM



Votes:

0

You'd need to use -WarningEvents "258" (as a String, instead of a number) to make this work. Can you verify that? :)

Created on Aug 25, 2020 8:05:42 AM by  Stephan Linke [Paessler Support]



Votes:

0

I changed -WarningEvents to a string and received the same results.

Created on Aug 25, 2020 7:19:47 PM



Votes:

0

Could you pass -StateBasedOnLastEventID as parameter as well? Only then, it will likely throw a warning.

Created on Aug 27, 2020 11:27:41 AM by  Stephan Linke [Paessler Support]




Disclaimer: The information in the Paessler Knowledge Base comes without warranty of any kind. Use at your own risk. Before applying any instructions please exercise proper system administrator housekeeping. You must make sure that a proper backup of all your data is available.