Use PowerShell Cmdlet to Filter Event Log for Easy Parsing

Use PowerShell Cmdlet to Filter Event Log for Easy Parsing

  • Comments 11
  • Likes

 

Summary: Learn how to use the Get-WinEvent Windows PowerShell cmdlet to filter the event log prior to parsing it.

Hey, Scripting Guy! Question Hey, Scripting Guy! I am confused. I have enjoyed using the Get-EventLog Windows PowerShell cmdlet. It is fast, and easy to use. However, I do not always like the way it seems to return all the records from a remote computer before I can parse it with the Where-Object cmdlet. Why does it not support a –filter parameter?

-- RD

Hey, Scripting Guy! Answer Hello RD,

Microsoft Scripting Guy Ed Wilson here. After SQL Saturday in Tampa, Florida, Dr. Scripto and I decided to head to the beach so we could play a little volleyball. The following action shot captures Dr. Scripto in peak beach volleyball form.

Image of Dr. Scripto in action on beach

Speaking of things that seem to bounce around, Windows PowerShell 2.0 introduces a new cmdlet to permit filtering of an event log prior to returning it to the workstation for additional parsing. I will admit that the Get-EventLog Windows PowerShell cmdlet is extremely easy to use. In Windows PowerShell 2.0, it even has a computername parameter that provides easy access to remote event logs. There are a couple of problems with the Get-EventLog cmdlet. The first is that it must first return the log entries before they can be parsed with the Where-Object cmdlet. When working on a local computer, this might not be a huge issue, but when connecting across the network, it is better to filter the events at the remote computer before returning them to the local machine. The second issue with the Get-EventLog cmdlet is that it does not allow one to query the ETL type of logs; it is limited to the so-called “traditional event logs” such as the system, application, and security logs. The last problem with Get-EventLog is it is limited to online logs. When the event log is archived, Get-EventLog cannot access it. All of these problems are addressed with the Get-WinEvent cmdlet.

Unfortunately, Get-WinEvent is not as easy to use as the Get-EventLog cmdlet. Confusion with Get-WinEvent begins with its name—it sounds as if it would have something to do with Windows events such as a shutdown event instead of event logs. It extends to the use of hash tables for filter mechanisms, and concludes with reading the event logs backward. But do not let these quirks scare you away from using an extremely powerful cmdlet.

If I want to look at the most recent event from the application log on my computer, I use the logname parameter and specify a value for maxevents. In the example that follows, I use the number 1 to retrieve the most recent event in the application log (1 maximum event is the most recent event). I illustrate this technique with the code that appears here.

PS C:\> Get-WinEvent -LogName application -MaxEvents 1

TimeCreated                     ProviderName                       Id          Message

-----------                            ------------                              --          -------

1/16/2011 11:41:11 AM    Windows Error Reporting     1001      Fault bucket , type 0...

PS C:\>

If I want to see all of the entries in a particular event log (or ETL log), I do not specify a value for maxevents. An example of doing that is shown here:

PS C:\> Get-WinEvent -LogName system

TimeCreated                         ProviderName                       Id           Message

-----------                                ------------                              --           -------

1/16/2011 12:00:00 PM         EventLog                             6013      The system uptime is ...

1/16/2011 11:47:53 AM         Service Control Manager     7036       The Application Exper...

1/16/2011 11:47:20 AM         Microsoft-Windows-Tim...     129         NtpClient was unable ...

1/16/2011 11:43:34 AM         Service Control Manager      7036       The Multimedia Class ...

<output truncated>

When I use a hash table to filter events prior to returning them, the power of the cmdlet begins to show. A hash table uses the syntax of key = value, and each key must be unique. If I use the FilterHashTable parameter, I am not able to supply a value for the LogName parameter. I discovered this by examining the parameter sets that appear in the Get-Help Get-WinEvent help topic. The two applicable parameter sets appear here:

Get-WinEvent [-LogName] <string[]> [-ComputerName <string>] [-Credential <PSCredential>] [-Filt

erXPath <string>] [-Force <switch>] [-MaxEvents <int64>] [-Oldest] [<CommonParameters>]

Get-WinEvent -FilterHashTable <Hashtable[]> [-ComputerName <string>] [-Credential <PSCredential

>] [-Force <switch>] [-MaxEvents <int64>] [-Oldest] [<CommonParameters>]

What this means is that I must include the log name in my hash table filter when I execute the command. The following table details the permitted key names and the associated data type of that associated value. Only two of the value fields accept wildcard characters. The * key represents a named event data field.

Key name

Value data type

Accepts wildcard characters?

LogName

<String[]>

Yes

ProviderName

<String[]>

Yes

Path

<String[]>

No

Keywords

<Long[]>

No

ID

<Int32[]>

No

Level

<Int32[]>

No

StartTime

<DateTime>

No

EndTime

<DataTime>

No

UserID

<SID>

No

Data

<String[]>

No

*

<String[]>

No

Suppose I am concerned with events that are coming from Capi2 with the EventID of 4107. When developing these types of filters, I always like to look at the actual event log record so that I can test my results to ensure the accuracy of my efforts. The particular event log entry I am interested in obtaining is shown in the following image.

