Advanced Event Viewer filters - powershell

Exist the possibility to extract some information from Event viewer about event id description via powershell?
For example i want to see if I have on domain controller this event id 4776 with description "Authentication Package: WDigest".

You can use Get-WinEvent's -FilterXPath parameter to very granularly filter against the raw event xml.
In your case we could find all WDigest auth events with:
Get-WinEvent -FilterXPath "*[System[EventID=4776] and EventData[Data[#Name='PackageName']='WDigest']]" -LogName "Security"

It would be easier with the logname or providername, but it's possible to search all the logs, unlike in the event viewer. You can't just say '*' for the logname, because of a 256 logname query limit in the windows api:
get-winevent -listlog * |
foreach {
get-winevent #{ logname = $_.logname; id = 4776 } -ea 0 } |
where message -match 'Authentication Package: WDigest'
In powershell 6 or 7 you can filter on named eventdata:
get-winevent #{ Logname = 'Security'; ID = 4776; PackageName = 'WDigest' }
In powershell 5 maybe this. Data can't have wildcards.
get-winevent #{ Logname = 'Security'; ID = 4776; Data = 'WDigest' }

Related

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 = ... }

How to return filtered event log entries for TaskDisplayName = 'Boot Performance Monitoring' using Get-WinEvent in PowerShell

The following code works to return the Windows Log events with ID = 100.
$Date = (Get-Date).AddDays(-30)
Get-WinEvent -FilterHashTable #{ LogName = "Microsoft-Windows-Diagnostics-Performance/Operational"; StartTime = $Date; ID = 100 } -MaxEvents 1 | Select-Object -Property TimeCreated, Id, Task, TaskDisplayName, LevelDisplayName, Message
This code returns an error for the TaskDisplayName = 'Boot Performance Monitoring'
$Date = (Get-Date).AddDays(-30)
Get-WinEvent -FilterHashTable #{ LogName = "Microsoft-Windows-Diagnostics-Performance/Operational"; StartTime = $Date; TaskDisplayName = 'Boot Performance Monitoring' } | Select-Object -Property TimeCreated, Id, Task, TaskDisplayName, LevelDisplayName, Message
Get-WinEvent : No events were found that match the specified selection criteria.
At D:\tfsws\TG-Dev-ICSG2\Support\PowerShell Scripts\Get-WinEvent-TEST.ps1:6 char:1
+ Get-WinEvent -FilterHashTable #{ LogName = "Microsoft-Windows-Diagnos ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-WinEvent], Exception
+ FullyQualifiedErrorId : NoMatchingEventsFound,Microsoft.PowerShell.Commands.GetWinEventCommand
How do I make Get-WinEvent accept the TaskDisplayName filter?
Unfortunately you can't use -FilterHashTable to filter on TaskDisplayName for 2 reasons:
In the Microsoft docs Get-WinEvent valid FilterHashTable values are:
LogName=<String[]>
ProviderName=<String[]>
Path=<String[]>
Keywords=<Long[]>
ID=<Int32[]>
Level=<Int32[]>
StartTime=<DateTime>
EndTime=<DataTime>
UserID=<SID>
Data=<String[]>
TaskDisplayName isn't one of the -FilterHashTable options.... ok. So next option is to use -FilterXPath or -FilterXML which gives us access to some more lower level filtering. For simplicity, I will use -FilterXPath. In order to find the right keys to filter on, you have to go to the details tab on the event. Here is a sample event:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Microsoft-Windows-Diagnostics-Performance" Guid="{CFC18EC0-96B1-4EBA-961B-622CAEE05B0A}" />
<EventID>100</EventID>
<Version>2</Version>
<Level>2</Level>
<Task>4002</Task>
<Opcode>34</Opcode>
<Keywords>0x8000000000010000</Keywords>
<TimeCreated SystemTime="2018-05-23T21:09:42.047563100Z" />
<EventRecordID>8</EventRecordID>
<Correlation ActivityID="{F774E0CC-F2D9-0006-E0FA-74F7D9F2D301}" />
<Execution ProcessID="3876" ThreadID="4980" />
<Channel>Microsoft-Windows-Diagnostics-Performance/Operational</Channel>
<Computer>D4700.adcfcu.connectfirstcu.com</Computer>
<Security UserID="S-1-5-19" />
</System>
<EventData>
<Data Name="BootTsVersion">2</Data>
<Data Name="BootStartTime">2018-05-23T21:06:49.733345200Z</Data>
..........
<Data Name="UserLogonWaitDuration">9415</Data>
</EventData>
</Event>
When you expand it out, you notice that there is no TaskDisplayName. This is because TaskDisplayName == Task Category. Ok... let's look for Task Category... Well there is no Task Category either. That's because Categories are actually stored numerically in the event, and then mapped into a proper description using an Event Category String. That is why you can't filter based on a TaskDisplayName or Task Category. Instead you will have to filter on the Task number, which you in this case is 4002. And if you use the StartDate, which is TimeCreated, you can calculate that 30 days is 2592000000 miliseconds, then your code becomes:
Get-WinEvent -LogName "Microsoft-Windows-Diagnostics-Performance/Operational" -FilterXPath "*[System[Task=4002 and TimeCreated[timediff(#SystemTime) <= 2592000000]]]" | Select-Object -Property TimeCreated, Id, Task, TaskDisplayName, LevelDisplayName, Message
As stated in the Documentation, the -FilterHashTable only allows a subset of filters:
LogName=
ProviderName=
Path=
Keywords=
ID=
Level=
StartTime=
EndTime=
UserID=
Data=
*=
What you can do instead, is to create a FilterXML with the -FilterXML parameter.
You can create an appropriate XML with eventvwr.msc (find and example event, get the task number from the details view, configure a protocol filter, switch to the "XML" tab, and copy the content).
The result will be something like this:
$Filter = #'
<QueryList>
<Query Id="0" Path="Microsoft-Windows-Diagnostics-Performance/Operational">
<Select Path="Microsoft-Windows-Diagnostics-Performance/Operational">*[System[(EventID=100) and (Task=4002)]]</Select>
</Query>
</QueryList>
'#
Get-WinEvent -FilterXml $Filter
TaskDisplayName however is not a valid property, you have to figure out, what Task number 'Boot Performance Monitoring' has.
Note, that Task 4002 is not the one you are looking for. It's just the only number that is available in my eventlog. ;)
Alterantively, but not as performant and not recommended (general rule: filter left), you can use your sample command and add an Where-Object filter.
$FitlerHashTable = #{
LogName = "Microsoft-Windows-Diagnostics-Performance/Operational"
StartTime = $Date
ID = 100
}
Get-WinEvent -FilterHashTable $FitlerHashTable -MaxEvents 1 |
Where-Object { $_.Task -eq 4002 } |
Select-Object -Property TimeCreated, Id, Task, TaskDisplayName, LevelDisplayName, Message

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

