win-eventlog-triage
Triage Windows Event Logs across one or many servers. Pulls Critical/Error events (System + Application by default; Security opt-in) over a time window via PowerShell Remoting (WinRM), groups them det
SKILL.mdskills/windows-ops/win-eventlog-triage/SKILL.md
GitHubWindows Event Log Triage
Targets Windows servers over PowerShell Remoting (WinRM). The bundled script
scripts/Invoke-EventLogTriage.ps1collects + groups events and emits JSON; the agent (you) writes the triage narrative. The script never calls an LLM.
SCRIPT = this skill's scripts/Invoke-EventLogTriage.ps1. It requires PowerShell 7+ (pwsh) and WinRM enabled on the targets.
Credentials (important)
The script always prompts for a tier-admin credential via Get-Credential — held in memory for that run only, reused across all servers, never written to disk. The user's normal account does not need server access; the prompted credential authenticates the remoting session. Do not try to pass a password on the command line. (A -Credential parameter exists only as a testing/automation seam.)
Agent/non-interactive runners:
Get-Credentialneeds an interactive console. If you drive this from a tool that runs PowerShell non-interactively (an AI agent's shell, CI, a background job) the prompt cannot render and the run hangs. Launch it in a visible window and then read the-OutFile— e.g.Start-Process pwsh -ArgumentList '-NoExit','-File','SCRIPT','-ComputerName','SRV01.contoso.local','-OutFile','C:\ops\triage.json'. From a non-domain-joined client, use the FQDN (so it matches a*.domainTrustedHosts entry — a short name won't) plus-Authentication Negotiate. See REFERENCE.md → Gotchas.
How to run
Always run with pwsh. Parse the JSON it prints on stdout.
| Want | Pass |
|---|---|
| One server | -ComputerName SRV01 |
| Several inline | -ComputerName SRV01,SRV02,SRV03 |
| A list from a file | -ServerListFile C:\path\hosts.txt (one host per line; # comments + blank lines ignored) |
| Time window | -Hours 24 (default) · -Since '2026-06-08T00:00' · -From <dt> -To <dt> |
| Severity | default Critical+Error · -IncludeWarning · -Level 1,2,3 |
| More logs | default System+Application · -IncludeSecurity · -Logs System,Application,'Microsoft-Windows-...' |
| Noise control | -SuppressList C:\path\suppress.json ({ "eventIds": [..], "providers": [".."] }) |
| Save full report | -OutFile C:\path\triage.json |
| Transport/auth | -UseSSL (HTTPS/5986) · -Authentication Negotiate|Kerberos|CredSSP (default Default) |
| Tuning | -MaxEvents 5000 (cap/log) · -MaxMessageLength 1000 · -ThrottleLimit 8 · -TopCritical 20 |
Examples (the user will be prompted for the credential when the script starts):
# Single server, last 24h
pwsh -File SCRIPT -ComputerName SRV01
# Server list, last 12h, save full detail to a file
pwsh -File SCRIPT -ServerListFile C:\ops\hosts.txt -Hours 12 -OutFile C:\ops\triage.json
# Include warnings + the Security log for one box
pwsh -File SCRIPT -ComputerName DC01 -IncludeWarning -IncludeSecurity
Output contract
- Without
-OutFile→ full JSON (all hosts + groups) on stdout. - With
-OutFile→ full detail JSON to the file; a compact JSON (summary +top_critical, no per-host groups) on stdout. For big sweeps, prefer-OutFileso your context stays small.
Key JSON fields: status (ok/partial/error), summary.top_critical (deterministically ranked: Critical→Error, then count, then recency), summary.failures (per-host problems), and hosts[].groups (each group = computer+log+provider+event_id+level with count, first_seen/last_seen in UTC, and one truncated sample_message). truncated: true on a host means the MaxEvents cap was hit — coverage was capped, say so. See REFERENCE.md for the full schema.
What you (the agent) do with the result
- Run the script, parse the JSON.
- Always give a short, critical-first summary in chat — even when full detail went to
-OutFile. Lead withsummary.top_critical: the most severe / highest-count / most recent issues, named by server + event id + provider, with a one-line plain-English read of likely cause and a suggested next action. Group related events (e.g. a service crash + dependent failures) rather than listing them flat. - Surface coverage gaps loudly (Karpathy fail-loud): list any host in
summary.failures(unreachable / auth_failed / error) and any host withtruncated: true. Never imply full coverage if some servers failed or were capped. - Only dig into
hosts[].groupswhen the user wants detail beyond the top criticals.
Errors
Get-Credentialcancelled → script aborts with a clear message; ask the user to re-run and enter the tier-admin credential.- Per-host
auth_failed→ the credential lacks rights on that box (or wrong tier), or an auth-transport config issue.unreachable→ WinRM/DNS/firewall. Each failure carries ahintfield — relay it. These are per-host and do not stop the sweep — report them, continue with the rest. - Non-domain-joined client (error
0x80090311or aTrustedHostsmessage): Kerberos is unavailable. Run from a domain-joined admin host, or add the targets to WinRM TrustedHosts and retry, or use-UseSSL. See REFERENCE.md. pwshnot found → PowerShell 7 isn't installed; the repo installer auto-installs it, or install manually:winget install Microsoft.PowerShell.