To use the Get-WinEvent cmdlet to query the application log for event ID 4107, I create a hash table that will be supplied to the FilterHashTable parameter. The key names (from the table above) do not need to be placed in quotation marks. The value data types that are listed as String or SID will need the quotation marks around it. The ID data type is an int32 and therefore does not need quotation marks around it. The resulting command appears here, along with the associated output.

PS C:\> Get-WinEvent -FilterHashtable @{logname='application'; id=4107}

TimeCreated                           ProviderName                       Id             Message

-----------                                 ------------                               --             -------

1/17/2011 8:18:27 AM            Microsoft-Windows-CAPI2    4107        Failed extract of thi...

1/16/2011 11:32:02 AM          Microsoft-Windows-CAPI2    4107        Failed extract of thi...

1/15/2011 9:23:30 AM            Microsoft-Windows-CAPI2    4107        Failed extract of thi...

<truncated output>

If I want to limit the output to events from the current day, I add the StartTime key. The StartTime key expects a datetime object. I therefore use the Get-Date Windows PowerShell cmdlet to retrieve the current date and time. I then specify that I only want today’s date from the cmdlet. I do this by placing parentheses around the Get-Date cmdlet, and then using dotted notation to retrieve only the date property from the System.DateTime object that is returned by Get-Date. I knew which properties were available from the System.DateTime object because I used the Get-Member cmdlet to examine the properties. This is illustrated here:

PS C:\> get-date | Get-Member -MemberType property

TypeName: System.DateTime

Name                     MemberType                           Definition

----                         ----------                                   ----------

Date                       Property                                  System.DateTime Date {get;}

Day                         Property                                  System.Int32 Day {get;}

DayOfWeek            Property                                   System.DayOfWeek DayOfWeek {get;}

DayOfYear              Property                                   System.Int32 DayOfYear {get;}

Hour                       Property                                   System.Int32 Hour {get;}

Kind                        Property                                   System.DateTimeKind Kind {get;}

Millisecond              Property                                   System.Int32 Millisecond {get;}

Minute                    Property                                   System.Int32 Minute {get;}

Month                     Property                                   System.Int32 Month {get;}

Second                   Property                                   System.Int32 Second {get;}

Ticks                       Property                                   System.Int64 Ticks {get;}

TimeOfDay              Property                                    System.TimeSpan TimeOfDay {get;}

Year                        Property                                    System.Int32 Year {get;}

Next, I tested the command to ensure it returned what I expected. This technique is shown here:

PS C:\> (get-date).date

Monday, January 17, 2011 12:00:00 AM

PS C:\>

After I had confirmed that the command worked as I expected, I incorporated it into my previous hash table filter. This is shown here, along with the associated output:

PS C:\> Get-WinEvent -FilterHashtable @{logname='application'; id=4107; StartTime=(Get-Date).date}

TimeCreated                        ProviderName                       Id              Message

-----------                              ------------                               --              -------

1/17/2011 8:18:27 AM        Microsoft-Windows-CAPI2      4107         Failed extract of thi...

PS C:\>

I then decided to try an experiment. Because the StartTIme key expects a DateTime data type, I wondered if I could give it a string and have it cast it to a System.DateTime object for me. I knew that I could do something like the following command and that I would receive a DateTime object:

PS C:\> [datetime]"1/17/11"

Monday, January 17, 2011 12:00:00 AM

PS C:\> ([datetime]"1/17/11").GetType()

IsPublic              IsSerial      Name              BaseType

--------                --------        ----                  --------

True                   True           DateTime         System.ValueType

PS C:\>

However, I was not certain that Windows PowerShell was smart enough to perform the conversion for me. Therefore, I decided to test it out. As you can see, the command worked out great:

PS C:\> Get-WinEvent -FilterHashtable @{logname='application'; id=4107; StartTime="1/17/11"}

TimeCreated                    ProviderName                      Id          Message

-----------                           -----------                              --           -------

1/17/2011 8:18:27 AM     Microsoft-Windows-CAPI2   4107      Failed extract of thi...

PS C:\>

When I had the previous command working, I decided to see if I could use the same technique to retrieve a range of records. To do this, I used both the StartTime key and the EndTime key. The command and associated output appear here:

