Home / Guides / Search Event Logs on multiple servers
Search Windows Event Logs on multiple servers at once
Something failed overnight and the clue is in an Event Log, but you do not know which server. Event Viewer shows one computer at a time. This guide covers what Event Viewer and Event Forwarding can do, a PowerShell script that searches the logs of a list of servers and puts the results in one sorted table and a CSV file, what the remote read needs, the errors you will meet, and where the script stops.
Short answer.
Loop over your servers with Get-WinEvent -ComputerName and a -FilterHashtable that narrows the logs, levels and
start time on the server side, then check the message text, collect everything into one list, sort it by time and export it to CSV.
Each server needs your account in its Event Log Readers group and its Remote Event Log Management firewall rules turned on.
This finds what is in the logs right now. It does not keep anything, so an event that has rolled out of a log is gone, and it does not read SQL Server's own error log.
Event Viewer: one computer at a time
Event Viewer can open another computer's logs: right-click Event Viewer (Local) and choose Connect to Another Computer. The whole console then shows that one computer. To check ten servers you connect ten times and repeat the same filter each time.
A custom view (Create Custom View in the Actions pane) saves a filter over several logs, such as errors in
Application and System for the last 24 hours. It runs against the computer Event Viewer is connected to. It is still useful here: build the
filter in the dialog, open the XML tab, and you have a query that Get-WinEvent -FilterXml accepts as it is.
Windows Event Forwarding (WEF) is Windows' own way to bring events from many computers into one. It is more setup than a search:
- WinRM configured on the collector and on every source computer, and the Windows Event Collector service on the collector (
winrm qcandwecutil qc). - A subscription that says which events to send. A collector-initiated one lists the computers; a source-initiated one is pushed to the sources by Group Policy.
- Outside a domain, certificates on both sides and HTTPS on port 5986.
The forwarded events land in the collector's ForwardedEvents log, which you then search like any other log. Microsoft's guidance is to keep a collector-initiated subscription to 500 computers or fewer. WEF only moves event log entries: log files and SQL Server's error log are not covered.
Search several servers with PowerShell
Get-WinEvent reads another computer's logs with -ComputerName, but it takes one name at a time, so the script
loops. -FilterHashtable does the filtering on the server, which is much faster than sending every event back and filtering
with Where-Object. The hashtable has no key for message text, so the text check runs in the script after the events arrive.
param(
[string[]]$ComputerName = @('SERVER01', 'SERVER02', 'SERVER03'),
[string[]]$LogName = @('Application', 'System'),
[int[]]$Level = @(1, 2), # 1 Critical, 2 Error, 3 Warning, 4 Information
[datetime]$StartTime = (Get-Date).AddHours(-24),
[string]$Text = '', # part of the message to look for; empty = all
[string]$CsvPath = "$env:USERPROFILE\Desktop\EventSearch.csv"
)
$filter = @{ LogName = $LogName; Level = $Level; StartTime = $StartTime }
$found = [System.Collections.Generic.List[object]]::new()
$failed = [System.Collections.Generic.List[object]]::new()
foreach ($computer in $ComputerName) {
try {
$events = Get-WinEvent -ComputerName $computer -FilterHashtable $filter -ErrorAction Stop
}
catch {
# "No events were found" is an error in Get-WinEvent; here it just means nothing matched.
if ($_.FullyQualifiedErrorId -like 'NoMatchingEventsFound*') { continue }
$failed.Add([pscustomobject]@{ Computer = $computer; Error = $_.Exception.Message })
continue
}
foreach ($e in $events) {
# The message text is not part of the filter, so it is checked here, case-insensitive.
if ($Text -and -not ($e.Message -and
$e.Message.IndexOf($Text, [System.StringComparison]::OrdinalIgnoreCase) -ge 0)) { continue }
$found.Add([pscustomobject]@{
Time = $e.TimeCreated
Computer = $computer
Log = $e.LogName
Level = $e.LevelDisplayName
Source = $e.ProviderName
EventId = $e.Id
Message = $e.Message
})
}
}
$sorted = $found | Sort-Object Time -Descending
$sorted | Format-Table Time, Computer, Level, Source, EventId, Message -AutoSize
$sorted | Export-Csv -Path $CsvPath -NoTypeInformation -Encoding UTF8
"{0} events from {1} computers written to {2}" -f $found.Count, $ComputerName.Count, $CsvPath
foreach ($f in $failed) { Write-Warning "$($f.Computer): $($f.Error)" }
Save it as C:\Scripts\Search-EventLogs.ps1 and run it with your own server names:
C:\Scripts\Search-EventLogs.ps1 -ComputerName SQL01, SQL02, WEB01 -Text 'timeout' -StartTime (Get-Date).AddDays(-3)
# Or read the server list from a file, one name per line, and include warnings:
C:\Scripts\Search-EventLogs.ps1 -ComputerName (Get-Content C:\Scripts\servers.txt) -Level 1,2,3 -Text 'disk'
-ComputerName: the servers to search. Use the names you would use in Event Viewer.-LogName: the logs to read (default Application and System).-Level: 1 Critical, 2 Error, 3 Warning, 4 Information. The default is Critical and Error.-StartTime: how far back to look (default 24 hours).-Text: part of the message to look for, not case-sensitive. Leave it out to get every event that matches the other filters.-CsvPath: where the results go. The table on screen is the same list, newest first, with a Computer column.
Keep the time window and levels tight. Only those filters run on the server; every event that passes them travels over the network before the text check, so "all Information events for a month" can be slow on a busy server.
Tested on Windows PowerShell 5.1 on Windows 11, reading the test computer by its own name and as localhost: the results from
both names came back in one table sorted newest first and in the CSV; the text search matched the same rows in upper and lower case; a
filter that matched nothing gave an empty result and no error; a log name that does not exist was reported as a warning and the script
carried on. The remote parts (other servers, firewall rules, Invoke-Command) are from Microsoft's documentation and were not run.
Faster on many servers: Invoke-Command
The loop above reads one server after another. Invoke-Command runs the same search on the servers, up to 32 at a time
by default (-ThrottleLimit), and only the matching events come back. Each result carries a PSComputerName property:
$servers = Get-Content C:\Scripts\servers.txt
$search = {
param([string]$Text, [datetime]$Since)
$filter = @{ LogName = 'Application', 'System'; Level = 1, 2; StartTime = $Since }
try { $events = Get-WinEvent -FilterHashtable $filter -ErrorAction Stop }
catch {
if ($_.FullyQualifiedErrorId -like 'NoMatchingEventsFound*') { return }
throw
}
$events |
Where-Object { $_.Message -and $_.Message.IndexOf($Text, [System.StringComparison]::OrdinalIgnoreCase) -ge 0 } |
Select-Object TimeCreated, LogName, LevelDisplayName, ProviderName, Id, Message
}
Invoke-Command -ComputerName $servers -ScriptBlock $search -ArgumentList 'timeout', (Get-Date).AddDays(-1) |
Sort-Object TimeCreated -Descending |
Select-Object PSComputerName, TimeCreated, LogName, LevelDisplayName, ProviderName, Id, Message |
Export-Csv "$env:USERPROFILE\Desktop\EventSearch.csv" -NoTypeInformation -Encoding UTF8
The trade-off is what it needs: Invoke-Command uses PowerShell remoting over WinRM, not the Event Log's own remote access.
See the next section.
What a remote read needs
Get-WinEvent -ComputerName does not use PowerShell remoting. It talks to the Event Log service on the server over RPC, so it
works on servers where remoting is off. It needs three things on each server:
- Permission. Your account (or the account the script runs as) in the server's local Event Log Readers group, whose members can read the event logs, or in Administrators. Domain controllers have a domain-wide Event Log Readers group in the Builtin container.
- The firewall. The inbound rules in the Remote Event Log Management group (RPC, RPC-EPMAP and NP-In) turned on. On the Windows 11 computer we tested on, all six rules in that group were off.
- RPC through the network. Any firewall between you and the server must let through TCP port 135 (the RPC endpoint mapper) and the dynamic port the server hands out after that.
An administrator can set the first two up on a server like this (replace the account name with yours):
# Run on each server, in an elevated PowerShell window.
Add-LocalGroupMember -Group 'Event Log Readers' -Member 'CONTOSO\svc-logsearch'
Enable-NetFirewallRule -DisplayGroup 'Remote Event Log Management'
On many servers, do the same through Group Policy instead of one by one. Enable-NetFirewallRule without a filter turns on
every rule on the computer, so always give it -DisplayGroup or a rule name.
Invoke-Command needs something else: WinRM listening on the server (on by default on Windows Server 2012 and later), the
WinRM ports 5985/5986 open, and an account in the server's Administrators or Remote Management Users group. The search then runs on the
server as that account, so a non-administrator also needs Event Log Readers there.
Common errors
- "The RPC server is unavailable." The script could not reach the Event Log service. Microsoft documents this error for a
server whose Remote Event Log Management firewall rules are not enabled. Other causes: a wrong name or DNS entry, the server is off,
or a firewall in between blocks port 135 or the dynamic RPC port.
Test-NetConnection SERVER01 -Port 135checks the first port only. - Access denied. The connection worked but the account cannot read the log. Add it to Event Log Readers on that server,
or run the script as an account that is.
Get-WinEventalso has-Credentialfor another account. - "No events were found that match the specified selection criteria." This is not a failure.
Get-WinEventreports an empty result as an error (its id isNoMatchingEventsFound). The script catches exactly that error and moves on, and reports everything else. Do not use-ErrorAction SilentlyContinuefor this: it hides the RPC and access errors too, and a server you could not read looks the same as a server with nothing to find. - "There is not an event log on the SERVER01 computer that matches ..." One of the log names in
-LogNamedoes not exist on that server (for example a log that only exists where a product is installed). The whole call fails for that server, so search such a log in a separate run.
Where the script stops
- Only what is still in the log. Event logs have a maximum size, and the usual setting (LogMode Circular)
overwrites the oldest events when the log is full. On a busy server that can be days. The script keeps nothing between runs, so last month's error is gone if the log
has wrapped since. To see how far back each log goes:
Get-WinEvent -ListLog Application, System -ComputerName SERVER01 | Select-Object LogName, LogMode, MaximumSizeInBytes, RecordCount Get-WinEvent -LogName System -ComputerName SERVER01 -Oldest -MaxEvents 1 | Select-Object TimeCreated - No SQL Server error log. SQL Server writes its own error log (the ERRORLOG file), which
Get-WinEventdoes not read. Microsoft documentssp_readerrorlogfor searching it, one instance at a time:
The first number is the log (0 is the current one, 1 the one before), the second is 1 for SQL Server or 2 for SQL Server Agent, then up to two search strings. It needsUSE [master]; EXECUTE sp_readerrorlog 0, 1, N'timeout';securityadminorVIEW SERVER STATE(SQL Server 2022 and later also acceptVIEW ANY ERROR LOG). A new error log starts every time SQL Server starts, and by default only six old ones are kept. You will seexp_readerrorlogin many scripts; it has no reference page on Microsoft Learn, so this guide uses the documented procedure. - No log files. IIS logs, application logs and other text files are not in the Event Log. That is a separate search.
- A search, not a record. Every run reads the servers again, and nothing tells you when a new error appears.
Questions
Can Event Viewer search several servers at once?
No. Event Viewer connects to one computer at a time (Connect to Another Computer), and a custom view runs on that computer. To search many servers at once, loop over them with Get-WinEvent -ComputerName in PowerShell, forward their events to one collector with Windows Event Forwarding, or use a log collector.
Does Get-WinEvent -ComputerName need PowerShell remoting?
No. It reads the logs through the Event Log service over RPC, so it works with remoting turned off. The server needs your account in its Event Log Readers group and the Remote Event Log Management firewall rules on. Invoke-Command is different: it uses PowerShell remoting over WinRM.
Why do I get "The RPC server is unavailable"?
The script could not reach the Event Log service on that server. The usual causes are the Remote Event Log Management firewall rules being off on the server, a firewall in between that blocks port 135 or the dynamic RPC port, a wrong name, or the server being down.
Why does Get-WinEvent throw an error when nothing matches?
Get-WinEvent reports an empty result as the error "No events were found that match the specified selection criteria." Catch that one error (its id starts with NoMatchingEventsFound) and treat it as zero results. Do not hide all errors with -ErrorAction SilentlyContinue, or a server you could not read looks like a server with nothing to find.
How far back can I search an Event Log?
Only as far as the log still holds. Event logs have a maximum size, and the usual setting overwrites the oldest events when the log is full, which on a busy server can be a few days. Get-WinEvent -LogName System -Oldest -MaxEvents 1 shows the oldest event that is left. To search further back, the events must be copied somewhere first, for example by Windows Event Forwarding or a collector such as LogTrawler.
Can I search the SQL Server error log the same way?
Not with Get-WinEvent: SQL Server writes its error log to a file, not to the Event Log. Use sp_readerrorlog on each instance, for example EXECUTE sp_readerrorlog 0, 1, N'timeout'; to search the current log. By default SQL Server keeps the current log and six older ones, and starts a new one every time it starts.
Related
Sources (Microsoft Learn): Get-WinEvent, Creating Get-WinEvent queries with FilterHashtable, Windows Event Log: accessing remote computers, Configure remote management in Server Manager, Active Directory security groups (Event Log Readers), Add-LocalGroupMember, Enable-NetFirewallRule, RPC error troubleshooting, Test-NetConnection, Security considerations for PowerShell remoting, about_Remote_Requirements, Invoke-Command, Windows Event Collector, Setting up a source-initiated subscription, WinRM best practices, How to select computers in Event Viewer, sp_readerrorlog, Configure SQL Server error logs.