Windows Event Log in PowerShell - Part II

Windows Event Log in PowerShell - Part II

  • Comments 12

In part 1 of “Event logs in Powershell” we talked about differences between Get-EventLog and Get-WinEvent. In this second part we will dig deeper into Get-WinEvent.

 

Starting in Windows Vista, the Windows Event Log was updated to provide a more powerful event model which allows for events to be easily categorized into logs and for event providers to be easily discovered.  PowerShell V2 lets you search the Event Log for the data that's interesting to you, making it easier than ever to mine events from the Event Log. The new Get-WinEvent cmdlet provides access to all event logs on the system, including the legacy Event Logs.  Get-WinEvent cmdLet will be available on PowerShell V2 Running on Windows Vista and above (i.e. Windows Vista, Windows Vista SP1, Windows Server 2008, Windows 7 and Windows Server 2008 R2).  Get-WinEvent replaces the existing Get-EventLog cmdlet on these systems.

 

Getting Events from the Event Log

Getting events is easy with PowerShell. If I want to list all of the events in the system, I can run Get-WinEvent without any parameters:

> Get-WinEvent

The number of events can be excessively large, so be prepared to use ‘Ctrl-C’ to break out of it early. Since some logs are protected, you might see an error when running this command if you are not running PowerShell elevated as an Administrator.

Suppose you want to see the most recent 10 events which were logged. In PowerShell, I’d make a small modification:

> Get-WinEvent -MaxEvents 10

 The command above uses the default behavior to get events from every event log and uses the MaxEvents parameter to return only the most recent 10 events.

 If you wanted to see the events from a specific channel, such as the general “Application” and “System” channels, simply specify which logs you want to get events from:

> Get-WinEvent Application, System -MaxEvents 10

 

Discovering Logs, Providers and Events

Generally it is more interesting to find the specific events that you care about.  For that, we can use wildcards to find event providers and logs which we're interested in.

 

For example, if we're interested in what Windows Update has been doing on a particular machine, we can search for the Windows Update event provider:

PS C:\> Get-WinEvent -ListProvider *update*

Name                                                                        LogLinks

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

MCUpdate                                                                {Media Center}

Microsoft-Windows-WindowsUpdateClient      {System, Microsoft-Windows-WindowsUpdateClient/Operational}

 

Now that we know the name of the provider (Microsoft-Windows-WindowsUpdateClient), we can see the events that the provider has already logged to the Event Log:

> Get-WinEvent -ProviderName  Microsoft-Windows-WindowsUpdateClient

 

In some cases, it is more interesting to know what events the provider can potentially log instead of what it already has logged.  If we want to see the events that Windows Update can log in the future, we can take a deeper look at the provider: E.g to see the messages that will be logged with word “success” in the event message, we can use the following:

 

PS C:\> $provider = Get-WinEvent -ListProvider  Microsoft-Windows-WindowsUpdateClient

PS C:\> $provider.events | ? {$_.description -match "success"} | select id,description | ft -AutoSize

 

Id   Description

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

19   Installation Successful: Windows successfully installed the following update: %1

23   Uninstallation Successful: Windows successfully uninstalled the following update: %1

36   The Windows Update Client Core component was successfully updated from version %1 to version %2.

37   The Windows Update Client Auxillary was successfully updated from version %1 to version %2.

 

PS C:\> $provider.events | ? {$_.description -match "success"} | select -First 1

 

Id               : 19

Version      : 0

LogLink     : System.Diagnostics.Eventing.Reader.EventLogLink

Level          : System.Diagnostics.Eventing.Reader.EventLevel

Opcode       : System.Diagnostics.Eventing.Reader.EventOpcode

Task            : System.Diagnostics.Eventing.Reader.EventTask

Keywords    : {, success, install}

Template     : <template xmlns="http://schemas.microsoft.com/win/2004/08/events">

                         <data name="updateTitle" inType="win:UnicodeString" outType="xs:string"/>

                         <data name="updateGuid" inType="win:GUID" outType="xs:GUID"/>

                         <data name="updateRevisionNumber" inType="win:UInt32" outType="xs:unsignedInt"/>

                     </template>

 

Description : Installation Successful: Windows successfully installed the following update: %1

 

From this event description I can tell that the event logs the title of the update in the "updateTitle" field of the event Template, which will let me write a script that reports back the installed updates.

 

Filtering Events

If I want to look at all of the updates that have been installed, I can simply filter them in PowerShell:

> Get-WinEvent -Provider Microsoft-Windows-WindowsUpdateClient | ? {$_.id -eq 19} | ft timestamp, message -auto

While using PowerShell to filter the events in our last example works well, Get-WinEvent provides even more powerful filtering which is done by the Event Log. Generally, you'll want to use the Get-WinEvent FilterXPath or FilterHashTable parameters to reduce the number of events that you have to process in PowerShell. This is because the Event Log is very efficient at filtering events based on these queries.

 

For example, to perform the same filtering as the previous example without using PowerShell to filter I can use a FilterHashTable:

> Get-WinEvent -FilterHashTable @{ProviderName="Microsoft-Windows-WindowsUpdateClient"; ID=19} | ft timestamp, message -auto

 

Take a look at Get-WinEvent help for more information about how to use the FilterHashTable parameter.  It is very, very useful!

 

Working with Events

An individual event carries much more information than just the timestamp, event ID, and a message string. Event Log events contain a wealth of information such as the level which indicates if the event was an error, warning, or simply informational.  Events also carry payloads of data which are unique to each event which are described by their <template>.

 

Going back to our Windows Update example, if I wanted to just return the title of the update of each update installed, I could do so by pulling that data out of the event.  I know from the event description (above) that the first property of the event is the title of the update (which is given by the event template).  Knowing that, I can just pull out the single property and display it:

 

PS C:\> Get-WinEvent -FilterHashTable @{ProviderName="Microsoft-Windows-WindowsUpdateClient"; ID=19} | foreach {$_.properties[0]}

 

Value

-----

Definition Update for Windows Defender - KB915597 (Definition 1.59.789.0)

