Summary: Learn how to use XML and Windows PowerShell to parse the Windows system event log for Windows updates.
Microsoft Scripting Guy, Ed Wilson, is here. Sometimes I come up with a solution, and then I go looking for a problem to fix. Not often, but sometimes. This is usually the result of playing around with Windows PowerShell on the weekend, and trying different things until I come up with something I think is cool.
This is not one of those occasions. In fact, today’s blog post is pretty important for several reasons. The first reason is that I show how to use an XML filter to retrieve Windows event log entries that are written by the Windows Update. Not too big of a deal because I did that yesterday—at least, I used XML to filter the event log. In and of itself, this is a very valuable skill to develop because it generates huge performance increases.
The second reason this post is important is because I also show you how to use XML to retrieve a specific node from the event data portion of the event log entry. This is huge, because more event log entries are storing more information in the message portion. It is well-formed XML. Most of the stuff you see floating around on the Internet takes a long way around, and it ends up doing all sorts of contorted stuff. My approach is relatively straightforward.
First the query
Obviously the first thing to do is to develop the query. I am using a FilterXML query to retrieve Windows Update events from the system event log.
Note For more information about this technique, refer to Data Mine the Windows Event Log by Using PowerShell and XML.
I did not have to do too much to this query. In fact, it was as simple as clicking the boxes in the Event Viewer, and then switching to XML. The results of this operation are shown here:
I store the query in a here string. Here is my query:
$query = @"
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[Provider
[@Name='Microsoft-Windows-WindowsUpdateClient']
and (Level=4 or Level=0) and Task = 1
and (band(Keywords,8200))]]</Select>
</Query>
</QueryList>
"@
Now, I need to use the –FilterXML parameter of the Get-WinEvent cmdlet to perform the actual query. I will also store the resulting events in a variable I call $systemEvents. This is shown here:
$systemEvents = Get-WinEvent -FilterXml $query
Search the matching events
I now need to search all of the matching Windows Update events that I stored in the $systemEvents variable and look for the name of the updates. To do this, I use a Foreach loop to walk through the events. I store the time the event was created, and I convert the event log entry to XML by calling the toxml method. This is shown here:
$systemEvents = Get-WinEvent -FilterXml $query
Foreach($event in $systemEvents)
{
$tc = $event.TimeCreated
$xml = [xml]$event.toxml()
When I have an XML document, I call the SelectSingleNode method, and I retrieve the @Name=’updateTitle’ node. When trying to find out what I need to use, I refer to the XML view of the event log entry in the Event Viewer as shown here:
I then pipe the node to the Select-Object cmdlet, and I expand the #text property. I found this whilst playing around with Get-Member and exploring the returned objects from the various commands. I store the update title (from the #text property) in a variable I call $ud.
Note Because the #text property begins with a pound sign, I have to put quotation marks around it; otherwise, Windows PowerShell views it as the comment character. I should also say that using the grave accent character ( ` ) does not work for this.
Now, I want to return an object, so I use the New-Object cmdlet to create an object that contains the time stamp and the name of the update. This section is shown here:
$ud = $xml.SelectSingleNode("//*[@Name='updateTitle']") |
Select -ExpandProperty '#text'
New-Object -TypeName psobject -Property @{"Date"=$tc;"Update"=$ud}
}
When I run the script, the following output appears:
The complete script is shown here:
$query = @"
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[Provider
[@Name='Microsoft-Windows-WindowsUpdateClient']
and (Level=4 or Level=0) and Task = 1
and (band(Keywords,8200))]]</Select>
</Query>
</QueryList>
"@
$systemEvents = Get-WinEvent -FilterXml $query
Foreach($event in $systemEvents)
{
$tc = $event.TimeCreated
$xml = [xml]$event.toxml()
$ud = $xml.SelectSingleNode("//*[@Name='updateTitle']") |
Select -ExpandProperty '#text'
New-Object -TypeName psobject -Property @{"Date"=$tc;"Update"=$ud}
}
That is all there is to using XML to return event log data from a single node. Event Log Week will continue tomorrow when I will talk about some really cool stuff.
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