The Windows event logs are a great place to start when troubleshooting problems or investigating potential security breaches.
Windows provides an extensive list of various event logs grouped by a provider with a sometimes staggering number of events recorded within. With all of these events being recorded, it's hard to figure out what's going on. One way to search event logs across not one but hundreds of servers at once is with PowerShell.
PowerShell has two main commands that allow you to query event logs called Get-EventLog and Get-WinEvent. In this article, we're going to be focusing on Get-WinEvent because it supports all types of event logs and has better filtering capabilities.
Querying events from servers is easy with Get-WinEvent. The Get-WinEvent cmdlet has a parameter called ComputerName that allows you to specify a remote server. We'll also need to provide the name of the event log to query using the LogName parameter. You can see below that the output is grouped by the provider.
PS> Get-WinEvent -ComputerName SRV1 -LogName System
ProviderName: Microsoft-Windows-NDIS
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
3/14/2019 9:20:50 AM 10400 Warning The network interface "Intel(R) PRO/1000 MT Network Connection" has begun resetting. There will ...
3/14/2019 9:12:16 AM 10400 Warning The network interface "Intel(R) PRO/1000 MT Network Connection" has begun resetting. There will ...
ProviderName: Service Control Manager
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
3/14/2019 8:08:46 AM 7040 Information The start type of the Background Intelligent Transfer Service service was changed from auto start...
<SNIP>
We typically don't want to find all events in a particular log, so we have options to limit that output down. First, we can use the MaxEvents parameter. This does not filter the results but merely limits the number of events returned.
PS> Get-WinEvent -ComputerName SRV1 -LogName System -MaxEvents 1
To narrow down what I'm looking for, one way to filter events with Get-WinEvent is to use the FilterHashTable parameter. This parameter allows you to provide a hashtable as input specifying different attributes to filter events on.
For example, we could filter events by criticality using the Level key inside of the FilterHashTable parameter. In the case below, this query would only return critical and errors only from my SRV1 server.
Get-WinEvent -ComputerName SRV1 -FilterHashtable @{
LogName = 'System'
Level = 1,2 # 1 Critical, 2 Error, 3 Warning, 4 Information
}
I can also perform some other common event log queries by finding account lockouts which I know generates an event ID of 4740 in the Security log. Or I could filter on the provider. There are a lot of different ways you can filter event logs.
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4740
}
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ProviderName = 'Microsoft-Windows-GroupPolicy'
}
Now that I have a good idea of how to query events and filter them, let's expand out to performing queries on multiple computers. To do this, you'll need to execute the Get-WinEvent command for each remote computer name. We'll have to create a foreach loop to query all of our servers.
Let's say I have a list of servers in a text file, and I'd like to search for events logs across all of them. To do that, I can use Get-Content to read the text file which will return each server name where I can then pass that name to the ComputerName parameter on Get-WinEvent. The below example will query the System event log returning the first five events on every server that's defined in the C:\servers.txt text file.
$servers = Get-Content -Path C:\servers.txt
foreach ($server in $servers) {
Get-WinEvent -ComputerName $server -MaxEvents 5 -FilterHashtable @{
LogName = 'System'
}
}
This returns the expected events, but you can't tell which server the event is coming from. To remedy this, I can ensure that the MachineName property is returned with only the properties I care about. Perhaps I want to see the event ID and the server name. I can limit the output by those properties by using Select-Object.
$servers = Get-Content -Path C:\servers.txt
foreach ($server in $servers) {
Get-WinEvent -ComputerName $server -MaxEvents 5 -FilterHashtable @{
LogName = 'System'
} | Select-Object -Property ID, MachineName
}
Id MachineName
-- -----------
7036 SRV1.techsnips.local
10016 SRV2.techsnips.local
7036 SRV3.techsnips.local
You can see that once you're able to come up with the appropriate filter for a single computer, expanding that to multiple servers is easy with a foreach loop. Once you do this, the only issue you'll need to address is output and using Select-Object; you can craft any specific property you'd like to see in your final report.
Adam Bertram
Adam Bertram is a 25+ year IT veteran and an experienced online business professional. He’s a successful blogger, consultant, 6x Microsoft MVP, trainer, published author and freelance writer for dozens of publications. For how-to tech tutorials, catch up with Adam at adamtheautomator.com, connect on LinkedIn or follow him on X at @adbertram.