Last active
February 11, 2026 15:22
-
-
Save michaelsanford/b74359375a617122bcf375efa20f41b6 to your computer and use it in GitHub Desktop.
Windows 11 Maintenance (DISM, SFC, compact VM disks)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Performs comprehensive Windows system maintenance tasks. | |
| .DESCRIPTION | |
| Runs a series of maintenance operations including Windows Defender scans, system integrity checks, | |
| disk optimization, VHDX optimization, temporary file cleanup, and system error reporting. | |
| All operations are logged to a timestamped file on the Desktop. | |
| Captures performance metrics before and after maintenance. | |
| .PARAMETER Force | |
| Skips all confirmation prompts. Use for unattended/automated execution. | |
| If a reboot is pending, automatically reboots without prompting. | |
| .PARAMETER WhatIf | |
| Shows what operations would be performed without actually executing them. | |
| .PARAMETER Shutdown | |
| Performs a full shutdown (/s /g) after maintenance completes. Prompts for confirmation unless -Force is used. | |
| .EXAMPLE | |
| .\Start-Maintenance.ps1 | |
| Runs maintenance interactively with confirmation prompts. | |
| .EXAMPLE | |
| .\Start-Maintenance.ps1 -Force | |
| Runs maintenance without any prompts (unattended mode). | |
| .EXAMPLE | |
| .\Start-Maintenance.ps1 -WhatIf | |
| Previews all maintenance operations without executing them. | |
| .EXAMPLE | |
| .\Start-Maintenance.ps1 -Force -Shutdown | |
| Runs maintenance and performs a full shutdown without prompts. | |
| .NOTES | |
| Requires Administrator privileges. | |
| Log files are saved to: $env:USERPROFILE\Desktop\WindowsMaintenance_<timestamp>.log | |
| #> | |
| #Requires -RunAsAdministrator | |
| param( | |
| [switch]$Force, | |
| [switch]$WhatIf, | |
| [switch]$Shutdown | |
| ) | |
| $ErrorActionPreference = 'Stop' | |
| $script:hasErrors = $false | |
| $logFile = "$env:USERPROFILE\Desktop\WindowsMaintenance_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" | |
| if (-not $WhatIf) { | |
| Start-Transcript -Path $logFile -Append | |
| } | |
| function Test-PendingReboot { | |
| $rebootPending = $false | |
| # Check Component Based Servicing | |
| if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -ErrorAction SilentlyContinue) { | |
| $rebootPending = $true | |
| } | |
| # Check Windows Update | |
| if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -ErrorAction SilentlyContinue) { | |
| $rebootPending = $true | |
| } | |
| # Check PendingFileRenameOperations | |
| $pendingFileRename = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -ErrorAction SilentlyContinue | |
| if ($pendingFileRename) { | |
| $rebootPending = $true | |
| } | |
| return $rebootPending | |
| } | |
| "Starting Windows maintenance routine..." | |
| "Start time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | |
| "" | |
| # Capture baseline performance metrics | |
| "Capturing baseline performance metrics..." | |
| $bootTime = (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime | |
| $uptime = (Get-Date) - $bootTime | |
| $memoryBefore = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object FreePhysicalMemory, TotalVisibleMemorySize | |
| "System uptime: $($uptime.Days) days, $($uptime.Hours) hours, $($uptime.Minutes) minutes" | |
| "Free memory: $([math]::Round($memoryBefore.FreePhysicalMemory / 1MB, 2)) GB of $([math]::Round($memoryBefore.TotalVisibleMemorySize / 1MB, 2)) GB" | |
| "" | |
| # Check and shutdown WSL if enabled | |
| Write-Progress -Activity "Windows Maintenance" -Status "Checking WSL status" -PercentComplete 5 | |
| $wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -ErrorAction SilentlyContinue | |
| if ($wslFeature -and $wslFeature.State -eq 'Enabled') { | |
| if ($Force -or $WhatIf -or $PSCmdlet.ShouldContinue("Shut down WSL to release VHDX locks for optimization?", "WSL Shutdown")) { | |
| "Shutting down WSL..." | |
| if (-not $WhatIf) { | |
| wsl --shutdown | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Warning "WSL shutdown failed with exit code $LASTEXITCODE" | |
| $script:hasErrors = $true | |
| } | |
| } | |
| } | |
| } | |
| $drives = Get-Volume | Where-Object { | |
| $_.DriveLetter -match '^[A-Z]$' -and $_.FileSystem -ne $null -and $_.DriveType -eq "Fixed" | |
| } | |
| # Windows Defender scan | |
| Write-Progress -Activity "Windows Maintenance" -Status "Running Windows Defender scan" -PercentComplete 10 | |
| "Running Windows Defender quick scan..." | |
| if (-not $WhatIf) { | |
| try { | |
| Start-MpScan -ScanType QuickScan | |
| } catch { | |
| Write-Warning "Defender scan failed: $_" | |
| $script:hasErrors = $true | |
| } | |
| } | |
| # DISM | |
| Write-Progress -Activity "Windows Maintenance" -Status "Running DISM restore health (this may take 15-20 minutes)" -PercentComplete 20 | |
| "Running DISM restore health (this may take 15-20 minutes)..." | |
| if (-not $WhatIf) { | |
| $dismResult = DISM /Online /Cleanup-Image /RestoreHealth | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Warning "DISM failed with exit code $LASTEXITCODE" | |
| $script:hasErrors = $true | |
| } | |
| } | |
| # SFC | |
| Write-Progress -Activity "Windows Maintenance" -Status "Running system file checker (this may take 10-15 minutes)" -PercentComplete 40 | |
| "Running system file checker (this may take 10-15 minutes)..." | |
| if (-not $WhatIf) { | |
| $sfcResult = sfc /scannow | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Warning "SFC failed with exit code $LASTEXITCODE" | |
| $script:hasErrors = $true | |
| } | |
| } | |
| # Chkdsk | |
| $driveCount = $drives.Count | |
| $driveIndex = 0 | |
| foreach ($drive in $drives) { | |
| $letter = $drive.DriveLetter | |
| $percent = 50 + (($driveIndex / $driveCount) * 10) | |
| Write-Progress -Activity "Windows Maintenance" -Status "Running chkdsk on drive $letter" -PercentComplete $percent | |
| "Running chkdsk on drive $letter..." | |
| if (-not $WhatIf) { | |
| chkdsk.exe /scan "${letter}:" | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Warning "Chkdsk on ${letter}: failed with exit code $LASTEXITCODE" | |
| $script:hasErrors = $true | |
| } | |
| } | |
| $driveIndex++ | |
| } | |
| # Optimize VHDX files | |
| Write-Progress -Activity "Windows Maintenance" -Status "Optimizing VHDX files" -PercentComplete 60 | |
| "Optimizing VHDX files..." | |
| $vhdxPaths = @( | |
| "$Env:LOCALAPPDATA", | |
| "$Env:USERPROFILE\AppData\Local\Packages" | |
| ) | |
| foreach ($path in $vhdxPaths) { | |
| if (Test-Path $path) { | |
| Get-ChildItem -Path $path -Recurse -Filter *.vhdx -ErrorAction SilentlyContinue | ForEach-Object { | |
| "Optimizing VHD: $($_.FullName)" | |
| if (-not $WhatIf) { | |
| try { | |
| Optimize-VHD -Path $_.FullName -Mode Full | |
| } catch { | |
| Write-Warning "Failed to optimize $($_.FullName): $_" | |
| $script:hasErrors = $true | |
| } | |
| } | |
| } | |
| } | |
| } | |
| # Optimize volumes | |
| $driveIndex = 0 | |
| foreach ($drive in $drives) { | |
| $letter = $drive.DriveLetter | |
| $percent = 70 + (($driveIndex / $driveCount) * 15) | |
| Write-Progress -Activity "Windows Maintenance" -Status "Optimizing drive $letter" -PercentComplete $percent | |
| "Optimizing drive $letter..." | |
| if (-not $WhatIf) { | |
| try { | |
| Optimize-Volume -DriveLetter $letter -NormalPriority -Verbose | |
| } catch { | |
| Write-Warning "Failed to optimize drive ${letter}: $_" | |
| $script:hasErrors = $true | |
| } | |
| } | |
| $driveIndex++ | |
| } | |
| # Clean temp files older than 7 days | |
| Write-Progress -Activity "Windows Maintenance" -Status "Cleaning temporary files" -PercentComplete 85 | |
| "Cleaning temporary files older than 7 days..." | |
| if (-not $WhatIf) { | |
| $cutoffDate = (Get-Date).AddDays(-7) | |
| Get-ChildItem "$env:TEMP" -Recurse -ErrorAction SilentlyContinue | | |
| Where-Object { $_.LastWriteTime -lt $cutoffDate } | | |
| Remove-Item -Force -Recurse -ErrorAction SilentlyContinue | |
| } | |
| # Disk space report | |
| Write-Progress -Activity "Windows Maintenance" -Status "Generating disk space report" -PercentComplete 90 | |
| "" | |
| "Disk space report:" | |
| foreach ($drive in $drives) { | |
| $letter = $drive.DriveLetter | |
| $freeGB = "{0:N1}" -f ($drive.SizeRemaining / 1GB) | |
| $totalGB = "{0:N1}" -f ($drive.Size / 1GB) | |
| "Drive ${letter}: $freeGB GB free of $totalGB GB" | |
| } | |
| # Review system errors | |
| Write-Progress -Activity "Windows Maintenance" -Status "Reviewing system errors" -PercentComplete 95 | |
| "" | |
| "Reviewing recent system errors..." | |
| try { | |
| $errors = Get-WinEvent -LogName System -FilterXPath "*[System[(Level=1 or Level=2) and TimeCreated[timediff(@SystemTime) <= 86400000]]]" -ErrorAction SilentlyContinue | |
| if ($errors.Count -gt 0) { | |
| $grouped = $errors | Group-Object Id | Sort-Object Count -Descending | Select-Object -First 10 | |
| "Top 10 error types in the last 24 hours:" | |
| $grouped | ForEach-Object { | |
| " Event ID $($_.Name): $($_.Count) occurrence(s)" | |
| " Sample: $($_.Group[0].Message.Split("`n")[0])" | |
| } | |
| } else { | |
| "No critical or error-level events in the last 24 hours." | |
| } | |
| } catch { | |
| Write-Warning "Failed to retrieve event log: $_" | |
| $script:hasErrors = $true | |
| } | |
| # Update Oh My Posh | |
| if (Get-AppxPackage -Name 'ohmyposh.cli' -ErrorAction SilentlyContinue) { | |
| "Updating Oh My Posh..." | |
| if (-not $WhatIf) { | |
| winget upgrade JanDeDobbeleer.OhMyPosh --source winget | |
| } | |
| } | |
| # if (Get-Module -ListAvailable -Name PSWindowsUpdate) { | |
| # "Checking for Windows Updates..." | |
| # Import-Module PSWindowsUpdate | |
| # Get-WindowsUpdate -AcceptAll -Install -AutoReboot | |
| # } else { | |
| # "PSWindowsUpdate module not found. Skipping update check." | |
| # } | |
| Write-Progress -Activity "Windows Maintenance" -Status "Complete" -PercentComplete 100 -Completed | |
| # Capture post-maintenance metrics | |
| "" | |
| "Capturing post-maintenance performance metrics..." | |
| $memoryAfter = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object FreePhysicalMemory | |
| $memoryFreed = [math]::Round(($memoryAfter.FreePhysicalMemory - $memoryBefore.FreePhysicalMemory) / 1MB, 2) | |
| "Free memory after: $([math]::Round($memoryAfter.FreePhysicalMemory / 1MB, 2)) GB (change: $memoryFreed GB)" | |
| "" | |
| "End time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | |
| "Windows maintenance complete!" | |
| # Check for pending reboot | |
| $rebootPending = Test-PendingReboot | |
| if ($rebootPending) { | |
| "" | |
| Write-Warning "A system reboot is pending to complete maintenance operations." | |
| if ($Force) { | |
| "Initiating automatic reboot due to -Force flag..." | |
| if (-not $WhatIf) { | |
| Stop-Transcript | |
| shutdown /r /t 30 /c "Maintenance complete - reboot required to finalize repairs" | |
| exit 0 | |
| } | |
| } else { | |
| if ($PSCmdlet.ShouldContinue("A reboot is required to complete maintenance. Reboot now?", "Pending Reboot")) { | |
| if (-not $WhatIf) { | |
| Stop-Transcript | |
| shutdown /r /t 30 /c "Maintenance complete - reboot required to finalize repairs" | |
| exit 0 | |
| } | |
| } | |
| } | |
| } | |
| if (-not $WhatIf) { | |
| Stop-Transcript | |
| } | |
| if ($Shutdown) { | |
| if ($Force -or $WhatIf -or $PSCmdlet.ShouldContinue("Perform a full shutdown (clears hibernation file)?", "System Shutdown")) { | |
| "Initiating full shutdown..." | |
| if (-not $WhatIf) { | |
| shutdown /s /g /t 10 /c "Maintenance complete - performing full shutdown" | |
| } | |
| } | |
| } | |
| # Exit with appropriate code | |
| if ($script:hasErrors) { | |
| exit 1 | |
| } else { | |
| exit 0 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment