What it does
Automates the standard Windows corruption repair workflow: runs DISM to repair the component store, then runs SFC to scan and repair corrupted system files. Checks for admin rights before running, logs every step with timestamps, and gives a plain-language summary of whether corruption was found and fixed — no scrolling through raw console output to figure out what happened.
Requirements
- Windows 10 or Windows 11
- Must be run from an elevated (Administrator) PowerShell session
- Internet access for DISM to pull repair files via Windows Update (unless using -Source with local media)
Usage
.\Repair-WindowsSystemFiles.ps1
.\Repair-WindowsSystemFiles.ps1 -Source D:\
.\Repair-WindowsSystemFiles.ps1 -SkipDism
The script
<#
.SYNOPSIS
Automates the DISM + SFC Windows system file repair workflow.
.DESCRIPTION
Runs DISM /Online /Cleanup-Image /RestoreHealth to repair the component store,
then runs SFC /scannow to scan and repair corrupted Windows system files.
Logs all output with timestamps and prints a summary at the end. Optionally
prompts to restart the machine if repairs were made.
.PARAMETER Source
Optional. Path to recovery media or a mounted Windows image to use as the
repair source, for cases where Windows Update is broken and DISM can't pull
files online. Example: -Source D:\ or -Source E:\RepairSource\ExtraFolder
.PARAMETER SkipDism
Optional switch. Skips the DISM step and runs SFC only.
.PARAMETER LogPath
Optional. Where to write the log file. Defaults to
C:\Windows\Temp\Repair-WindowsSystemFiles_<timestamp>.log
.EXAMPLE
.\Repair-WindowsSystemFiles.ps1
.EXAMPLE
.\Repair-WindowsSystemFiles.ps1 -Source D:\ -Verbose
.EXAMPLE
.\Repair-WindowsSystemFiles.ps1 -SkipDism
.NOTES
Must be run from an elevated (Administrator) PowerShell session.
DISM can take a long time and may appear to sit at 50-70% for a while —
this is normal. Do not close the window.
#>
[CmdletBinding()]
param(
[string]$Source,
[switch]$SkipDism,
[string]$LogPath = "C:\Windows\Temp\Repair-WindowsSystemFiles_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
)
function Write-Log {
param(
[Parameter(Mandatory)][string]$Message,
[ValidateSet('INFO','WARN','ERROR')][string]$Level = 'INFO'
)
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$line = "[$timestamp] [$Level] $Message"
Add-Content -Path $LogPath -Value $line
switch ($Level) {
'WARN' { Write-Warning $Message }
'ERROR' { Write-Error $Message }
default { Write-Host $line }
}
}
# --- Admin check ---
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Error "This script must be run as Administrator. Right-click PowerShell and choose 'Run as administrator', then re-run this script."
exit 1
}
Write-Log "=== Windows System File Repair started ==="
Write-Log "Log file: $LogPath"
$results = [ordered]@{
DismRan = $false
DismExitCode = $null
SfcExitCode = $null
CorruptionFound = $null
RepairsMade = $null
}
# --- Step 1: DISM RestoreHealth ---
if (-not $SkipDism) {
Write-Log "Starting DISM /Online /Cleanup-Image /RestoreHealth. This can take a long time and may sit at 50-70% for a while — that is normal."
$dismArgs = @('/Online', '/Cleanup-Image', '/RestoreHealth')
if ($Source) {
$dismArgs += "/Source:$Source"
$dismArgs += '/LimitAccess'
Write-Log "Using repair source: $Source"
}
try {
$dismProcess = Start-Process -FilePath 'DISM.exe' -ArgumentList $dismArgs -NoNewWindow -Wait -PassThru `
-RedirectStandardOutput "$env:TEMP\dism_stdout.log" -RedirectStandardError "$env:TEMP\dism_stderr.log"
$results.DismRan = $true
$results.DismExitCode = $dismProcess.ExitCode
Get-Content "$env:TEMP\dism_stdout.log" -ErrorAction SilentlyContinue | ForEach-Object { Write-Log "DISM: $_" }
if ($dismProcess.ExitCode -eq 0) {
Write-Log "DISM completed successfully (exit code 0)."
}
else {
Write-Log "DISM finished with exit code $($dismProcess.ExitCode). This may indicate DISM could not fully repair the component store, or Windows Update is unreachable — consider re-running with -Source pointing at installation media." -Level WARN
}
}
catch {
Write-Log "DISM failed to run: $($_.Exception.Message)" -Level ERROR
$results.DismExitCode = -1
}
}
else {
Write-Log "Skipping DISM step (-SkipDism specified)."
}
# --- Step 2: SFC scan ---
Write-Log "Starting SFC /scannow. This can take a while depending on system speed — it may pause at points, this is normal."
try {
# sfc.exe output is UTF-16LE and Start-Process redirection can mangle it,
# so we capture it directly via cmd /c and parse the text output.
$sfcOutput = & cmd.exe /c "sfc /scannow" 2>&1
$results.SfcExitCode = $LASTEXITCODE
$sfcOutput | ForEach-Object { Write-Log "SFC: $_" }
$sfcText = $sfcOutput -join "`n"
if ($sfcText -match 'did not find any integrity violations') {
$results.CorruptionFound = $false
$results.RepairsMade = $false
Write-Log "SFC found no integrity violations."
}
elseif ($sfcText -match 'successfully repaired') {
$results.CorruptionFound = $true
$results.RepairsMade = $true
Write-Log "SFC found corruption and successfully repaired it."
}
elseif ($sfcText -match 'found corrupt files but was unable to fix') {
$results.CorruptionFound = $true
$results.RepairsMade = $false
Write-Log "SFC found corruption it could NOT repair. Check CBS.log (C:\Windows\Logs\CBS\CBS.log) for details, or consider re-running DISM with -Source pointing at installation media." -Level WARN
}
else {
Write-Log "Could not parse a definitive result from SFC output — review the log for details." -Level WARN
}
}
catch {
Write-Log "SFC failed to run: $($_.Exception.Message)" -Level ERROR
$results.SfcExitCode = -1
}
# --- Summary ---
Write-Log "=== Summary ==="
Write-Log "DISM exit code: $($results.DismExitCode)"
Write-Log "SFC exit code: $($results.SfcExitCode)"
Write-Log "Corruption found: $($results.CorruptionFound)"
Write-Log "Repairs made: $($results.RepairsMade)"
Write-Log "Full log saved to: $LogPath"
Write-Host ""
Write-Host "=========================================="
Write-Host " Windows System File Repair — Summary"
Write-Host "=========================================="
Write-Host " DISM exit code : $($results.DismExitCode)"
Write-Host " SFC exit code : $($results.SfcExitCode)"
Write-Host " Corruption found: $($results.CorruptionFound)"
Write-Host " Repairs made : $($results.RepairsMade)"
Write-Host " Log file : $LogPath"
Write-Host "=========================================="
# --- Optional restart prompt ---
if ($results.RepairsMade -eq $true) {
$answer = Read-Host "Repairs were made. It's recommended to restart the PC now. Restart now? (Y/N)"
if ($answer -match '^[Yy]') {
Write-Log "User confirmed restart."
Restart-Computer -Force
}
else {
Write-Log "User declined restart. Remember to restart the PC manually to complete the repair."
}
}
Notes
DISM can sit at 50-70% progress for a long time — this is normal, don’t close the window. If Windows Update is broken, DISM will fail without a -Source pointing at installation media or a mounted image. SFC results and any unrepaired corruption details are also written to C:\Windows\Logs\CBS\CBS.log.