The Windows Event Log is an important tool for administrators to track errors, warnings, and other information reports that are logged by the operating system, its components, or programs. You can use the Event Viewer graphical MMC snap-in (eventvwr.msc
) to view the Windows event log. In some cases, it is much more convenient to use PowerShell to parse and analyze information from the Event Logs. In this article, you’ll learn how to use the Get-WinEvent cmdlet to get information from the Windows event logs.
Get-WinEvent: Search the Event Logs Using PowerShell
To use the Get-WinEvent command, you must run PowerShell as an administrator. If you try to run Get-WinEvent as a non-admin user, you will not be able to access certain logs, including the Security logs.
To get a list of events from a specific log, you must specify its name. For example, the following command lists the last 20 events from the System log:
Get-WinEvent -LogName Application -MaxEvents 20
System, Application, Security, or Setup logs are the most common logs to query. You can also specify other log names. You can get a full list of event logs in Windows with the command:
Get-WinEvent -ListLog *
For example, to view the RDP connection history on a computer, you must specify the Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational
log:
Get-WinEvent -LogName Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational
Or you can get the SSH connection logs in Windows from the OpenSSH/Operational log:
Get-WinEvent -LogName OpenSSH/Operational
You can also select events from multiple logs at once. For example, if you want to get information about errors and warnings from System and Application logs for the last 24 hours, use the following code:
$StartDate = (Get-Date) - (New-TimeSpan -Day 1)
Get-WinEvent Application,System | Where-Object {($_.LevelDisplayName -eq "Error" -or $_.LevelDisplayName -eq "Warning") -and ($_.TimeCreated -ge $StartDate )}
You can use the Select-Object or Format-Table cmdlets to display only specific event fields:
Get-WinEvent -LogName System | Format-Table Machinename, TimeCreated, Id, UserID
You can further process data obtained from the event log. In this example, we will immediately convert the username to SID:
Get-WinEvent -filterhash @{Logname = 'system'} |
Select-Object @{Name="Computername";Expression = {$_.machinename}},@{Name="UserName";Expression = {$_.UserId.translate([System.Security.Principal.NTAccount]).value}}, TimeCreated
Fast Event Search with the FilterHashtable Option
The above method of filtering specific events from the Event Viewer logs with Where-Object may be easy to understand, but it is extremely slow, especially if you want to search through a large number of events. In most cases, it’s better to use Event Viewer server-side filtering using the FilterHashtable option.
Now, let’s try to generate a list of errors and warnings over a 30-day period using Where-Object and FilterHashtable. We will then use Measure-Command
to compare the execution time of these two PowerShell commands:
$StartDate = (Get-Date).AddDays(-30)
First, we check the execution time of the command with the Where-Object filter:
(Measure-Command {Get-WinEvent Application,System | Where-Object {($_.LevelDisplayName -eq "Error" -or $_.LevelDisplayName -eq "Warning") -and ($_.TimeCreated -ge $StartDate )}}).TotalMilliseconds
The same command with FilterHashtable:
(Measure-Command {Get-WinEvent -FilterHashtable @{LogName = 'System','Application'; Level =2,3; StartTime=$StartDate }}).TotalMilliseconds
This example shows that the FilterHashtable event filtering command is 30 times faster than the usual Where-Object filter ( 2.5
secs vs 76
secs).
If you need to find events by EventID, use the following command with the FilterHashtable parameter:
Get-WinEvent -FilterHashtable @{logname='System';id=1074}|ft TimeCreated,Id,Message
The FilterHashtable argument allows you to filter by the following event attributes:
- LogName
- ProviderName
- Path
- Keywords (use 9007199254740992 to search for successful events and 4503599627370496 for failed ones)
- ID
- Level (1=FATAL, 2=ERROR, 3=Warning, 4=Information, 5=DEBUG, 6=TRACE, 0=Info)
- StartTime
- EndTime
- UserID (user’s SID)
- Data
Here’s an example of searching for an event over a certain period of time:
Get-WinEvent -FilterHashTable @{LogName='System'; StartTime=(get-date).AddDays(-7); EndTime=(get-date).AddHours(-1); ID=1234}
The command below can be used if you want to find specific text in the event description:
Get-WinEvent -FilterHashtable @{logname='System'}|Where {$_.Message -like "*USB*"}
Advanced Get-WinEvent Filtering with FilterXml
The Get-WinEvent filters with the FilterHashtable option have some limitations. If you need to use complex queries with many criteria to select events by, you need to use the FilterXml flag, which allows you to make the selection using an XML query. Similar to FilterHashtable, FilterXml filters are server-side. This means you get the result pretty quickly.
For example, here’s another way to get the most recent errors from the System log for the last 30 days:
$xmlQuery = @'
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[(Level=2 or Level=3) and TimeCreated[timediff(@SystemTime) <= 2592000000]]]</Select>
</Query>
</QueryList>
'@
Get-WinEvent -FilterXML $xmlQuery
To generate a complex XML query code, you can use the Event Viewer graphical console:
- Run the command
eventvwr.msc
; - Find the log you want to create a query for and click Filter Current Log;
- Select the required query parameters in the filter form. In this example, I want to find events with specific EventIDs for the last 7 days for a specific user;
- To get the XML query code, go to the XML tab, and copy the XPath code (
CTRL+A
,CTRL+C
); - You can edit this query manually if necessary.
To export the list of events to a CSV file, use the Export-CSV cmdlet:
$Events= Get-WinEvent -FilterXML $xmlQuery
$events| Export-CSV "C:\ps\FilterSYSEvents.csv" -NoTypeInformation -Encoding UTF8
Get Event Logs from Remote Computers
To get events from a remote computer, simply specify its name in the -ComputerName parameter:
$computer='mun-dc01'
Get-WinEvent -ComputerName $computer -FilterHashtable @{LogName="System"; StartTime=(get-date).AddHours(-24)} | select Message,Id,TimeCreated
You can query multiple remote hosts at once to search for specific events. You can get the list of remote computers from a text file:
$servers = Get-Content -Path C:\ps\servers.txt
Or from Active Directory:
$servers = (Get-ADComputer -Filter 'operatingsystem -like "*Windows Server*" -and enabled -eq "true"').Name
foreach ($server in $servers) {
Get-WinEvent -ComputerName $server -MaxEvents 5 -FilterHashtable @{
LogName = 'System'; ID= 1234
} | Select-Object -Property ID, MachineName
}
Here is another example of searching for user account lockout events on all domain controllers:
$Username = 'a.muller'
Get-ADDomainController -fi * | select -exp hostname | % {
$GweParams = @{
'Computername' = $_
'LogName' = 'Security'
'FilterXPath' = "*[System[EventID=4740] and EventData[Data[@Name='TargetUserName']='$Username']]"
}
$Events = Get-WinEvent @GweParams
$Events | foreach {$_.Computer + " " +$_.Properties[1].value + ' ' + $_.TimeCreated}
}