Skip to content

Instantly share code, notes, and snippets.

@Neo23x0
Last active December 13, 2025 15:48
Show Gist options
  • Select an option

  • Save Neo23x0/f43e42c18fe93b77223c484680401cb0 to your computer and use it in GitHub Desktop.

Select an option

Save Neo23x0/f43e42c18fe93b77223c484680401cb0 to your computer and use it in GitHub Desktop.
Windows Error Eval
<#
.SYNOPSIS
Windows triage collector (best-effort) - builds a ZIP with event logs and key system snapshots.
.DESCRIPTION
Collects a focused set of Windows artefacts to help debug "sometimes slow boot / input lag / occasional black screen"
issues. Designed to never hang indefinitely:
- Self-elevates to Administrator (UAC) if needed
- Adds hard timeouts for external tools (msinfo32, dxdiag, wevtutil, robocopy)
- Skips WER ReportQueue (often huge/locked and causes infinite runs)
- Produces a ZIP in C:\Users\Public\triage_YYYYMMDD_HHMMSS.zip
Notes:
- Event logs (EVTX) and WER archives can include hostnames, usernames, installed software, file paths, device IDs.
- If you need a "privacy-strict" mode, add a switch and we can trim channels further.
.EXAMPLE
powershell -NoProfile -ExecutionPolicy Bypass -File .\win-eval.ps1
.EXAMPLE
powershell -NoProfile -ExecutionPolicy Bypass -File .\win-eval.ps1 -TimeoutSec 240 -NoTranscript
.EXAMPLE
powershell -NoProfile -ExecutionPolicy Bypass -File .\win-eval.ps1 -OutDir C:\Temp
.NOTES
Author: Florian Roth with ChatGPT 5.2 (vibe-coded, but with timeouts)
Version: 0.4
License: MIT-ish, do whatever, no warranty, if it breaks your day - that's on you ;)
.COPYRIGHT
(c) 2025 Florian Roth. All rights reserved (for the header vibes). Use freely.
#>
[CmdletBinding()]
param(
[int]$TimeoutSec = 180,
[string]$OutDir = $env:PUBLIC,
[switch]$NoTranscript,
[switch]$NoElevate
)
# ---------------- helpers ----------------
function Step([string]$msg) {
$t = Get-Date -Format "HH:mm:ss"
Write-Host "[$t] $msg"
}
function Test-IsAdmin {
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
$p = New-Object Security.Principal.WindowsPrincipal($id)
return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Ensure-Admin {
if (-not $NoElevate -and -not (Test-IsAdmin)) {
Step "Not running as admin - relaunching elevated (UAC prompt)..."
$argList = @(
'-NoProfile',
'-ExecutionPolicy','Bypass',
'-File', "`"$PSCommandPath`"",
'-NoElevate',
"-TimeoutSec",$TimeoutSec
)
if ($OutDir) { $argList += @("-OutDir", "`"$OutDir`"") }
if ($NoTranscript) { $argList += "-NoTranscript" }
Start-Process -FilePath "powershell.exe" -Verb RunAs -ArgumentList $argList
exit
}
}
function Run-Exe {
param(
[Parameter(Mandatory=$true)][string]$File,
[string]$Args = "",
[int]$Timeout = 180,
[switch]$HideWindow
)
Step "RUN $File $Args"
try {
$ws = $(if ($HideWindow) { "Hidden" } else { "Normal" })
$p = Start-Process -FilePath $File -ArgumentList $Args -PassThru -WindowStyle $ws
} catch {
Step "FAIL start $File - $($_.Exception.Message)"
return $false
}
$ok = $false
try { $ok = Wait-Process -Id $p.Id -Timeout $Timeout -ErrorAction SilentlyContinue } catch {}
if (-not $ok) {
Step "TIMEOUT ($Timeout s) - killing $File (pid $($p.Id))"
try { Stop-Process -Id $p.Id -Force } catch {}
return $false
}
Step "DONE $File"
return $true
}
function Safe-Run([scriptblock]$sb, [string]$label) {
Step $label
try { & $sb | Out-Null } catch { Step "WARN: $($_.Exception.Message)" }
}
# ---------------- main ----------------
$ErrorActionPreference = "SilentlyContinue"
Step "win-eval.ps1 starting..."
Step "TimeoutSec = $TimeoutSec"
Step "OutDir = $OutDir"
Step "Transcript = $(-not $NoTranscript)"
Ensure-Admin
$ts = Get-Date -Format "yyyyMMdd_HHmmss"
$out = Join-Path $OutDir "triage_$ts"
New-Item -ItemType Directory -Path $out -Force | Out-Null
Step "Output folder: $out"
if (-not $NoTranscript) {
try {
$tr = Join-Path $out "transcript.txt"
Start-Transcript -Path $tr -Append | Out-Null
Step "Transcript started: $tr"
} catch {
Step "WARN: transcript could not be started - $($_.Exception.Message)"
}
}
Step "Stage 1/5 - system snapshots"
# msinfo32 is helpful but sometimes gets stuck; hard timeout and continue
Run-Exe "msinfo32.exe" "/report `"$out\msinfo32.txt`"" $TimeoutSec -HideWindow | Out-Null
Run-Exe "msinfo32.exe" "/nfo `"$out\msinfo32.nfo`"" $TimeoutSec -HideWindow | Out-Null
# dxdiag can also hang occasionally; timeout applies
Run-Exe "dxdiag.exe" "/t `"$out\dxdiag.txt`"" $TimeoutSec -HideWindow | Out-Null
# Non-GUI fallbacks (usually reliable)
Safe-Run { Get-ComputerInfo | Out-File -Encoding UTF8 (Join-Path $out "Get-ComputerInfo.txt") } "Collecting Get-ComputerInfo"
Safe-Run { systeminfo /fo csv > (Join-Path $out "systeminfo.csv") } "Collecting systeminfo"
Safe-Run { driverquery /v /fo csv > (Join-Path $out "driverquery.csv") } "Collecting driver list (driverquery)"
Safe-Run { pnputil /enum-drivers > (Join-Path $out "pnputil_enum-drivers.txt") } "Collecting driver store (pnputil /enum-drivers)"
Safe-Run { powercfg /a > (Join-Path $out "powercfg_a.txt") } "Collecting supported sleep states (powercfg /a)"
Safe-Run { powercfg /requests > (Join-Path $out "powercfg_requests.txt") } "Collecting power requests (powercfg /requests)"
Step "Stage 2/5 - event log exports (EVTX)"
$logs = @(
"System",
"Application",
"Setup",
"Microsoft-Windows-Diagnostics-Performance/Operational",
"Microsoft-Windows-DriverFrameworks-UserMode/Operational",
"Microsoft-Windows-Kernel-PnP/Configuration",
"Microsoft-Windows-USB-USBHUB3/Operational",
"Microsoft-Windows-USB-USBXHCI/Operational",
"Microsoft-Windows-WHEA-Logger/Operational"
)
$logTimeout = [Math]::Max(60, [Math]::Min(180, [int]($TimeoutSec * 0.66)))
foreach ($l in $logs) {
$safe = ($l -replace '[\\\/:]','_')
$dst = Join-Path $out "$safe.evtx"
Step "Exporting: $l"
Run-Exe "wevtutil.exe" "epl `"$l`" `"$dst`" /ow:true" $logTimeout -HideWindow | Out-Null
}
Step "Stage 3/5 - crash and WER artefacts"
# ReportQueue intentionally skipped; it’s the #1 "runs forever" folder
$copyJobs = @(
@{ src="$env:WINDIR\Minidump"; dst=(Join-Path $out "extra_Minidump"); timeout=120 },
@{ src="$env:WINDIR\LiveKernelReports"; dst=(Join-Path $out "extra_LiveKernelReports"); timeout=120 },
@{ src="$env:ProgramData\Microsoft\Windows\WER\ReportArchive"; dst=(Join-Path $out "extra_WER_ReportArchive"); timeout=180 }
)
foreach ($j in $copyJobs) {
if (Test-Path $j.src) {
New-Item -ItemType Directory -Path $j.dst -Force | Out-Null
Step "Copying: $($j.src)"
Run-Exe "robocopy.exe" "`"$($j.src)`" `"$($j.dst)`" /E /R:0 /W:0 /NFL /NDL /NJH /NJS /NP" $j.timeout -HideWindow | Out-Null
} else {
Step "Skip missing: $($j.src)"
}
}
Step "Stage 4/5 - packaging (ZIP)"
$zip = "$out.zip"
try {
Compress-Archive -Path (Join-Path $out "*") -DestinationPath $zip -Force
Step "ZIP created: $zip"
} catch {
Step "ZIP failed: $($_.Exception.Message)"
}
Step "Stage 5/5 - done"
if (-not $NoTranscript) {
try { Stop-Transcript | Out-Null } catch {}
}
Step "Finished."
$zip
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment