PC running slow? Get a full health report with one PowerShell script
A free PowerShell script that shows what's slowing your PC down: CPU and memory hogs, startup programs, disk health, free space and recent errors, in one zip file.
- 1Download
- 2Right-click › Run with PowerShell
- 3Results open in Downloads
- 1Copy script
- 2Open PowerShell from Start
- 3Paste and press Enter
Safety & file details
Free to use, provided as-is. It doesn't install anything or send data anywhere, and it runs without admin rights. As with any script, read it before you run it — or ask us.
PowerShell · Version 2.0 · 330 lines
SHA-256 (to check the download wasn't altered: Get-FileHash .\PCHealthReport.ps1)
0d9cc1afbc8f120f73e46a52482210d49482617cb09adf3e57b92007554267d5
View the script
#Requires -Version 5.1
<#
.SYNOPSIS
PC Health & Performance Report
.DESCRIPTION
Collects read-only diagnostics that help work out why a Windows PC is
slow, freezing, overheating or restarting: hardware and BIOS, what is
using the CPU and memory right now, startup programs, drivers, disk
health and free space, disk response time, recent system errors,
unexpected shutdowns, battery health, recent Windows updates and uptime.
Everything is saved to a folder in your Downloads folder and zipped,
ready to send to whoever is helping you.
The script does NOT change any settings, install anything or send data
anywhere. The only things it writes are the report folder and zip file.
.NOTES
Source : https://techs4u.com.au/resources
Version : 2.0
Requires : Windows 10/11, Windows PowerShell 5.1 or PowerShell 7+
Licence : MIT - free to use and modify. Provided as-is, without warranty.
HOW TO RUN
1. Download - your browser saves it to your Downloads folder.
2. Run it - right-click PCHealthReport.ps1 > Run with PowerShell
(Windows 11: right-click > Show more options first).
3. Results - saved to your Downloads folder, which opens for you.
IF RIGHT-CLICK DOESN'T WORK (the most reliable method):
1. Copy the whole script (the "Copy script" button on the web page,
or open the file in Notepad, Ctrl+A, Ctrl+C).
2. Open Windows PowerShell from the Start menu.
3. Paste it in (right-click inside the window, or Ctrl+V) and press
Enter. If Windows warns about pasting multiple lines, choose
"Paste anyway".
Results are saved to your Downloads folder the same way.
TIP: run it while the PC is being slow, so the "right now" sections
show what is actually using it.
PRIVACY: the report includes your computer name, Windows username, the
PC's serial number (useful for warranty checks) and the names of running
programs. Review it before sharing it with anyone.
#>
# ============================================================================
# TECHS4U SCRIPT STANDARD - output to Downloads, zip, Explorer, wait for Enter
# ============================================================================
$ScriptTitle = 'PC Health & Performance Report'
$OutputName = 'PCHealthReport'
function Get-DownloadsFolder {
$path = $null
try { $path = (New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path } catch {}
if (-not $path -or -not (Test-Path $path)) {
try {
$raw = (Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -ErrorAction Stop).'{374DE290-123F-4565-9164-39C4925E467B}'
if ($raw) { $path = [Environment]::ExpandEnvironmentVariables($raw) }
} catch {}
}
if (-not $path -or -not (Test-Path $path)) { $path = Join-Path $env:USERPROFILE 'Downloads' }
if (-not (Test-Path $path)) { $path = $env:TEMP }
return $path
}
function Wait-BeforeClose {
Write-Host ""
Write-Host "Press Enter to close this window..." -ForegroundColor DarkGray
[void](Read-Host)
}
Clear-Host
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host " $ScriptTitle" -ForegroundColor Cyan
Write-Host " techs4u.com.au" -ForegroundColor Cyan
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "This script only READS system information - it changes nothing." -ForegroundColor Gray
Write-Host "The report includes your computer name, username and PC serial number." -ForegroundColor Gray
Write-Host ""
Start-Sleep -Seconds 2
try {
$Folder = Join-Path (Get-DownloadsFolder) ("{0}_{1}" -f $OutputName, (Get-Date -Format 'yyyyMMdd-HHmm'))
New-Item -ItemType Directory -Path $Folder -Force -ErrorAction Stop | Out-Null
$ReportPath = Join-Path $Folder "$OutputName.txt"
} catch {
Write-Host "SETUP FAILED - could not create the report folder: $($_.Exception.Message)" -ForegroundColor Red
Wait-BeforeClose
exit
}
$Steps = @(
"System Information","Device & BIOS","CPU","Memory","What is using the CPU right now",
"What is using memory","Startup Programs","Drivers","Disk Health","Free Space",
"Disk Response Time","System Errors","Unexpected Shutdowns","Battery","Windows Updates",
"Power Plan","Uptime"
)
$Total = $Steps.Count
$Current = 0
function Show-Progress {
param([string]$Status)
$script:Current++
$pct = [math]::Round(($script:Current / $Total) * 100)
Write-Host "[$pct%] $Status..." -ForegroundColor Yellow
}
function Add-Section {
param([string]$Title, [scriptblock]$Content)
Add-Content -Path $ReportPath -Value "`r`n===== $Title =====`r`n"
try {
$out = & $Content | Out-String -Width 300
if ([string]::IsNullOrWhiteSpace($out)) { $out = "(nothing found)" }
Add-Content -Path $ReportPath -Value $out
} catch {
Add-Content -Path $ReportPath -Value "ERROR collecting $Title : $($_.Exception.Message)"
}
}
# First 2 non-empty lines of an event message
function Get-ShortMessage {
param([string]$Message)
(($Message -split "`r?`n") | Where-Object { $_.Trim() } | Select-Object -First 2) -join ' | '
}
Add-Content -Path $ReportPath -Value "PC HEALTH & PERFORMANCE REPORT (techs4u.com.au)"
Add-Content -Path $ReportPath -Value "Generated: $(Get-Date)"
Add-Content -Path $ReportPath -Value "Computer: $env:COMPUTERNAME | User: $env:USERNAME"
Add-Content -Path $ReportPath -Value "Report Folder: $Folder"
Write-Host ""
Write-Host "Collecting the report - this takes about a minute..." -ForegroundColor Cyan
Write-Host ""
Show-Progress "System Information"
Add-Section "System Information" {
$os = Get-CimInstance Win32_OperatingSystem
$cs = Get-CimInstance Win32_ComputerSystem
[PSCustomObject]@{
OS = $os.Caption
Version = $os.Version
Build = $os.BuildNumber
Architecture = $os.OSArchitecture
InstallDate = $os.InstallDate
PartOfDomain = $cs.PartOfDomain
TotalRAMGB = [math]::Round($cs.TotalPhysicalMemory/1GB,1)
}
}
Show-Progress "Device & BIOS"
Add-Section "Device & BIOS" {
$bios = Get-CimInstance Win32_BIOS
$cs = Get-CimInstance Win32_ComputerSystem
[PSCustomObject]@{
Manufacturer = $cs.Manufacturer
Model = $cs.Model
SerialNumber = $bios.SerialNumber
BIOSVersion = $bios.SMBIOSBIOSVersion
BIOSReleaseDate = $bios.ReleaseDate
}
}
Show-Progress "CPU"
Add-Section "CPU" {
Get-CimInstance Win32_Processor |
Select-Object Name,NumberOfCores,NumberOfLogicalProcessors,MaxClockSpeed,CurrentClockSpeed,LoadPercentage
}
Show-Progress "Memory"
Add-Section "Memory" {
$os = Get-CimInstance Win32_OperatingSystem
[PSCustomObject]@{
TotalGB = [math]::Round($os.TotalVisibleMemorySize/1MB,2)
FreeGB = [math]::Round($os.FreePhysicalMemory/1MB,2)
UsedPercent = [math]::Round((($os.TotalVisibleMemorySize-$os.FreePhysicalMemory)/$os.TotalVisibleMemorySize)*100,1)
}
}
Show-Progress "What is using the CPU right now"
Add-Section "Top 15 Processes by CPU (measured over 3 seconds)" {
# Two snapshots of each process's CPU time, 3 seconds apart. The
# difference is what it used in that window - unlike the plain CPU
# column, which is total CPU seconds since the process started.
$cores = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors
$before = @{}
Get-Process | ForEach-Object { if ($_.CPU -ne $null) { $before[$_.Id] = $_.CPU } }
Start-Sleep -Seconds 3
Get-Process | Where-Object { $_.CPU -ne $null -and $before.ContainsKey($_.Id) } | ForEach-Object {
[PSCustomObject]@{
Process = $_.ProcessName
Id = $_.Id
CPUPercent = [math]::Round((($_.CPU - $before[$_.Id]) / 3 / $cores) * 100, 1)
MemoryMB = [math]::Round($_.WorkingSet64/1MB)
}
} | Sort-Object CPUPercent -Descending | Select-Object -First 15 | Format-Table -AutoSize
}
Show-Progress "What is using memory"
Add-Section "Top 15 Processes by Memory" {
Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object -First 15 |
Select-Object @{N='Process';E={$_.ProcessName}},Id,
@{N='MemoryMB';E={[math]::Round($_.WorkingSet64/1MB)}} | Format-Table -AutoSize
}
Show-Progress "Startup Programs"
Add-Section "Startup Programs" {
Get-CimInstance Win32_StartupCommand | Select-Object Name,Command,Location | Format-Table -AutoSize -Wrap
}
Show-Progress "Drivers"
Add-Section "Drivers - non-Microsoft, oldest first" {
Get-CimInstance Win32_PnPSignedDriver |
Where-Object { $_.DriverProviderName -and $_.DriverProviderName -notlike '*Microsoft*' } |
Sort-Object DriverDate |
Select-Object DeviceName,DriverVersion,DriverDate,DriverProviderName | Format-Table -AutoSize
}
Show-Progress "Disk Health"
Add-Section "Disk Health" {
Get-PhysicalDisk |
Select-Object FriendlyName,MediaType,BusType,HealthStatus,OperationalStatus,
@{N='SizeGB';E={[math]::Round($_.Size/1GB)}}
}
Show-Progress "Free Space"
Add-Section "Free Space (90%+ used = slow PC and failed updates)" {
Get-Volume | Where-Object { $_.DriveLetter -and $_.Size } | ForEach-Object {
$used = [math]::Round((1 - $_.SizeRemaining / $_.Size) * 100, 1)
[PSCustomObject]@{
Drive = "$($_.DriveLetter):"
Label = $_.FileSystemLabel
SizeGB = [math]::Round($_.Size/1GB,1)
FreeGB = [math]::Round($_.SizeRemaining/1GB,1)
UsedPercent = $used
Status = if ($used -ge 90) { "CRITICAL - very low space" } elseif ($used -ge 75) { "WARNING - getting full" } else { "OK" }
}
} | Format-Table -AutoSize
}
Show-Progress "Disk Response Time"
Add-Section "Disk Response Time (average of 5 seconds; over 0.050 sec = slow disk)" {
$samples = Get-Counter '\PhysicalDisk(_Total)\Avg. Disk sec/Transfer' -SampleInterval 1 -MaxSamples 5 -ErrorAction SilentlyContinue
if ($samples) {
$vals = $samples.CounterSamples | ForEach-Object { $_.CookedValue }
[PSCustomObject]@{
AverageSec = [math]::Round(($vals | Measure-Object -Average).Average, 4)
WorstSec = [math]::Round(($vals | Measure-Object -Maximum).Maximum, 4)
}
} else {
"Performance counter not available on this PC"
}
}
Show-Progress "System Errors"
Add-Section "System Errors - last 7 days (newest 60)" {
Get-WinEvent -FilterHashtable @{ LogName='System'; Level=1,2; StartTime=(Get-Date).AddDays(-7) } -MaxEvents 60 -ErrorAction SilentlyContinue |
Select-Object TimeCreated,ProviderName,Id,LevelDisplayName,@{N='Message';E={ Get-ShortMessage $_.Message }} |
Format-Table -AutoSize -Wrap
}
Show-Progress "Unexpected Shutdowns"
Add-Section "Unexpected Shutdowns / CPU Throttling - last 30 days" {
# 41 = unexpected restart, 6008 = previous shutdown was unexpected,
# 37 = CPU speed limited by firmware (often heat or power)
Get-WinEvent -FilterHashtable @{ LogName='System'; Id=41,6008,37; StartTime=(Get-Date).AddDays(-30) } -MaxEvents 50 -ErrorAction SilentlyContinue |
Select-Object TimeCreated,Id,ProviderName,@{N='Message';E={ Get-ShortMessage $_.Message }} |
Format-Table -AutoSize -Wrap
}
Show-Progress "Battery"
Add-Section "Battery" {
$bat = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue
if ($bat) {
$bat | Select-Object Name,EstimatedChargeRemaining,BatteryStatus
"Full battery health report saved as BatteryReport.html in this folder."
} else {
"No battery found (desktop PC)."
}
}
if (Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue) {
try { powercfg /batteryreport /output (Join-Path $Folder 'BatteryReport.html') 2>&1 | Out-Null } catch {}
}
Show-Progress "Windows Updates"
Add-Section "Most recent Windows updates" {
Get-HotFix -ErrorAction SilentlyContinue | Where-Object InstalledOn |
Sort-Object InstalledOn -Descending | Select-Object -First 8 HotFixID,Description,InstalledOn
}
Show-Progress "Power Plan"
Add-Section "Power Plan" { powercfg /getactivescheme }
Show-Progress "Uptime"
Add-Section "Uptime (weeks without a restart can slow a PC down)" {
$os = Get-CimInstance Win32_OperatingSystem
[PSCustomObject]@{
LastBoot = $os.LastBootUpTime
UptimeDays = [math]::Round(((Get-Date)-$os.LastBootUpTime).TotalDays,1)
}
}
# ============================================================================
# FINISH - zip, show the result, wait for Enter
# ============================================================================
Write-Host ""
Write-Host "Zipping the report..." -ForegroundColor DarkGray
$ZipPath = "$Folder.zip"
try { Compress-Archive -Path (Join-Path $Folder '*') -DestinationPath $ZipPath -Force -ErrorAction Stop }
catch { $ZipPath = $null }
Write-Host ""
if (Test-Path $ReportPath) {
Write-Host "========================================" -ForegroundColor Green
Write-Host "DONE. The report is in your Downloads folder:" -ForegroundColor Green
Write-Host " $Folder" -ForegroundColor Green
if ($ZipPath) {
Write-Host "Send this one file to your IT support person:" -ForegroundColor Green
Write-Host " $ZipPath" -ForegroundColor Green
}
Write-Host "Check the report before sharing - it includes your computer name, username and serial number." -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Start-Process explorer.exe -ArgumentList "/select,`"$(if ($ZipPath) { $ZipPath } else { $ReportPath })`""
} else {
Write-Host "SOMETHING WENT WRONG - the report file was not created." -ForegroundColor Red
Write-Host "Expected location: $ReportPath" -ForegroundColor Red
}
# Kept on one line so a pasted run pauses here correctly.
Wait-BeforeClose; exit
A slow PC usually has one main cause: a program hogging the CPU, too little free space, a failing disk, or too many things starting with Windows. This script checks all of them in about a minute and zips the results into one file.
When to use it
- The PC is slow to start or open programs
- It freezes, or the fan is loud all the time
- It restarts or shuts down on its own
What it checks
- What's using the CPU and memory right now
- Programs that start with Windows
- Disk health, free space and disk speed
- System errors and unexpected shutdowns
- Battery health (laptops), recent updates and uptime
It doesn't change anything on your PC, and nothing is sent anywhere.
Reading the report
Open PCHealthReport.txt and check these first:
- Free Space: anything marked CRITICAL or WARNING. Free up space, or ask us.
- Disk Health: anything other than Healthy means back up now.
- Top Processes by CPU: one program using a lot is usually the cause.
- Uptime: more than 7 days? Restart the PC.
Not sure what it means? Send it to us and we'll take a look.
Privacy
The report includes your computer name, Windows username and the PC's serial number. Only share it with people you trust.