Powershell, -filterhashtable, and operators - powershell

I'm filtering event log entries using the "Get-Winevent" cmdlet. I want to get events whose levels are less than 4 (or where LevelName isn't "Informational").
I use the -filterhashtable flag to filter the events. But is there a way to do comparisons with filterhashtable? Or just put a "not"? Or does filterhashtable only accept "=" as an operator?
These two snippets work and get the same results:
where-object
$events = Get-WinEvent -computer ServerName -LogName System | Where-Object {$_.level -lt 4}
-filterhashtable
$events = Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level = 1}
$events += Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level = 2}
$events += Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level = 3}
The second snippet runs much faster than the first snippet (2 minutes versus 16 seconds in one case). As I understand it, "where-object" has to wait until "Get-WinEvent" has gotten every event object (possibly thousands). Adding "-filterhashtable" causes the target system's event log to filter before it gives the event object ot Get-WinEvent, which is much faster.
Can I combine the statements? These snippets don't work:
$events = Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level < 4}
$events = Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level != 2}
The "Level" properties is type "int[32]" so a comparison operator should work. In fact, it does work with "where-object". But it doesn't work with the "-filterhashtable" flag. Is there no way to do that sort of comparison? Is "=" the only operator -filterhashtable accepts?

No dice on operators like that. The FilterXPath parameter supports that. However the help on the FilterHashtable parameter indicates it takes an array of int, so it would accept:
... -FilterHashtable #{LogName='System';Level=0,1,3}

No, you cant. A hashtable is a collection of key = value pairs, so it won't allow relational operators.
Btw, in Powershell < is -lt and > is -gt.

Related

Apply conditional to output of a commandlet

I want to output the result of the commandlet (Invoke-Command) on success and add a custom message if the result is null. The code as shown below produces the desired results except in the event of a null response, it simply outputs nothing on that line.
I can not pipe directly to an if statement, nor can I output on 2 opposing conditions (True & False). Is it possible to get a custom response on $null while not suppressing the normal output on success?
Invoke-Command -ComputerName PC1, PC2, PC3 -Scriptblock {get-eventlog system | where-object {$_.eventid -eq 129} | select MachineName, EventID, TimeGenerated, Message -last 1}
If you run the example code block assuming that PC1 and PC3 have the event ID but PC2 does not, the output will simply skip PC2.
I want to output something like "Event Not found" in that case.
Placing the entire thing in a loop and then running the results through another conditional loops destroys performance so that is not an ideal solution.
I would create a new object for returning from Invoke-Command. So you are sure you will receive from every host something even the event log is not present. And might you can change get-eventlog to Get-WinEvent. Get-WinEvent was for my tasks the most time faster than get-eventlog.
[System.Management.Automation.ScriptBlock]$Scriptblock = {
[System.Collections.Hashtable]$Hashtable = #{
WinEvent = Get-WinEvent -FilterHashtable #{ LogName = 'System'; Id = 129 } -MaxEvents 1 -ErrorAction SilentlyContinue #-ErrorAction SilentlyContinue --> otherwise there is an error if no event is available
}
return (New-Object -TypeName PSCustomObject -Property $Hashtable)
}
Invoke-Command -ComputerName 'PC1', 'PC2', 'PC3' -Scriptblock $Scriptblock

powershell get-eventlog add if more than 5 times

