feat(template): cleanup-after-update + ruff fixes
Move disk cleanup out of the Windows Update loop: - PS scripts: extract Invoke-DiskCleanup function, add -CleanupOnly switch for standalone post-update cleanup pass. - Python command: always pass -SkipCleanup:$true during WU loop; after loop exits 0, run a separate -CleanupOnly invocation (skipped if --skip-cleanup). Fails fast if cleanup-only returns nonzero. - Tests: 3 new tests for cleanup-runs-after-loop, skip-cleanup suppresses it, and cleanup-failure propagates as ClickException. - ruff --fix: remove unused shutil import, sort imports, fix UP037/UP035. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -148,6 +148,11 @@ param(
|
||||
# Skip disk cleanup step (cleanmgr + DISM + cache clear) — speeds up re-runs during dev/debug
|
||||
[switch] $SkipCleanup,
|
||||
|
||||
# Run disk cleanup only — skip all provisioning steps and jump straight to the
|
||||
# cleanup section. Intended for use by Prepare-WinBuild2022.ps1 after the Windows
|
||||
# Update loop completes, so cleanup always runs on the fully-updated system.
|
||||
[switch] $CleanupOnly,
|
||||
|
||||
# Administrator password — used only to write DefaultPassword for autologin.
|
||||
# Optional: if empty, DefaultPassword registry value is left as-is (Deploy may have set it).
|
||||
[string] $AdminPassword = '',
|
||||
@@ -231,6 +236,85 @@ function Assert-Hash {
|
||||
Write-Host " [OK] SHA256 verified: $Label" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Invoke-DiskCleanup {
|
||||
Write-Step "Cleaning up disk"
|
||||
|
||||
$cleanKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
|
||||
$sageset = 1
|
||||
$cleanCategories = @(
|
||||
'Active Setup Temp Folders',
|
||||
'BranchCache',
|
||||
'Downloaded Program Files',
|
||||
'Internet Cache Files',
|
||||
'Memory Dump Files',
|
||||
'Old ChkDsk Files',
|
||||
'Previous Installations',
|
||||
'Recycle Bin',
|
||||
'Service Pack Cleanup',
|
||||
'Setup Log Files',
|
||||
'System error memory dump files',
|
||||
'System error minidump files',
|
||||
'Temporary Files',
|
||||
'Temporary Setup Files',
|
||||
'Thumbnail Cache',
|
||||
'Update Cleanup',
|
||||
'Upgrade Discarded Files',
|
||||
'Windows Defender',
|
||||
'Windows Error Reporting Archive Files',
|
||||
'Windows Error Reporting Files',
|
||||
'Windows Error Reporting Queue Files',
|
||||
'Windows Error Reporting Temp Files',
|
||||
'Windows ESD installation files',
|
||||
'Windows Upgrade Log Files'
|
||||
)
|
||||
foreach ($cat in $cleanCategories) {
|
||||
$keyPath = "$cleanKey\$cat"
|
||||
if (Test-Path $keyPath) {
|
||||
Set-ItemProperty -Path $keyPath -Name "StateFlags$('{0:D4}' -f $sageset)" -Value 2 -Type DWord -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
Write-Host "Running cleanmgr /sagerun:$sageset (may take a few minutes)..."
|
||||
$proc = Start-Process -FilePath 'cleanmgr.exe' -ArgumentList "/sagerun:$sageset" -Wait -PassThru
|
||||
Write-Host "cleanmgr exited (code $($proc.ExitCode))."
|
||||
|
||||
Write-Host "Stopping WU-adjacent services before cache clear..."
|
||||
foreach ($svc in 'wuauserv', 'UsoSvc', 'bits', 'dosvc', 'TrustedInstaller') {
|
||||
Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
$sdDownload = 'C:\Windows\SoftwareDistribution\Download'
|
||||
Remove-Item $sdDownload -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Path $sdDownload -Force | Out-Null
|
||||
Write-Host "Windows Update download cache cleared."
|
||||
|
||||
Remove-Item 'C:\Windows\Logs\CBS\*' -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item 'C:\Windows\Temp\*' -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item 'C:\Windows\Prefetch\*' -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host "Running DISM component store cleanup (StartComponentCleanup)..."
|
||||
$dism = Start-Process -FilePath 'dism.exe' `
|
||||
-ArgumentList '/Online','/Cleanup-Image','/StartComponentCleanup','/ResetBase' `
|
||||
-Wait -PassThru
|
||||
Write-Host "DISM exited (code $($dism.ExitCode))."
|
||||
Write-Host "Disk cleanup complete."
|
||||
|
||||
foreach ($svc in 'wuauserv', 'UsoSvc') {
|
||||
Set-Service -Name $svc -StartupType Disabled -ErrorAction SilentlyContinue
|
||||
}
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\wuauserv' `
|
||||
-Name Start -Value 4 -Type DWord -Force -ErrorAction SilentlyContinue
|
||||
|
||||
$sdFiles = Get-ChildItem 'C:\Windows\SoftwareDistribution\Download' -Recurse -File -Force -ErrorAction SilentlyContinue
|
||||
$sdBytes = [long]0
|
||||
if ($sdFiles) { foreach ($f in $sdFiles) { $sdBytes += $f.Length } }
|
||||
$sdMB = [math]::Round($sdBytes / 1MB, 1)
|
||||
Write-Host " SoftwareDistribution\Download: $(@($sdFiles).Count) file(s), ${sdMB} MB remaining (WaaSMedicSvc best-effort, WU is disabled)."
|
||||
|
||||
Assert-Step 'Cleanup' 'wuauserv StartType Disabled after cache clear' {
|
||||
(Get-Service wuauserv -ErrorAction Stop).StartType -eq 'Disabled'
|
||||
}
|
||||
}
|
||||
|
||||
# ── Pinned SHA256 hashes for downloaded installers (§1.3) ─────────────────────
|
||||
# Fill in the hash values below. Get them by:
|
||||
# (Invoke-WebRequest '<URL>' -UseBasicParsing -OutFile 'tmp.bin'; (Get-FileHash 'tmp.bin' -Algorithm SHA256).Hash)
|
||||
@@ -272,6 +356,18 @@ $script:Hashes = @{
|
||||
# vcpkg: cloned from GitHub via TLS; pin integrity with $VcpkgRef (commit SHA)
|
||||
}
|
||||
|
||||
# ── CleanupOnly early-exit ────────────────────────────────────────────────────
|
||||
if ($CleanupOnly) {
|
||||
if ($SkipCleanup) {
|
||||
Write-Host "[Setup] -CleanupOnly + -SkipCleanup: nothing to do." -ForegroundColor Yellow
|
||||
exit 0
|
||||
}
|
||||
Write-Host "[Setup] CleanupOnly mode — running disk cleanup on fully-updated system." -ForegroundColor Cyan
|
||||
Invoke-DiskCleanup
|
||||
Write-Host "[Setup] CleanupOnly complete." -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ── Pre-flight: remove temp files from any previous partial run ───────────────
|
||||
# These files are normally deleted at the end of each step that creates them.
|
||||
# A previous interrupted run may have left them behind — remove before starting.
|
||||
@@ -1527,114 +1623,9 @@ Write-Host "Tier-2 Toolchain installation complete." -ForegroundColor Green
|
||||
# ── Cleanup ───────────────────────────────────────────────────────────────────
|
||||
if ($SkipCleanup) {
|
||||
Write-Host "`n[Setup] Skipping disk cleanup (-SkipCleanup switch set)." -ForegroundColor Yellow
|
||||
} else {
|
||||
Invoke-DiskCleanup
|
||||
}
|
||||
else {
|
||||
Write-Step "Cleaning up disk"
|
||||
|
||||
# 1. Windows Disk Cleanup (cleanmgr) — run with /sagerun:1 using preset flags
|
||||
# First register the flags via /sageset:1 in the registry (silent, no UI)
|
||||
$cleanKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
|
||||
$sageset = 1
|
||||
$cleanCategories = @(
|
||||
'Active Setup Temp Folders',
|
||||
'BranchCache',
|
||||
'Downloaded Program Files',
|
||||
'Internet Cache Files',
|
||||
'Memory Dump Files',
|
||||
'Old ChkDsk Files',
|
||||
'Previous Installations',
|
||||
'Recycle Bin',
|
||||
'Service Pack Cleanup',
|
||||
'Setup Log Files',
|
||||
'System error memory dump files',
|
||||
'System error minidump files',
|
||||
'Temporary Files',
|
||||
'Temporary Setup Files',
|
||||
'Thumbnail Cache',
|
||||
'Update Cleanup',
|
||||
'Upgrade Discarded Files',
|
||||
'Windows Defender',
|
||||
'Windows Error Reporting Archive Files',
|
||||
'Windows Error Reporting Files',
|
||||
'Windows Error Reporting Queue Files',
|
||||
'Windows Error Reporting Temp Files',
|
||||
'Windows ESD installation files',
|
||||
'Windows Upgrade Log Files'
|
||||
)
|
||||
foreach ($cat in $cleanCategories) {
|
||||
$keyPath = "$cleanKey\$cat"
|
||||
if (Test-Path $keyPath) {
|
||||
Set-ItemProperty -Path $keyPath -Name "StateFlags$('{0:D4}' -f $sageset)" -Value 2 -Type DWord -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
Write-Host "Running cleanmgr /sagerun:$sageset (may take a few minutes)..."
|
||||
$proc = Start-Process -FilePath 'cleanmgr.exe' -ArgumentList "/sagerun:$sageset" -Wait -PassThru
|
||||
Write-Host "cleanmgr exited (code $($proc.ExitCode))."
|
||||
|
||||
# 2. Clear Windows Update cache
|
||||
# Stop all services that might hold file locks inside SoftwareDistribution:
|
||||
# wuauserv — Windows Update (already Disabled from Step 6b, stop is idempotent)
|
||||
# UsoSvc — Update Orchestrator (already Disabled from Step 6b)
|
||||
# bits — Background Intelligent Transfer Service (downloads WU payloads)
|
||||
# dosvc — Delivery Optimization (peer-to-peer WU cache)
|
||||
# TrustedInstaller — Windows Modules Installer (unpacks .cab/.msu update packages)
|
||||
Write-Host "Stopping WU-adjacent services before cache clear..."
|
||||
foreach ($svc in 'wuauserv', 'UsoSvc', 'bits', 'dosvc', 'TrustedInstaller') {
|
||||
Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# Delete and recreate the Download folder — more reliable than 'Remove-Item *'
|
||||
# because locked files inside subdirectories silently fail with wildcard delete.
|
||||
$sdDownload = 'C:\Windows\SoftwareDistribution\Download'
|
||||
Remove-Item $sdDownload -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Path $sdDownload -Force | Out-Null
|
||||
Write-Host "Windows Update download cache cleared."
|
||||
|
||||
# 3. Clear CBS / component store logs
|
||||
Remove-Item 'C:\Windows\Logs\CBS\*' -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 4. Clear TEMP folders
|
||||
Remove-Item 'C:\Windows\Temp\*' -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 5. Clear Prefetch
|
||||
Remove-Item 'C:\Windows\Prefetch\*' -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 6. Run DISM component store cleanup (removes superseded update components)
|
||||
Write-Host "Running DISM component store cleanup (StartComponentCleanup)..."
|
||||
$dism = Start-Process -FilePath 'dism.exe' `
|
||||
-ArgumentList '/Online','/Cleanup-Image','/StartComponentCleanup','/ResetBase' `
|
||||
-Wait -PassThru
|
||||
Write-Host "DISM exited (code $($dism.ExitCode))."
|
||||
|
||||
Write-Host "Disk cleanup complete."
|
||||
|
||||
# DISM StartComponentCleanup / WaaSMedicSvc may reset service StartType during cleanup.
|
||||
# Re-enforce Disabled on both WU services after DISM completes.
|
||||
foreach ($svc in 'wuauserv', 'UsoSvc') {
|
||||
Set-Service -Name $svc -StartupType Disabled -ErrorAction SilentlyContinue
|
||||
}
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\wuauserv' `
|
||||
-Name Start -Value 4 -Type DWord -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# Validation — leftover artifacts from this run should be gone
|
||||
# Note: do NOT assert SoftwareDistribution\Download empty.
|
||||
# WaaSMedicSvc (Windows Update Medic Service) is tamper-protected — cannot be stopped —
|
||||
# and actively restores WU files immediately after deletion. Files reappearing here is
|
||||
# normal and harmless: WU is permanently disabled by Step 6b (service Disabled + GPO keys).
|
||||
# Report remaining size as informational only.
|
||||
$sdFiles = Get-ChildItem 'C:\Windows\SoftwareDistribution\Download' -Recurse -File -Force -ErrorAction SilentlyContinue
|
||||
# Avoid Measure-Object .Sum: under Set-StrictMode -Version Latest, PS 5.1 treats the
|
||||
# nullable Sum property as absent when the pipe is empty, throwing "property not found".
|
||||
$sdBytes = [long]0
|
||||
if ($sdFiles) { foreach ($f in $sdFiles) { $sdBytes += $f.Length } }
|
||||
$sdMB = [math]::Round($sdBytes / 1MB, 1)
|
||||
Write-Host " SoftwareDistribution\Download: $(@($sdFiles).Count) file(s), ${sdMB} MB remaining (WaaSMedicSvc best-effort, WU is disabled)."
|
||||
|
||||
Assert-Step 'Cleanup' 'wuauserv StartType Disabled after cache clear' {
|
||||
(Get-Service wuauserv -ErrorAction Stop).StartType -eq 'Disabled'
|
||||
}
|
||||
} # end if (-not $SkipCleanup)
|
||||
|
||||
# ── Pre-Final re-affirmation ──────────────────────────────────────────────────
|
||||
# Steps 7-10 (installs, cleanup, DISM) can take hours. During that window background
|
||||
|
||||
@@ -148,6 +148,11 @@ param(
|
||||
# Skip disk cleanup step (cleanmgr + DISM + cache clear) — speeds up re-runs during dev/debug
|
||||
[switch] $SkipCleanup,
|
||||
|
||||
# Run disk cleanup only — skip all provisioning steps and jump straight to the
|
||||
# cleanup section. Intended for use by Prepare-WinBuild2025.ps1 after the Windows
|
||||
# Update loop completes, so cleanup always runs on the fully-updated system.
|
||||
[switch] $CleanupOnly,
|
||||
|
||||
# Administrator password — used only to write DefaultPassword for autologin.
|
||||
# Optional: if empty, DefaultPassword registry value is left as-is (Deploy may have set it).
|
||||
[string] $AdminPassword = '',
|
||||
@@ -231,6 +236,97 @@ function Assert-Hash {
|
||||
Write-Host " [OK] SHA256 verified: $Label" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Invoke-DiskCleanup {
|
||||
Write-Step "Cleaning up disk"
|
||||
|
||||
# 1. Windows Disk Cleanup (cleanmgr) — run with /sagerun:1 using preset flags
|
||||
# First register the flags via /sageset:1 in the registry (silent, no UI)
|
||||
$cleanKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
|
||||
$sageset = 1
|
||||
$cleanCategories = @(
|
||||
'Active Setup Temp Folders',
|
||||
'BranchCache',
|
||||
'Downloaded Program Files',
|
||||
'Internet Cache Files',
|
||||
'Memory Dump Files',
|
||||
'Old ChkDsk Files',
|
||||
'Previous Installations',
|
||||
'Recycle Bin',
|
||||
'Service Pack Cleanup',
|
||||
'Setup Log Files',
|
||||
'System error memory dump files',
|
||||
'System error minidump files',
|
||||
'Temporary Files',
|
||||
'Temporary Setup Files',
|
||||
'Thumbnail Cache',
|
||||
'Update Cleanup',
|
||||
'Upgrade Discarded Files',
|
||||
'Windows Defender',
|
||||
'Windows Error Reporting Archive Files',
|
||||
'Windows Error Reporting Files',
|
||||
'Windows Error Reporting Queue Files',
|
||||
'Windows Error Reporting Temp Files',
|
||||
'Windows ESD installation files',
|
||||
'Windows Upgrade Log Files'
|
||||
)
|
||||
foreach ($cat in $cleanCategories) {
|
||||
$keyPath = "$cleanKey\$cat"
|
||||
if (Test-Path $keyPath) {
|
||||
Set-ItemProperty -Path $keyPath -Name "StateFlags$('{0:D4}' -f $sageset)" -Value 2 -Type DWord -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
Write-Host "Running cleanmgr /sagerun:$sageset (may take a few minutes)..."
|
||||
$proc = Start-Process -FilePath 'cleanmgr.exe' -ArgumentList "/sagerun:$sageset" -Wait -PassThru
|
||||
Write-Host "cleanmgr exited (code $($proc.ExitCode))."
|
||||
|
||||
# 2. Clear Windows Update cache
|
||||
Write-Host "Stopping WU-adjacent services before cache clear..."
|
||||
foreach ($svc in 'wuauserv', 'UsoSvc', 'bits', 'dosvc', 'TrustedInstaller') {
|
||||
Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
$sdDownload = 'C:\Windows\SoftwareDistribution\Download'
|
||||
Remove-Item $sdDownload -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Path $sdDownload -Force | Out-Null
|
||||
Write-Host "Windows Update download cache cleared."
|
||||
|
||||
# 3. Clear CBS / component store logs
|
||||
Remove-Item 'C:\Windows\Logs\CBS\*' -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 4. Clear TEMP folders
|
||||
Remove-Item 'C:\Windows\Temp\*' -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 5. Clear Prefetch
|
||||
Remove-Item 'C:\Windows\Prefetch\*' -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 6. Run DISM component store cleanup (removes superseded update components)
|
||||
Write-Host "Running DISM component store cleanup (StartComponentCleanup)..."
|
||||
$dism = Start-Process -FilePath 'dism.exe' `
|
||||
-ArgumentList '/Online','/Cleanup-Image','/StartComponentCleanup','/ResetBase' `
|
||||
-Wait -PassThru
|
||||
Write-Host "DISM exited (code $($dism.ExitCode))."
|
||||
|
||||
Write-Host "Disk cleanup complete."
|
||||
|
||||
# DISM / WaaSMedicSvc may reset WU service StartType — re-enforce Disabled.
|
||||
foreach ($svc in 'wuauserv', 'UsoSvc') {
|
||||
Set-Service -Name $svc -StartupType Disabled -ErrorAction SilentlyContinue
|
||||
}
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\wuauserv' `
|
||||
-Name Start -Value 4 -Type DWord -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# Report remaining WU cache size (WaaSMedicSvc restores files — informational only).
|
||||
$sdFiles = Get-ChildItem 'C:\Windows\SoftwareDistribution\Download' -Recurse -File -Force -ErrorAction SilentlyContinue
|
||||
$sdBytes = [long]0
|
||||
if ($sdFiles) { foreach ($f in $sdFiles) { $sdBytes += $f.Length } }
|
||||
$sdMB = [math]::Round($sdBytes / 1MB, 1)
|
||||
Write-Host " SoftwareDistribution\Download: $(@($sdFiles).Count) file(s), ${sdMB} MB remaining (WaaSMedicSvc best-effort, WU is disabled)."
|
||||
|
||||
Assert-Step 'Cleanup' 'wuauserv StartType Disabled after cache clear' {
|
||||
(Get-Service wuauserv -ErrorAction Stop).StartType -eq 'Disabled'
|
||||
}
|
||||
}
|
||||
|
||||
# ── Pinned SHA256 hashes for downloaded installers (§1.3) ─────────────────────
|
||||
# Fill in the hash values below. Get them by:
|
||||
# (Invoke-WebRequest '<URL>' -UseBasicParsing -OutFile 'tmp.bin'; (Get-FileHash 'tmp.bin' -Algorithm SHA256).Hash)
|
||||
@@ -272,6 +368,20 @@ $script:Hashes = @{
|
||||
# vcpkg: cloned from GitHub via TLS; pin integrity with $VcpkgRef (commit SHA)
|
||||
}
|
||||
|
||||
# ── CleanupOnly early-exit ────────────────────────────────────────────────────
|
||||
# Called by Prepare-WinBuild2025.ps1 after the Windows Update loop completes.
|
||||
# Skips all provisioning steps; runs only disk cleanup then exits.
|
||||
if ($CleanupOnly) {
|
||||
if ($SkipCleanup) {
|
||||
Write-Host "[Setup] -CleanupOnly + -SkipCleanup: nothing to do." -ForegroundColor Yellow
|
||||
exit 0
|
||||
}
|
||||
Write-Host "[Setup] CleanupOnly mode — running disk cleanup on fully-updated system." -ForegroundColor Cyan
|
||||
Invoke-DiskCleanup
|
||||
Write-Host "[Setup] CleanupOnly complete." -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ── Pre-flight: remove temp files from any previous partial run ───────────────
|
||||
# These files are normally deleted at the end of each step that creates them.
|
||||
# A previous interrupted run may have left them behind — remove before starting.
|
||||
@@ -1527,114 +1637,9 @@ Write-Host "Tier-2 Toolchain installation complete." -ForegroundColor Green
|
||||
# ── Cleanup ───────────────────────────────────────────────────────────────────
|
||||
if ($SkipCleanup) {
|
||||
Write-Host "`n[Setup] Skipping disk cleanup (-SkipCleanup switch set)." -ForegroundColor Yellow
|
||||
} else {
|
||||
Invoke-DiskCleanup
|
||||
}
|
||||
else {
|
||||
Write-Step "Cleaning up disk"
|
||||
|
||||
# 1. Windows Disk Cleanup (cleanmgr) — run with /sagerun:1 using preset flags
|
||||
# First register the flags via /sageset:1 in the registry (silent, no UI)
|
||||
$cleanKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
|
||||
$sageset = 1
|
||||
$cleanCategories = @(
|
||||
'Active Setup Temp Folders',
|
||||
'BranchCache',
|
||||
'Downloaded Program Files',
|
||||
'Internet Cache Files',
|
||||
'Memory Dump Files',
|
||||
'Old ChkDsk Files',
|
||||
'Previous Installations',
|
||||
'Recycle Bin',
|
||||
'Service Pack Cleanup',
|
||||
'Setup Log Files',
|
||||
'System error memory dump files',
|
||||
'System error minidump files',
|
||||
'Temporary Files',
|
||||
'Temporary Setup Files',
|
||||
'Thumbnail Cache',
|
||||
'Update Cleanup',
|
||||
'Upgrade Discarded Files',
|
||||
'Windows Defender',
|
||||
'Windows Error Reporting Archive Files',
|
||||
'Windows Error Reporting Files',
|
||||
'Windows Error Reporting Queue Files',
|
||||
'Windows Error Reporting Temp Files',
|
||||
'Windows ESD installation files',
|
||||
'Windows Upgrade Log Files'
|
||||
)
|
||||
foreach ($cat in $cleanCategories) {
|
||||
$keyPath = "$cleanKey\$cat"
|
||||
if (Test-Path $keyPath) {
|
||||
Set-ItemProperty -Path $keyPath -Name "StateFlags$('{0:D4}' -f $sageset)" -Value 2 -Type DWord -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
Write-Host "Running cleanmgr /sagerun:$sageset (may take a few minutes)..."
|
||||
$proc = Start-Process -FilePath 'cleanmgr.exe' -ArgumentList "/sagerun:$sageset" -Wait -PassThru
|
||||
Write-Host "cleanmgr exited (code $($proc.ExitCode))."
|
||||
|
||||
# 2. Clear Windows Update cache
|
||||
# Stop all services that might hold file locks inside SoftwareDistribution:
|
||||
# wuauserv — Windows Update (already Disabled from Step 6b, stop is idempotent)
|
||||
# UsoSvc — Update Orchestrator (already Disabled from Step 6b)
|
||||
# bits — Background Intelligent Transfer Service (downloads WU payloads)
|
||||
# dosvc — Delivery Optimization (peer-to-peer WU cache)
|
||||
# TrustedInstaller — Windows Modules Installer (unpacks .cab/.msu update packages)
|
||||
Write-Host "Stopping WU-adjacent services before cache clear..."
|
||||
foreach ($svc in 'wuauserv', 'UsoSvc', 'bits', 'dosvc', 'TrustedInstaller') {
|
||||
Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# Delete and recreate the Download folder — more reliable than 'Remove-Item *'
|
||||
# because locked files inside subdirectories silently fail with wildcard delete.
|
||||
$sdDownload = 'C:\Windows\SoftwareDistribution\Download'
|
||||
Remove-Item $sdDownload -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Path $sdDownload -Force | Out-Null
|
||||
Write-Host "Windows Update download cache cleared."
|
||||
|
||||
# 3. Clear CBS / component store logs
|
||||
Remove-Item 'C:\Windows\Logs\CBS\*' -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 4. Clear TEMP folders
|
||||
Remove-Item 'C:\Windows\Temp\*' -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 5. Clear Prefetch
|
||||
Remove-Item 'C:\Windows\Prefetch\*' -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# 6. Run DISM component store cleanup (removes superseded update components)
|
||||
Write-Host "Running DISM component store cleanup (StartComponentCleanup)..."
|
||||
$dism = Start-Process -FilePath 'dism.exe' `
|
||||
-ArgumentList '/Online','/Cleanup-Image','/StartComponentCleanup','/ResetBase' `
|
||||
-Wait -PassThru
|
||||
Write-Host "DISM exited (code $($dism.ExitCode))."
|
||||
|
||||
Write-Host "Disk cleanup complete."
|
||||
|
||||
# DISM StartComponentCleanup / WaaSMedicSvc may reset service StartType during cleanup.
|
||||
# Re-enforce Disabled on both WU services after DISM completes.
|
||||
foreach ($svc in 'wuauserv', 'UsoSvc') {
|
||||
Set-Service -Name $svc -StartupType Disabled -ErrorAction SilentlyContinue
|
||||
}
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\wuauserv' `
|
||||
-Name Start -Value 4 -Type DWord -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# Validation — leftover artifacts from this run should be gone
|
||||
# Note: do NOT assert SoftwareDistribution\Download empty.
|
||||
# WaaSMedicSvc (Windows Update Medic Service) is tamper-protected — cannot be stopped —
|
||||
# and actively restores WU files immediately after deletion. Files reappearing here is
|
||||
# normal and harmless: WU is permanently disabled by Step 6b (service Disabled + GPO keys).
|
||||
# Report remaining size as informational only.
|
||||
$sdFiles = Get-ChildItem 'C:\Windows\SoftwareDistribution\Download' -Recurse -File -Force -ErrorAction SilentlyContinue
|
||||
# Avoid Measure-Object .Sum: under Set-StrictMode -Version Latest, PS 5.1 treats the
|
||||
# nullable Sum property as absent when the pipe is empty, throwing "property not found".
|
||||
$sdBytes = [long]0
|
||||
if ($sdFiles) { foreach ($f in $sdFiles) { $sdBytes += $f.Length } }
|
||||
$sdMB = [math]::Round($sdBytes / 1MB, 1)
|
||||
Write-Host " SoftwareDistribution\Download: $(@($sdFiles).Count) file(s), ${sdMB} MB remaining (WaaSMedicSvc best-effort, WU is disabled)."
|
||||
|
||||
Assert-Step 'Cleanup' 'wuauserv StartType Disabled after cache clear' {
|
||||
(Get-Service wuauserv -ErrorAction Stop).StartType -eq 'Disabled'
|
||||
}
|
||||
} # end if (-not $SkipCleanup)
|
||||
|
||||
# ── Pre-Final re-affirmation ──────────────────────────────────────────────────
|
||||
# Steps 7-10 (installs, cleanup, DISM) can take hours. During that window background
|
||||
|
||||
Reference in New Issue
Block a user