Intel Corporation driver update for Mobile Intel(R) 45 Express Chipset Family (Microsoft Corporation …

Test Update for Windows 7 Release Candidate for x64-based Systems (KB970424)

Update for Office Communicator 2007 R2 (KB 971083)

Definition Update for Windows Defender - KB915597 (Definition 1.59.659.0)

Definition Update for Windows Defender - KB915597 (Definition 1.59.458.0)

Test Update for Windows 7 Release Candidate for x64-based Systems (KB970421)

Test Update for Windows 7 Release Candidate for x64-based Systems (KB970423)

Update for Internet Explorer 8 Compatibility View List for Windows 7 Release Candidate for x64-based Systems …

 

  

Events from Remote Machines

The Get-WinEvent cmdlet can be used to get events from remote machines using the Event Log remote connection protocol.  For example, if I want to get the most recent 100 events from the Application and System log from a remote machine:

> Get-WinEvent -ComputerName machine.name.contoso.com -LogName Application, System -MaxEvents 100

 

It's important to know that Get-WinEvent uses the Windows Event Log to establish the remote connection and does not use the Windows PowerShell remoting for a couple of reasons.  First, since the Event Log is installed on every version of Windows Vista and above, you can use it to collect logs without requiring PowerShell Remoting. Secondly, since the cmdlet uses the Event Log's remote protocol, any filtering that you pass to the cmdlet will be done on the remote machine. This means that, if you use one of the Filter* parameters of the cmdlet, you'll minimize the data sent across the wire since the remote machine does the filtering instead of the local machine.

 If you want to gather data from multiple machines at once, you can do that with a simple script. Note that the events will need to be sorted after this script is run, since the events are gathered from one machine at a time.

PS C:\temp> import-csv .\computers.csv

ComputerName

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

127.0.0.1

Localhost

PS C:\temp> import-csv .\computers.csv | %{Get-WinEvent -ComputerName $_.ComputerName -LogName Application, System -MaxEvents 100}

TimeCreated                          ProviderName                                    Id         Message

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

6/8/2009 2:45:59 PM           Service Control Manager                 7036    The Multimedia Class Sched...

6/8/2009 2:45:59 PM           Service Control Manager                 7036    The Multimedia Class Sched...

 

We hope you find the new cmdlet very useful!

 

Kevin Woley, Windows Event Log PM 

 

Osama Sajid, Windows PowerShell PM

 

 

Leave a Comment
  • Please add 1 and 6 and type the answer here:
  • Post
  • You know, in that last example, does the cmdlet's -computerName parameter support an array? If so, it'd be more straightforward to keep your computer names in a flat file than a CSV (e.g., one name per line with no header). Then you could just use (gc computers.txt) for the -computername parameter and eliminate a ForEach-Object. Or, if -computerName can be pipeline-bound ByPropertyName, you could pipe the output of Import-CSV directly to Get-WinEvent, right? Eliminating ForEach situations is desirable simply because it's less intuitive to newcomers.

  • Hi, I liked your cmd window color and font. I tried to setup my cmd window like yours but I didn't find the same fon. Can you tell us your cmd window properties?

  • yeah you can provide multiple computers in the -computername 'array' but it's very simple:

    $yesterday = (get-date).AddDays(-1)

    get-eventlog -logname application  -after $yesterday -computername server5,server6 |where {$_.eventID -eq 101} |format-list username

    actually using this every day to search for folder redirection errors, bit slow but hey..

  • The font used is Calibri, one of the ClearType fonts included in Windows Vista/Office 2007 and higher. It's likely not actually used as their shell font as it isn't fixed-width. The PowerShell "windows" on this page are HTML.

    You could try to get it working following these steps (restart required after the mod):

    http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q247815

    but you'd probably need an alternative console host.

    If you don't already, you can instead use Consolas (a fixed-width ClearType font made for this scenario). It's available out of the box in Windows 7 RC's console configuration settings. For downlevel platforms, you'll need to register it manually as above. A PowerShell script is also available:

    http://blogs.msdn.com/powershell/archive/2006/10/16/Windows-PowerShell-Font-Customization.aspx

  • Unfortunately Get-WinEvent does not support arrays for the -ComputerName parameter. Otherwise, Don would be right that the simplest thing to do would be to pass in a list of machines.

  • Hi

    How to recover if the provider has no events logged at present ?

    Thanks

  • Hi there

    Hope your well

    how can i output this to a log file in real time ? can this script be modified to monitor the event logs in real time and output to file ?

    psloglist from the pstools was nearly exactly what i was looking apart from that the when outputting to the stdout to file the buffer is delayed by 4kb before it was flushed thus missing event's until 4kb had been reached.

    I need a solution that will output events in realtime to a log file, the desired results will then be via a perl script and posted to a db.

    thanks in advance

  • Hi,I'm interesting in getting the last 100 logs from the following logname: "ForwardedEvents" and I'm able to do this but then I want to clean ONLY those 100 specific events and not the entire log.

    Now, in order to get the events I'm using :

    "$Events=Get-WinEvent -ListLog |Select-Object -Last 100"

    and it works fine but how can I remove(clean) those 100 events?

    Thanks!

  • hi,

    i need to make this script work with xp

    it work perfectly with win 7

    but i need it with win xp

    the script:

    *******************

    #Requires -version 2.0

    set-executionpolicy remotesigned

    Register-WmiEvent -Class win32_VolumeChangeEvent -SourceIdentifier volumeChange

    write-host (get-date -format s) " Beginning script..."

    do{

    $newEvent = Wait-Event -SourceIdentifier volumeChange

    $eventType = $newEvent.SourceEventArgs.NewEvent.EventType

    $eventTypeName = switch($eventType)

    {

    1 {"Configuration changed"}

    2 {"Device arrival"}

    3 {"Device removal"}

    4 {"docking"}

    }

    write-host (get-date -format s) " Event detected = " $eventTypeName

    if ($eventType -eq 2)

    {

    $driveLetter = $newEvent.SourceEventArgs.NewEvent.DriveName

    $driveLabel = ([wmi]"Win32_LogicalDisk='$driveLetter'").VolumeName

    write-host (get-date -format s) " Drive name = " $driveLetter

    write-host (get-date -format s) " Drive label = " $driveLabel

    # Execute process if drive matches specified condition(s)

    if ($driveLetter -eq 'H:')

    {

    write-host (get-date -format s) " Starting task in 3 seconds..."

    start-sleep -seconds 3

    start-process "c:\windows\system32\launch.bat"

    }

    }

    Remove-Event -SourceIdentifier volumeChange

    } while (1-eq1) #Loop until next event

    Unregister-Event -SourceIdentifier volumeChange

    **********************************

    thanks

  • Hi,

    I used next command to extract security event log from a evtx file(On a win2008 R2).

    PS>get-winevent -path c:\0910.evtx >> c:\tst.csv

    It need about 7 minutes.

    c:\0910.evtx is about 15MB big and contains only security event log.

    Because wevtutil needs only several seconds to conver the same c:\0910.evtx to a evt file, I wonder why get-winevent was so slowly.

    Does anyone have a faster solution?

    Thanks.

  • Thanks for the helpful post. 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

  • How do I get the last two commands to list the providers events like the first command?

    (Get-WinEvent -ListProvider Microsoft-Windows-WindowsUpdateClient).Events

    (Get-WinEvent -ListProvider Powershell).Events

    (Get-WinEvent -ListProvider MSSQLSERVER).Events

    I am running this on Windows Server 2008 R2 with MS SQL Server 2008 R2 installed. Finding a way to get the MSSQLSERVER provider events is what I really want to work, even if I have to use a different PowerShell command to get them.

    Thanks,

    SLDR

    (Stephen L. De Rudder)

Page 1 of 1 (12 items)