Select Some Strings From Event message

I am trying to pull all username,time created and public ip address from
Microsoft-Windows-TerminalServices-Gateway/Operational event.
I get all the events from messages with below command but I need only username,and ip
get-winevent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303}
Trying with this No luck
get-winevent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303}| select timecreated -expand Message [regex]::Match($event, 'user:\s*(.*)\s*').Groups[1].Value
Ok, I can see what you're trying with the Select command, and while it sounds good, that doesn't quite work. What you could do instead is pass it through a Where statement matching the string you need, and capturing the relevant data in a RegEx match, then using Select to add those fields onto the object.
So a Where match that would work for you should be:
| Where{$_.Message -match '"(.+?\\.+?)".+"(\d+\.\d+\.\d+\.\d+)"'}
Then you pipe to Select and build the User and IPAddress properties on the fly like this:
| Select TimeCreated,#{l='User';e={$Matches[1]}},#{l='IPAddress';e={$Matches[2]}}
Then you put it all together and you get:
get-winevent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303} | Where{$_.Message -match '"(.+?\\.+?)".+"(\d+\.\d+\.\d+\.\d+)"'} | Select TimeCreated,#{l='User';e={$Matches[1]}},#{l='IPAddress';e={$Matches[2]}}
Using that with some randomized values I was able to output:
TimeCreated User IPAddress
----------- ---- ---------
9/14/2016 9:47:29 AM DOMAIN\dhxrjqb 216.229.149.87
9/14/2016 9:47:29 AM DOMAIN\fkoilrh 236.65.23.77
9/14/2016 9:47:29 AM DOMAIN\mvibope 20.7.45.231
Ok, the solution to the issue that you noted in your comment is a minor tweak to the RegEx match to make one portion not greedy. This would do that:
| Where{$_.Message -match '"(.+?\\.+?)".+?"(\d+\.\d+\.\d+\.\d+)"'}
and all together again...
get-winevent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303} | Where{$_.Message -match '"(.+?\\.+?)".+?"(\d+\.\d+\.\d+\.\d+)"'} | Select TimeCreated,#{l='User';e={$Matches[1]}},#{l='IPAddress';e={$Matches[2]}}
I think there's a better approach using the ToXml() Method on the EventLogRecord Object:
First Catch the Events:
$Events = Get-WinEvent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303}
Then, Take for example the first item in the array and convert it to XML:
[xml]$Event = $Events[0].ToXml()
Now you can see all the Information you need:
$Event.Event.UserData.EventInfo
Update: Set it for you, this should do the work:
$Events = Get-WinEvent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303}
$ArrayList = New-Object System.Collections.ArrayList
Foreach ($Event in $Events)
{
[xml]$Xml = $Event.ToXml()
$Row = "" | Select Username,TimeCreated,IPAddress
$Row.Username = $Xml.Event.UserData.EventInfo.Username
$Row.TimeCreated = $Event.TimeCreated.ToString()
$Row.IPAddress = $Xml.Event.UserData.EventInfo.IpAddress
[void]$ArrayList.Add($Row)
}
$ArrayList