Expert Solution for 2011 Scripting Games Advanced Event 3: Use PowerShell to Get Latest Entries from All Windows Event Logs

Expert Solution for 2011 Scripting Games Advanced Event 3: Use PowerShell to Get Latest Entries from All Windows Event Logs

  • Comments 5
  • Likes

Summary: Microsoft software development engineer, Rajesh Ravindranath, uses Windows PowerShell to solve 2011 Scripting Games Advanced Event 3 and query from all Windows Event Logs. 

Microsoft Scripting Guy, Ed Wilson, here. Our expert commentator for today is Rajesh B. Ravindranath, who will be solving the Windows PowerShell 2011 Scripting Games Advanced Event 3.

Photo of Rajesh Ravindranath

Rajesh is a software development engineer at Microsoft where he works with the Remote Desktop Virtualization (RDV) team. He mainly focuses on management aspects of Remote Desktop Services. He was with the team that implemented Remote Desktop Services management through Windows PowerShell. He has authored Windows PowerShell scripts to ease the management of Remote Desktop Services. For information related to Remote Desktop Services management through Windows PowerShell, visit the Remote Desktop Services Team Blog

Worked solution

This event monitoring scenario requires that the script query for the latest event that occurred on the same day that the script was executed from each of the enabled logs.

In this solution, we will take the computer name as input along with filters for event ID and severity. To simplify the way admin can specify the computer name, the script can be enabled to support the wildcard character (*) for the computer name. All the computers with a name that matches the input will be queried for the events that match the specified criteria of ID and severity.

With this, the script authoring can be split into two tasks:

1. Query the Active Directory (AD) directory service and resolve the computer names.

2. Query the local or remote computer for events with the specified ID and severity.

To query AD, we use the System.DirectoryServices library of .NET. This provides easy access to Active Directory Domain Services from managed code, hence from Windows PowerShell. To start, we construct an LDAP query for all computer objects that match the given pattern.

$filter = "(&(objectCategory=Computer)(name=$ComputerName))"

Then we query AD by using System.DirectoryServices.DirectorySearcher. We specify the location, domain root in our case, and the query constructed earlier.

$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()

      $root = [ADSI]"GC://$($dom.Name)"

      $searcher = new-Object System.DirectoryServices.DirectorySearcher($root, $filter)

The list returned contains the computer objects with names that match the specified pattern, from which we extract the names of computers.

$searcher.FindAll() | Foreach-Object {$computer = $_.Properties; $computer.name}

When we have the computer name, we query the remote machine for events. We use the Windows PowerShell cmdlet Get-WinEvent for this. First, we query for the list of logs that are enabled by using the Get-WinEvent command with switch parameter ListLog and filtering the list if the log is enabled—the IsEnabled property is set to True. We use the parameter ComputerName to query the same on a remote computer.

$enabledLogs = Get-WinEvent -ListLog * -ComputerName $ComputerName |

Where-Object {$_.IsEnabled} | ForEach-Object {$_.LogName}

After we have the list of log names that are enabled, we query for the list of events with the same cmdlet, Get-WinEvent. We construct a hash table with following Key and Value pairs:

 

Key

Value

LogName

An entry from the list of enabled logs.

ID

Event IDs to be filtered on, only if specified as input to script.

Level