I am using:
"%windir%\system32\WindowsPowerShell\v1.0\powershell.exe" $log=Get-EventLog -LogName Security -InstanceID 4625 -After (Get-Date).AddDays(-60); if (($log)) {Write-Output $log[0].Message} ELSE {Write-Output 'WARNING-NoEventFound'}
This works perfect for me. I want to expand if possible and say write the output if the event happened more than 5 times. Similar to:
Count(*) > 5 that I would use in SQL.
I'd like to mention an alternative to Get-EventLog: Get-WinEvent
It usually has a lot better performance, both locally and over the network, it can do server side filtering with -FilterHashTable before sending the results. This can come in handy since Active Directory logs can be quite large sometimes.
Since you're only interested in if it's >5 results or not, we can also speed it up by breaking early when we have found 6 results, using -MaxEvents, and then just check whether we found 6 events or not.
$maxEvents = 6
$filterHashtable = #{
LogName = 'Security'
Id = 4625
StartTime = (Get-Date).AddDays(-60)
}
$log = Get-WinEvent -FilterHashtable $filterHashtable -MaxEvents $maxEvents
if ($log.Count -ge $maxEvents) {
#your code here
For readability I prefer to have the hashtable in a variable, but it can also be written inline like this, with ; as separator for the key value pairs:
Get-WinEvent -FilterHashtable #{ LogName = 'Security'; Id = ... }

Why do I get different results with Get-WinEvent versus Get-EventLog?

I'm trying to use Get-WinEvent to retrieve events from the eventlog for specific provider names that my company uses to write to the eventlog. I'm finding that I'm getting differing results depending on whether I use Get-WinEvent versus Get-EventLog, and I'm not sure why.
Using this test code (both provider names are proprietary names for different applications my company has):
$pName1 = "MagicFS6"
$pName2 = "MT_WPLAppServer"
$provider = $pName2
$fhash = #{
logname = 'application';
providername = $provider;
StartTime = '8/1/2017 12:00:00 AM'
}
$fhashevent = $null
$fhashevent = Get-WinEvent -FilterHashtable $fhash
$count = $fhashevent.Count
Write-Host "$provider had $count events using Get-WinEvent"
$eventlog = Get-EventLog -LogName Application -Source $provider -After '8/1/2017 12:00:00 AM'
$count = $eventlog.Count
Write-Host "$provider had $count events using Get-EventLog"
Running with $pName1 (MagicFS6), both Get-WinEvent and Get-EventLog return the same number of events. This tells me that the code is equivalent.
However, running with $pName2 (MT_WPLAppServer), Get-WinEvent returns 0 events, and Get-EventLog correctly returns thousands of results.
MagicFS6 had 12662 events using Get-WinEvent
MagicFS6 had 12662 events using Get-EventLog
MT_WPLAppServer had 0 events using Get-WinEvent
MT_WPLAppServer had 11483 events using Get-EventLog
For my needs, I need to use Get-WinEvent, so I would love some ideas on why this is not returning reliable results.

Get events from yesterday

I am trying to use PowerShell to get the results from the TaskScheduler events since yesterday. This is my code:
Get-WinEvent -LogName Microsoft-Windows-TaskScheduler/Operational -MaxEvents 5 |
Where-Object ($_.TimeCreated -gt [DateTime]::Today.AddDays(-1))
Format-List *
Notes:
The -MaxEvents 5 is to limit output while I am developing.
When I remove the Where-object the cmdlet returns a full list. This is expected since no filtering is applied. So the error must be in the way the filtering is being done.
You can use the FilterHashTable property of Get-WinEvent to filter, it will be faster than retrieving all the events and then filtering only those you want.
This retrieves all events in the last day from the System log as I don't have any logging for TaskScheduler.
$date = (Get-Date).AddDays(-1)
$events = Get-WinEvent -FilterHashTable #{ LogName = "System"; StartTime = $date;}
$events | Format-List
You can filter on pretty much any field in the event log - further info on this

Powershell search by single and multiple keyword

I have some commands below that do not give any output when looking for specific keywords in Windows Logs using PowerShell.
Get-WinEvent -FilterHashtable #{LogName="Application"} | Select-String "Information"
However, if I only run Get-WinEvent -FilterHashtable #{LogName="Application"}, there are many entries with Information keyword. Select-String -pattern "Information" also does not work.
Ideally I'd like to search for multiple keywords in the above scenario.
You need to do:
Get-WinEvent -FilterHashtable #{LogName="Application"} | ? { $_.leveldisplayname -eq 'Information' }
The Information you're looking for is a property of the object. The Get-WinEvent cmdlet returns a collection of objects, so you need to add the Where-Object or ? to filter on the LevelDisplayName object property.
To answer your new questions:
The leveldisplayname is going to be Information, Error or Warning. You can add either of these or use logic to combine them. In order to search for keywords in a message, using a regex is probably the best approach:
Get-WinEvent -FilterHashtable #{LogName="Application"} | ? message -imatch "keyword1"
To search multiple keywords, you can modify the regex using the OR | operator:
Get-WinEvent -FilterHashtable #{LogName="Application"} | ? message -imatch "keyword1|keyword2|foo|bar"
If you wanted to search for all Error messages containing "foo" or "bar" you could do;
Get-WinEvent -FilterHashtable #{LogName="Application"} | ? { ($_.message -imatch "foo|bar") -and ($_.leveldisplayname -eq 'Error') }