PS C:\> Get-WinEvent -FilterHashtable @{logname='application';id=4107;StartTime="1/15/11";EndTime="1

/17/11"}

TimeCreated                    ProviderName                        Id          Message

-----------                           ------------                               --          -------

1/16/2011 11:32:02 AM   Microsoft-Windows-CAPI2     4107     Failed extract of thi...

1/15/2011 9:23:30 AM     Microsoft-Windows-CAPI2     4107     Failed extract of thi...

1/15/2011 8:28:07 AM     Microsoft-Windows-CAPI2     4107     Failed extract of thi...

1/15/2011 8:27:32 AM     Microsoft-Windows-CAPI2     4107     Failed extract of thi...

PS C:\>

RD, that is all there is to using the Get-WinEvent cmdlet. Neglected Cmdlet Week will continue tomorrow when I will talk about using the Get-WinEvent cmdlet to query offline event logs.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson, Microsoft Scripting Guy

 

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Great post as always, thanks for the information.  One question on the EventLogRecord object that gets returned from Get-WinEvent.  Is there a way to retrieve the "replacement strings" (msdn.microsoft.com/.../system.diagnostics.eventlogentry.replacementstrings.aspx) similar to what you get back from Get-EventLog and the EventLogEntry class?  Replacement strings make it much easier to pull information from the Message field of the event, without a ton of RegEx or other string manipulation.

    Thanks!

    Steve

  • @Steve - I am glad you enjoyed the post. The Get-WinEvent cmdlet returns an instance of the Diagnostics.Eventing.Reader.EventLogRecord which does not expose the replacement strings. However, the Get-Eventlog returns an instance of Diagnostics.EventLogEntry that does expose the replacement strings. So unfortunately, if you wish to parse replacement strings you will need to use the older Get-EventLog cmdlet. Great question by the way.

  • Thanks for the quick feedback.  After a little poking around after my last post, I noticed that Diagnostics.Eventing.Reader.EventLogRecord does expose a ToXML method (msdn.microsoft.com/.../system.diagnostics.eventing.reader.eventlogrecord.toxml.aspx).  Going from there and using some of your handy earlier XML posts...

    $Event = Get-WinEvent -log Security -max 1

    [XML] $EventXML = $Event.ToXML()

    $eventXML.Event.EventData.Data

    That at least can net you some of the similar information you get out of the replacement string, and without playing around with strings TOO much.  Although, I would gladely take any other examples from others!  :)

    Thanks again

    Steve

  • Thanks for the helpful post and comments. I did however discover two things that should be mentioned:

    * –FilterHashtable doesn't work on Vista/Server 2008.  You'll need to use -FilterXML or -FilterXPath.

    * If you need to filter the Security log for Audit Failures, Get-EventLog is much faster than Get-WinEvent.

    I wrote this up here:  www.mcbsys.com/.../powershell-get-winevent-vs-get-eventlog.

    Mark

  • Seems really handle, and I actually need to do this shortly. I tried the Get-WinEvent command, but needed to freshen my .NET. I did, but still can't execute it. Any thoughts? Thanks.

    <got error below>

    <installed .NET 4.0>

    PS C:\Windows\system32> get-winevent

    Get-WinEvent : This cmdlet requires Microsoft .NET Framework version 3.5 or greater.

    At line:1 char:13

    + get-winevent <<<<

       + CategoryInfo          : NotInstalled: (:) [Get-WinEvent], FileNotFoundException

       + FullyQualifiedErrorId : GetEventDotNet35Required,Microsoft.PowerShell.Commands.GetWinEventCommand

    PS C:\Windows\system32> gci 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' | sort pschildname -des | select -fi 1 -e

    xp pschildname

    v4.0

    So PS sees .NET 4.0, but still doesn't work !!??

  • Hello Ed,

    Another great post!

    Have a question for you. I am working on a script to parse event logs for errors and warnings in the previous day. It works great, but the part I have not figured out is the Level. It returns the numeric value, but I want to see the text instead.

    For example:

    TimeCreated             Level     Message  

    12/20/2011  5:20:33 PM      2     Failed to connect to the...

    I would like to see:

    TimeCreated             Level     Message  

    12/20/2011  5:20:33 PM  Error     Failed to connect to the...

    The simple version of the script (minus server name parsing loop):

    $Day = (Get-Date).AddDays(-1)

    $Events = Get-WinEvent -FilterHashTable @{LogName="Application"; StartTime=$Day; Level=2,3}  -ComputerName <ServerName> -ErrorAction SilentlyContinue

    $Events | Select TimeCreated,Level,Message | Export-CSV C:\Test\Test.csv

    I've been trying array's, hashtables, etc., but have not figured it out yet. Have any suggestions?

    Thanks,

    Steve

  • The loss of the replacementstrings property seems like a real loss. If you archive event logs, I don't see a way to really parse the message piece of the event (where a lot of the critical information exists). If you have a year worth of security logs that you archived and need to search beyond the basic fields the loss of the replacementstrings property is going to set you back a long way as far as I can tell...

  • @OldguardMD

    Replacementstrings has not been lost.  The name has been changed is all.

    Get-Winevent -FilterHashtable @{logname='application';id=4107} | select -expand properties

    'Properties' is an array of replacement values.

  • Hye... Great Post...I have question, how to filter only log with error message appear. In Get-EventLog for example have code like this: $_.EntryType -eq "Error" ... how about in Get-WinEvent? TQ in advanced.

  • Very useful information on Get-WinEvent - thanks! Up to this point I've been using Get-EventLog and invoking the command on the remote host. I believe that this means all the filtering is done on the remote host(s) prior to kicking the information back to my local machine. Is that correct? Example: PS C:\> Invoke-Command -ScriptBlock {Get-EventLog -LogName Application -EntryType Error -Source "MSExchangeIS" | where {$_.EventID -eq 9646} } -ComputerName host01,host02,host03

  • Hi This is awsome!
    But i need to extract only erroneous application event i.e. which are showing in red (Level = Error) by using powershell? Could you please help??