Severity level to be filtered on, only if specified as input to script. The severity level is converted into integer to be able to use with filter hash table.

 

 $filter = @{}

      if (${script:Id}) { $filter['ID'] = ${script:Id} }

      if (${script:Severity}) { $filter['Level'] = (${script:Severity} | %{$_.Value__})}

 

      foreach ($logName in $enabledLogs)

      {

            $filter['LogName'] = $logName

This hash table is specified as value to the parameter FilterHashTable, and we query for only one event. This is done by specifying MaxEvents as 1. By default Get-WinEvent returns only the latest event. If this latest event occurred on the same day on which the script was run, we write it to the output stream.

foreach ($logName in $enabledLogs)

      {

            $filter['LogName'] = $logName

            Get-WinEvent -FilterHashtable $filter -MaxEvents 1 -ComputerName $ComputerName -ErrorAction SilentlyContinue | Where-Object {$_.TimeCreated.Date -eq [DateTime]::Today}

Here is the complete script:

<# 

.Synopsis 

    Gets today's latest event from enabled event logs.

    

.Description 

    This script gets the latest event for today from each of the enabled logs. You can specify a computer name to fetch the events from remote computer. The wildcard '*' is supported for computer name.

      The events can be filtered in ID or the severity level.

 

      NOTE: For the script to get events from remote computers the firewall exception for 'Remote Event Log Management (RPC)' has to be enabled on remote computers and the user should have privileges to query the event logs.

    

.Parameter ComputerName

    The name of one or more remote computers to query. Wildcards are permitted.

    

.Example 

    PS C:\> .\Get-LatestEvent.ps1

    

    Gets today's latest event from enabled event logs on the local machine.

    

.Example 

    PS C:\> .\Get-LatestEvent.ps1 -Id 1006

    

    Gets today's latest event from local machine with event Id 1006.

    

.Example 

    PS C:\> .\Get-LatestEvent.ps1 -Severity Error

    

    Gets today's latest event from enabled event logs on the local machine whose severity level is 'Error'(2).

    

.Example

    PS C:\> .\Get-LatestEvent.ps1 -ComputerName server02

    

    Gets today's latest event from enabled event logs on the remote machine server02.

    

.Example

    PS C:\> .\Get-LatestEvent.ps1 -ComputerName server02, server05

    

    Gets today's latest event from enabled event logs on the remote machines server02 and server05.

    

.Example

    PS C:\> .\Get-LatestEvent.ps1 -ComputerName server*

    

    Gets today's latest event from enabled event logs on the remote machines in the domain, name matching 'server*'.

 

#>

 

param(

 

      [Parameter(Mandatory=$false, Position=0)] 

      [ValidateNotNullOrEmpty()] 

      [System.String[]]

      $ComputerName = @('localhost'),

 

      [Parameter(Mandatory=$false)]

      [System.Int32[]]

      $Id,

 

      [Parameter(Mandatory=$false)]

      [System.Diagnostics.Eventing.Reader.StandardEventLevel[]]

      $Severity

 

)

 

# This function queries the AD DS to resolve the computer name.

# =============================================================================

function Get-ComputerName {

 

param(

 

      [Parameter(Mandatory=$true, Position=0)] 

      [ValidateNotNullOrEmpty()] 

      [System.String]

      $ComputerName

 

)

 

      $filter = "(&(objectCategory=Computer)(name=$ComputerName))"

 

      $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()

      $root = [ADSI]"GC://$($domain.Name)"

      $searcher = new-Object System.DirectoryServices.DirectorySearcher($root, $filter)

      $searcher.PropertiesToLoad.Add("name") | Out-Null

 

      $searcher.FindAll() | Foreach-Object {$computer = $_.Properties; $computer.name}

}

 

# This function queries the remote computer for today's latest event.

# =============================================================================

function Get-TodaysLatestEvent {

 

param(

 

      [Parameter(Mandatory=$true, Position=0)] 

    [ValidateNotNullOrEmpty()] 

    [System.String]

    $ComputerName

 

)

 

      $enabledLogs = Get-WinEvent -ListLog * -ComputerName $ComputerName | Where-Object {$_.IsEnabled} | ForEach-Object {$_.LogName}

 

      $filter = @{}

      if (${script:Id}) { $filter['ID'] = ${script:Id} }

      if (${script:Severity}) { $filter['Level'] = (${script:Severity} | %{$_.Value__})}

 

      foreach ($logName in $enabledLogs)

      {

            $filter['LogName'] = $logName

            Get-WinEvent -FilterHashtable $filter -MaxEvents 1 -ComputerName $ComputerName -ErrorAction SilentlyContinue | Where-Object {$_.TimeCreated.Date -eq [DateTime]::Today}

      }

}

 

# We loop in here for each computer name specified and query for the events.

# =============================================================================

foreach ($name in $ComputerName)

{

      if ($name -ne 'localhost')

      {

            $ResolvedComputerNames = Get-ComputerName $name

            if (($ResolvedComputerNames -eq $null) -and ($name -notmatch '\*'))

            {

                  Write-Error "Specified Computer '$name' does not exist!"

                  continue

            }

      }

      else

      {

            $ResolvedComputerNames = $name

      }

     

      $ResolvedComputerNames | ForEach-Object {Get-TodaysLatestEvent $_}

}

When the script is executed, the output is as shown here:

Image of command output

If you want to view event details like the date and time the event occurred, the name of the event provider, the event ID, the message associated with that event, and the machine name on which it occurred, you can pipe the output of the script to the Format-List cmdlet, as shown here.

Image of command output

Thank you, Rajesh, for sharing your time to write this solution.

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
  • Really nice.

    ===

    <a href="http://dollarstopounds.org.uk" rel="dofollow">Dollars to Pounds</a>

  • Excellent and well explained solution!

    One thing to ask: Is it necessary to use script scope parameters? Usually I try to get along without them ... !?

    And my usual: "Error handling" reminder :-)

    kind regards, Klaus (Schulte)

  • Excellent and well explained solution!

    One thing to ask: Is it necessary to use script scope parameters? Usually I try to get along without them ... !?

    And my usual: "Error handling" reminder :-)

    kind regards, Klaus (Schulte)

  • @iXlinQ: It would be better to use them. You can minimize the scripting effort and you would be providing the consistent experience to the script users.

  • Hi there. When I worked on my solution to this event I became a little preoccupied with analytical and debug logs because they seemed a little different. I had some fun enabling one of them on my Vista box for testing.

    But when I tried this command

    PS > get-winevent -logname "Microsoft-Windows-WinRM/Analytic" -maxevents 1

    I got this message

    Get-WinEvent : The Microsoft-Windows-WinRM/Analytic event log can be read only in the forward chronological order because it is an analytical or a debug log. To see events from the Microsoft-Windows-WinRM/Analytic event log, use the Oldest parameter in the command.

    What I don't understand is how the solution given here handles analytic and debug logs. Can anyone explain ?

    BTW I really enjoyed this years games (as much as I enjoyed last years!). The problems force me to explore aspects of powershell and windows that I haven't needed before. The trouble with work is that there is a great temptation to stop at the first reasonable solution to a technical problem.

    Regards, Eddy from Tasmania.