Expert Solution for 2011 Scripting Games Advanced Event 3: Use PowerShell to Get Latest Entries from All Windows Event Logs
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.
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:
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.
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
0 comments