YaugerAIO.ps1
|
<#PSScriptInfo
.VERSION 1.5.2 .GUID ed3b18c9-b76d-4ba2-b3b4-59b1bf8e2113 .AUTHOR Rick Yauger .COMPANYNAME SoHo Integration, LLC .COPYRIGHT Copyright (c) Rick Yauger 2024 .TAGS Maintenance Cleanup HealthCheck .LICENSEURI https://opensource.org/licenses/MIT .PROJECTURI https://github.com/Graytools/YaugerAIO .RELEASENOTES Guided Windows maintenance with optional DISM repairs, automatic elevation, and readable reports. #> # Ask Windows for admin rights before doing anything with the console. # This handles launches from a regular CMD or PowerShell window. if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { $psCmd = if ($PSVersionTable.PSEdition -eq 'Core') { 'pwsh.exe' } else { 'powershell.exe' } $scriptPath = $PSCommandPath if ([string]::IsNullOrWhiteSpace($scriptPath)) { Write-Host 'YaugerAIO must be run from a saved script file to request administrator approval.' -ForegroundColor Red return } # Leave the new console open so the user can read the results. $quotedScriptPath = '"' + $scriptPath + '"' try { Start-Process -FilePath $psCmd -Verb RunAs -ArgumentList @('-NoProfile', '-NoExit', '-ExecutionPolicy', 'Bypass', '-File', $quotedScriptPath) -ErrorAction Stop } catch { Write-Host "YaugerAIO did not receive administrator approval. No maintenance was started: $_" -ForegroundColor Yellow } return } # Windows Terminal and some console hosts won't honor resize requests. try { $desiredWidth = 130 $desiredHeight = 50 $host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size($desiredWidth, $desiredHeight) $host.UI.RawUI.WindowSize = New-Object Management.Automation.Host.Size($desiredWidth, $desiredHeight) } catch { } # Keep the current runspace available to the console helpers. $global:myRunspace = [runspace]::DefaultRunspace [runspace]::DefaultRunspace = $global:myRunspace # Next-step labels used by the original servicing flow. $script:NextStepFor = @{ 'DISM' = 'SFC Scan' 'SFC' = 'Windows Updates' } $DebugDism = $false # Set to $true for extra DISM debug output # Scan notes shared across the workflow. $global:DISMSummary = "" $global:SFCSummary = "" # Bring the new console to the front when possible. function Focus-Console { Add-Type @" using System; using System.Runtime.InteropServices; public class Win32 { [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); } "@ $hwnd = (Get-Process -Id $pid).MainWindowHandle [Win32]::SetForegroundWindow($hwnd) | Out-Null } # Running applications can hold temp files open. function Get-TempLockingProcesses { $commonAppsMapping = @{ "chrome" = "Google Chrome" "firefox" = "Mozilla Firefox" "msedge" = "Microsoft Edge" "opera" = "Opera" "brave" = "Brave" "iexplore" = "Internet Explorer" "outlook" = "Microsoft Outlook" "winword" = "Microsoft Word" "excel" = "Microsoft Excel" "powerpnt" = "Microsoft PowerPoint" "onenote" = "Microsoft OneNote" "skype" = "Skype" "teams" = "Microsoft Teams" "onedrive" = "OneDrive" "discord" = "Discord" "slack" = "Slack" "steam" = "Steam" "epicgameslauncher" = "Epic Games Launcher" "origin" = "Origin" "uplay" = "Uplay" "battlenet" = "Battle.net" "leagueclient" = "League of Legends" "riotclient" = "Riot Client" "valorant" = "Valorant" "spotify" = "Spotify" "itunes" = "iTunes" "vlc" = "VLC Media Player" "winamp" = "Winamp" "pandora" = "Pandora" "skypeforbusiness" = "Skype for Business" "zoom" = "Zoom" "teamspeak" = "TeamSpeak" "minecraft" = "Minecraft" "roblox" = "Roblox" "fortnite" = "Fortnite" "gog" = "GOG Galaxy" } $runningProcesses = Get-Process | Where-Object { $commonAppsMapping.ContainsKey($_.ProcessName.ToLower()) } $result = @() foreach ($proc in $runningProcesses) { $key = $proc.ProcessName.ToLower() $friendly = $commonAppsMapping[$key] $result += [PSCustomObject]@{ ProcessName = $key; FriendlyName = $friendly } } return $result | Sort-Object FriendlyName -Unique } # Prefer the Windows system drive when checking free space. $selectedDrive = $env:SystemDrive if (-not $selectedDrive -or -not ($selectedDrive -match "^[A-Z]:$")) { Write-Host "SystemDrive environment variable is not set or invalid. Determining primary drive..." -ForegroundColor Yellow $selectedDrive = (Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Free -ne $null } | Sort-Object -Property Name | Select-Object -First 1).Name + ":" Write-Host "System drive: ${selectedDrive}" -ForegroundColor White } else { Write-Host "System drive: ${selectedDrive}" -ForegroundColor White } # Compare free space before and after. Other disk activity can affect this estimate. $initialFreeSpace = (Get-PSDrive -Name $selectedDrive.Substring(0,1)).Free Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force $script:StartTime = Get-Date $timestamp = $script:StartTime.ToString("yyyyMMdd_HHmmss_fff") $script:log = @() $script:maintenanceResults = @() $script:foundAndFixedCorruption = $false $script:runDiskCleanup = $false $script:needsRestart = $false function Log { param([string]$message, [string]$color = "Cyan") $script:log += $message Write-Host $message -ForegroundColor $color } # Store short outcomes separately from the detailed console messages. function Add-MaintenanceResult { param( [Parameter(Mandatory = $true)][string]$Name, [ValidateSet('PASS', 'WARN', 'FAIL', 'SKIP', 'CHECK')][string]$Status, [Parameter(Mandatory = $true)][string]$Summary, [string]$Action = '' ) $script:maintenanceResults += [PSCustomObject]@{ Name = $Name Status = $Status Summary = $Summary Action = $Action } } # Build the report from results collected during the run. function Format-YaugerMaintenanceReport { param( [object[]]$Results = @(), [datetime]$StartedAt, [datetime]$FinishedAt, [string]$ComputerName = 'Unknown', [string]$Drive = 'Unknown', [string]$SpaceFreed = 'Not measured', [bool]$RestartRequired = $false, [string]$TechnicalFile = '' ) $lines = New-Object 'System.Collections.Generic.List[string]' [void]$lines.Add('YAUGERAIO | WINDOWS MAINTENANCE REPORT') [void]$lines.Add('=====================================') [void]$lines.Add(('Computer: ' + $ComputerName)) [void]$lines.Add(('Started: ' + $StartedAt.ToString('yyyy-MM-dd HH:mm:ss'))) [void]$lines.Add(('Finished: ' + $FinishedAt.ToString('yyyy-MM-dd HH:mm:ss'))) [void]$lines.Add(('Duration: ' + ([math]::Round(($FinishedAt - $StartedAt).TotalMinutes, 1)) + ' minutes')) [void]$lines.Add(('System drive: ' + $Drive)) [void]$lines.Add(('Change in available disk space: ' + $SpaceFreed)) [void]$lines.Add(('Restart required by Windows Update: ' + $(if ($RestartRequired) { 'YES' } else { 'Not reported' }))) [void]$lines.Add('') $passed = @($Results | Where-Object { $_.Status -eq 'PASS' }).Count $warnings = @($Results | Where-Object { $_.Status -eq 'WARN' }).Count $failures = @($Results | Where-Object { $_.Status -eq 'FAIL' }).Count $checks = @($Results | Where-Object { $_.Status -eq 'CHECK' }).Count $skipped = @($Results | Where-Object { $_.Status -eq 'SKIP' }).Count [void]$lines.Add('AT A GLANCE') [void]$lines.Add('-----------') if ($failures -gt 0) { [void]$lines.Add('Overall: Some maintenance steps failed. Read the next section.') } elseif ($warnings -gt 0) { [void]$lines.Add('Overall: Some things still need attention.') } elseif ($checks -gt 0) { [void]$lines.Add('Overall: Some results could not be confirmed.') } elseif ($passed -gt 0) { [void]$lines.Add('Overall: No problems reported by the completed checks.') } else { [void]$lines.Add('Overall: No completed checks were recorded.') } [void]$lines.Add(("Passed: {0} | Warnings: {1} | Failed: {2} | Unverified: {3} | Skipped: {4}" -f $passed,$warnings,$failures,$checks,$skipped)) [void]$lines.Add('') [void]$lines.Add('WHAT TO DO NEXT') [void]$lines.Add('---------------') $actionItems = @($Results | Where-Object { $_.Status -in @('FAIL', 'WARN', 'CHECK') -and -not [string]::IsNullOrWhiteSpace($_.Action) }) $actionNumber = 0 foreach ($item in $actionItems) { $actionNumber++ [void]$lines.Add(('{0}. [{1}] {2}' -f $actionNumber, $item.Status, $item.Name)) [void]$lines.Add((' ' + (($item.Action -replace '[\r\n]+', ' ').Trim()))) } if ($RestartRequired) { $actionNumber++ [void]$lines.Add(('{0}. [WARN] Restart Windows when you have saved your work to finish installing updates.' -f $actionNumber)) } if ($actionNumber -eq 0) { [void]$lines.Add('No specific follow-up action recorded. Review any skipped or unverified checks below.') } [void]$lines.Add('') [void]$lines.Add('CHECK RESULTS') [void]$lines.Add('-------------') foreach ($item in $Results) { $shortSummary = ([string]$item.Summary -replace '[\r\n]+', ' ').Trim() [void]$lines.Add(('[{0}] {1} - {2}' -f $item.Status, $item.Name, $shortSummary)) } if ($Results.Count -eq 0) { [void]$lines.Add('No maintenance results were recorded.') } [void]$lines.Add('') [void]$lines.Add('TECHNICAL DETAILS') [void]$lines.Add('-----------------') if (-not [string]::IsNullOrWhiteSpace($TechnicalFile)) { [void]$lines.Add(('Recorded diagnostic details: ' + $TechnicalFile)) } else { [void]$lines.Add('No separate technical log was created.') } [void]$lines.Add('PASS = check passed | WARN = needs attention | FAIL = failed') [void]$lines.Add('CHECK = result unconfirmed | SKIP = was not run') [void]$lines.Add('A successful scan does not necessarily mean the entire computer is healthy.') return ($lines -join [Environment]::NewLine) } # Keep the quick summary and full log separate. Desktop is preferred. function Save-YaugerMaintenanceReport { param( [Parameter(Mandatory = $true)][string]$ReportText, [Parameter(Mandatory = $true)][string]$TechnicalText, [Parameter(Mandatory = $true)][string]$Timestamp, [string[]]$Destinations = @( [Environment]::GetFolderPath('Desktop'), [Environment]::GetFolderPath('MyDocuments'), [System.IO.Path]::GetTempPath() ) ) foreach ($folder in $Destinations) { if ([string]::IsNullOrWhiteSpace($folder) -or -not (Test-Path -LiteralPath $folder -PathType Container)) { continue } $reportPath = Join-Path $folder ('YaugerAIO_Report_' + $Timestamp + '.txt') $technicalPath = Join-Path $folder ('YaugerAIO_Technical_' + $Timestamp + '.txt') try { Set-Content -LiteralPath $reportPath -Value $ReportText -Encoding UTF8 -ErrorAction Stop Set-Content -LiteralPath $technicalPath -Value $TechnicalText -Encoding UTF8 -ErrorAction Stop return [PSCustomObject]@{ ReportPath = $reportPath; TechnicalPath = $technicalPath } } catch { # A redirected Desktop or missing permissions can block a write; try the next folder. continue } } throw 'Unable to save the maintenance report to Desktop, Documents, or the temporary directory.' } function Get-CBSSummary { $cbsPath = "C:\Windows\Logs\CBS\CBS.log" if (Test-Path $cbsPath) { $lines = Select-String -Path $cbsPath -Pattern "\[SR\].*" if ($lines) { foreach ($match in $lines) { Log "CBS: $($match.Line)" "Red" } } else { Log "No [SR] lines found in CBS.log" "Red" } } else { Log "CBS.log not found or inaccessible." "Red" } } function Show-Banner { $asciiArt = @" ██ ██ █████ ██ ██ ██████ ███████ ██████ █████ ██ ██████ ██ ██ ██ ██████ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ ████ ███████ ██ ██ ██ ███ █████ ██████ ███████ ██ ██ ██ ██ ██ ██ █████ █████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ ██████ ███████ ██ ██ ██ ██ ██ ██████ ████ ██ ██ ███████ ██ ███████ "@ Write-Host $asciiArt } # System checks function Check-DiskSpace { try { $disk = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='${selectedDrive}'" $freeGB = [math]::Round($disk.FreeSpace / 1GB, 2) $totalGB = [math]::Round($disk.Size / 1GB, 2) $freePercentage = [math]::Round(($disk.FreeSpace / $disk.Size) * 100, 2) Log "${selectedDrive} Drive Free Space: $freeGB GB out of $totalGB GB ($freePercentage% free)" "Cyan" if ($freePercentage -lt 10) { Add-MaintenanceResult -Name 'System disk space' -Status WARN -Summary "$freeGB GB free ($freePercentage%) on $selectedDrive." -Action 'Free up space or expand the system drive; target at least 10% free space.' } else { Add-MaintenanceResult -Name 'System disk space' -Status PASS -Summary "$freeGB GB free out of $totalGB GB on $selectedDrive ($freePercentage%)." } } catch { Add-MaintenanceResult -Name 'System disk space' -Status FAIL -Summary 'Could not retrieve drive capacity.' -Action 'Check Disk Management and review the technical log.' Log "Failed to retrieve disk space info for ${selectedDrive}: $_" "Red" } } function Check-CPUUsage { try { $cpu = Get-Counter '\Processor(_Total)\% Processor Time' $usage = [math]::Round($cpu.CounterSamples.CookedValue, 2) Log "CPU Usage: $usage%" "Cyan" if ($usage -ge 90) { Add-MaintenanceResult -Name 'CPU usage' -Status WARN -Summary "High CPU usage at $usage% during this sample." -Action 'Check Task Manager for CPU-intensive processes if performance is slow.' } else { Add-MaintenanceResult -Name 'CPU usage' -Status PASS -Summary "CPU usage was $usage% at the time of sampling." } } catch { Add-MaintenanceResult -Name 'CPU usage' -Status FAIL -Summary 'Unable to measure CPU usage.' -Action 'Check Windows Performance Monitor and the technical log.' Log "Failed to retrieve CPU usage: $_" "Red" } } function Flush-DNSCache { try { Write-Host "Flushing DNS cache..." -ForegroundColor Cyan $before = (Get-DnsClientCache).Count ipconfig /flushdns | Out-Null if ($LASTEXITCODE -ne 0) { throw "ipconfig /flushdns exited with code $LASTEXITCODE" } $after = (Get-DnsClientCache).Count $flushed = $before - $after Write-Host "✔ DNS cache flushed." -ForegroundColor Cyan Write-Host "Entries removed: $flushed" -ForegroundColor Cyan Add-MaintenanceResult -Name 'DNS cache' -Status PASS -Summary 'ipconfig /flushdns completed successfully.' Write-Host "" } catch { Add-MaintenanceResult -Name 'DNS cache' -Status FAIL -Summary 'Unable to confirm that the DNS cache was flushed.' -Action 'Retry ipconfig /flushdns in an elevated command prompt if name resolution is failing.' Log "Failed to flush DNS cache: $_" "Red" } } function Check-RAMUsage { try { $os = Get-CimInstance Win32_OperatingSystem $total = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2) $free = [math]::Round($os.FreePhysicalMemory / 1MB, 2) $used = [math]::Round($total - $free, 2) $percentUsed = [math]::Round(($used / $total) * 100, 2) Log "RAM Usage: $used GB of $total GB ($percentUsed%)" "Cyan" if ($percentUsed -gt 80) { Add-MaintenanceResult -Name 'Memory usage' -Status WARN -Summary "$used of $total GB in use ($percentUsed%) at sampling time." -Action 'Review memory-heavy apps in Task Manager if the PC is slow.' } else { Add-MaintenanceResult -Name 'Memory usage' -Status PASS -Summary "$used of $total GB in use ($percentUsed%) at sampling time." } Start-Sleep -Seconds 2 if ($percentUsed -gt 80) { Log "Random Access Memory is currently utilized above 80%. Please check and see if you have 14 million tabs open." "Yellow" Start-Sleep -Seconds 5 } } catch { Add-MaintenanceResult -Name 'Memory usage' -Status FAIL -Summary 'Could not measure physical memory usage.' -Action 'Review Task Manager memory usage and the technical log.' Log "Failed to retrieve RAM usage: $_" "Red" } } function Check-GPUDrivers { try { $gpus = Get-CimInstance Win32_VideoController foreach ($gpu in $gpus) { if ($gpu.DriverDate) { $driverDate = [datetime]$gpu.DriverDate $formattedDate = $driverDate.ToString("MMddyyyy") } else { $formattedDate = "Unknown" } Log "GPU Detected: $($gpu.Name) - $formattedDate" "Cyan" if ($gpu.DriverDate) { $releaseDate = [datetime]$gpu.DriverDate $monthsDifference = (New-TimeSpan -Start $releaseDate -End (Get-Date)).TotalDays / 30 if ($monthsDifference -ge 6) { Add-MaintenanceResult -Name "GPU driver: $($gpu.Name)" -Status WARN -Summary "Driver date is approximately $([math]::Round($monthsDifference)) months old; this does not prove an update is available." -Action 'Check the GPU vendor support page for a newer recommended driver if you have graphics issues.' Log "GPU Driver release more than 6 months ago. Please check your GPU Vendor for driver updates." "Red" } elseif ($monthsDifference -ge 3) { Add-MaintenanceResult -Name "GPU driver: $($gpu.Name)" -Status CHECK -Summary "Driver date is approximately $([math]::Round($monthsDifference)) months old." -Action 'If you are troubleshooting graphics problems, compare the installed driver with the vendor release.' Log "GPU Driver release more than 3 months ago. Please check your GPU Vendor for driver updates." "Yellow" } else { Add-MaintenanceResult -Name "GPU driver: $($gpu.Name)" -Status PASS -Summary "Installed driver date is within the last three months." Log "GPU Driver release is within the last 3 months." "Cyan" } } else { Add-MaintenanceResult -Name "GPU driver: $($gpu.Name)" -Status CHECK -Summary 'Driver date was not available.' -Action 'Review graphics driver details in Device Manager if necessary.' Log "GPU Driver release date not available." "Yellow" } } Write-Host "" } catch { Add-MaintenanceResult -Name 'GPU driver checks' -Status FAIL -Summary 'Could not query installed graphics drivers.' -Action 'Inspect graphics adapters in Device Manager.' Log "Failed to retrieve GPU driver info: $_" "Red" } } function Check-SystemUptime { try { $uptime = (Get-Date) - (gcim Win32_OperatingSystem).LastBootUpTime $days = [math]::Round($uptime.TotalDays, 2) Log "System Uptime: $days days" "Cyan" if ($days -ge 7) { Add-MaintenanceResult -Name 'System uptime' -Status CHECK -Summary "Windows has been running for $days days." -Action 'Restart when convenient if updates require it or the system is misbehaving.' } else { Add-MaintenanceResult -Name 'System uptime' -Status PASS -Summary "Windows has been running for $days days." } Start-Sleep -Seconds 4 if ($days -lt 3) { Log "Great job keeping up on your reboots. Keep it up!" "Yellow" } elseif ($days -ge 7) { $message = @" 🚨 Your system has been running for over a week without a restart! This can lead to performance issues, memory leaks, and failed updates. It is strongly recommended that you restart your computer ASAP. Make it a habit to restart at least every 2-3 days for optimal performance. "@ Log $message "Red" } elseif ($days -ge 3) { $message = @" ⚠️ Your system has been running for over 3 days. Regularly restarting your computer helps: - Apply critical updates - Clear temporary files and memory leaks - Improve performance and stability For best results, restart at least once every few days. "@ Log $message "Yellow" } Start-Sleep -Seconds 10 } catch { Add-MaintenanceResult -Name 'System uptime' -Status FAIL -Summary 'Could not determine the last Windows boot time.' -Action 'Check system information or Event Viewer.' Log "Failed to retrieve system uptime: $_" "Red" } } function Install-WindowsUpdates { Write-Host "Checking for Windows updates..." -ForegroundColor Cyan try { $updateSession = New-Object -ComObject Microsoft.Update.Session $searcher = $updateSession.CreateUpdateSearcher() $searchResult = $searcher.Search("IsInstalled=0 and Type='Software'") if ($searchResult.Updates.Count -eq 0) { Write-Host "No applicable pending software updates found." -ForegroundColor Green Add-MaintenanceResult -Name 'Windows Update' -Status PASS -Summary 'No pending software updates returned by Windows Update search.' return } $updates = New-Object -ComObject Microsoft.Update.UpdateColl foreach ($update in $searchResult.Updates) { Write-Host "Update: $($update.Title)" -ForegroundColor Gray $script:log += "Windows Update found: $($update.Title)" if (-not $update.EulaAccepted) { $update.AcceptEula() | Out-Null } [void]$updates.Add($update) } Write-Host "Downloading updates..." -ForegroundColor Cyan $downloader = $updateSession.CreateUpdateDownloader() $downloader.Updates = $updates $downloadResult = $downloader.Download() $script:log += 'Windows Update download result code: ' + $downloadResult.ResultCode if ($downloadResult.ResultCode -ne 2) { Add-MaintenanceResult -Name 'Windows Update' -Status WARN -Summary 'Not all updates downloaded successfully; installation was not attempted.' -Action 'Open Settings > Windows Update, review errors, and retry when convenient.' Write-Host "Windows Update downloads were not fully successful. Installation skipped." -ForegroundColor Yellow return } Write-Host "Installing updates..." -ForegroundColor Cyan $installer = $updateSession.CreateUpdateInstaller() $installer.Updates = $updates $installResult = $installer.Install() $script:log += 'Windows Update installation result code: ' + $installResult.ResultCode if ($installResult.RebootRequired) { $script:needsRestart = $true Write-Host "A restart is required to complete the update installation." -ForegroundColor Yellow } if ($installResult.ResultCode -eq 2) { Add-MaintenanceResult -Name 'Windows Update' -Status PASS -Summary "$($updates.Count) software update(s) were reported installed successfully." Write-Host "Windows Update reports successful installation." -ForegroundColor Green } elseif ($installResult.ResultCode -eq 3) { Add-MaintenanceResult -Name 'Windows Update' -Status WARN -Summary 'Windows Update completed with errors; some updates may be missing.' -Action 'Open Settings > Windows Update and review update history or retry failed updates.' Write-Host "Windows Update installed some updates but reported errors." -ForegroundColor Yellow } else { Add-MaintenanceResult -Name 'Windows Update' -Status FAIL -Summary "Windows Update installation returned result code $($installResult.ResultCode)." -Action 'Review Windows Update history and the technical log; retry updates through Settings.' Write-Host "Windows Update installation did not complete successfully." -ForegroundColor Red } } catch { Add-MaintenanceResult -Name 'Windows Update' -Status FAIL -Summary 'Windows Update failed or was unavailable.' -Action 'Check Windows Update in Settings, and review the technical log for details.' Log "Error during Windows update process: $_" "Red" } } # CheckHealth looks for component store damage before we offer a repair. function Get-DISMCheckHealthStatus { param( [string]$Output, [int]$ExitCode ) if ($ExitCode -ne 0) { return 'Failed' } if ($Output -match 'No component store corruption detected') { return 'Healthy' } if ($Output -match 'component store is not repairable') { return 'NotRepairable' } if ($Output -match 'component store is repairable') { return 'Repairable' } return 'Unknown' } # The initial check is quick. Ask before starting the potentially lengthy repair. function Run-DISMCheckHealth { Write-Host "Checking Windows repair files (DISM /CheckHealth)..." -ForegroundColor Cyan try { $dismOutput = & dism.exe /Online /Cleanup-Image /CheckHealth 2>&1 | Out-String $exitCode = $LASTEXITCODE $status = Get-DISMCheckHealthStatus -Output $dismOutput -ExitCode $exitCode $script:log += "DISM /CheckHealth status: $status (exit code $exitCode)" + [Environment]::NewLine + $dismOutput switch ($status) { 'Healthy' { Add-MaintenanceResult -Name 'Windows repair check' -Status PASS -Summary 'Windows did not report damage to its repair files.' Write-Host "Windows repair files look healthy. Moving on to SFC." -ForegroundColor Green } 'Repairable' { Write-Host "Windows found damage it may be able to repair." -ForegroundColor Yellow Write-Host "Try DISM repair now? This can take 20-30 minutes or longer and may need internet access. (Y/N)" -ForegroundColor Yellow $choice = [Console]::ReadKey($true) if ($choice.KeyChar.ToString().ToUpperInvariant() -eq 'Y') { Invoke-DISMRestoreHealth } else { Add-MaintenanceResult -Name 'Windows repair check' -Status WARN -Summary 'Repairable Windows damage was found. You chose not to run the repair.' -Action 'Run YaugerAIO again and accept Windows repair, or ask a technician for help.' Write-Host "Windows repair skipped. Continuing with SFC." -ForegroundColor Yellow } } 'NotRepairable' { Add-MaintenanceResult -Name 'Windows repair check' -Status FAIL -Summary 'Windows reported damage that this repair command cannot fix.' -Action 'Have a technician review the DISM details and Windows recovery options.' Write-Host "Windows reports damage that DISM cannot repair automatically." -ForegroundColor Red } 'Failed' { Add-MaintenanceResult -Name 'Windows repair check' -Status FAIL -Summary "The Windows repair check failed (DISM exit code $exitCode)." -Action 'Check the technical log, then ask a technician to investigate Windows servicing.' Write-Host "The Windows repair check failed. Review the technical log." -ForegroundColor Red } default { Add-MaintenanceResult -Name 'Windows repair check' -Status CHECK -Summary 'Windows repair check finished, but its result could not be confirmed.' -Action 'Review the DISM details in the technical log before deciding whether to repair.' Write-Host "DISM returned an unrecognized result. Repair was not started." -ForegroundColor Yellow } } } catch { Add-MaintenanceResult -Name 'Windows repair check' -Status FAIL -Summary 'The Windows repair check or repair prompt could not finish.' -Action 'Review the technical log and verify that DISM is available.' Log "DISM /CheckHealth or repair prompt failed: $_" "Red" } Write-Host "" } # A zero exit code is not enough to claim the repair worked; recheck Windows afterward. function Get-DISMRepairVerificationStatus { param( [int]$RepairExitCode, [string]$CheckStatus ) if ($RepairExitCode -ne 0) { return 'Failed' } if ($CheckStatus -eq 'Healthy') { return 'Repaired' } if ($CheckStatus -in @('Repairable', 'NotRepairable')) { return 'Unresolved' } return 'Unverified' } function Invoke-DISMRestoreHealth { Write-Host "Repairing Windows files with DISM /RestoreHealth. Please leave this window open..." -ForegroundColor Cyan try { $repairLines = @() & dism.exe /Online /Cleanup-Image /RestoreHealth 2>&1 | Tee-Object -Variable repairLines | Out-Host $repairExitCode = $LASTEXITCODE $repairOutput = $repairLines | Out-String $script:log += "DISM /RestoreHealth exit code: $repairExitCode" + [Environment]::NewLine + $repairOutput if ($repairExitCode -ne 0) { Add-MaintenanceResult -Name 'Windows repair' -Status FAIL -Summary 'DISM could not finish the repair.' -Action 'Review the repair details in the technical log. Windows Update or a technician may be needed.' Write-Host "Windows repair failed. Continuing to SFC." -ForegroundColor Red return } Write-Host "Checking whether Windows now reports its repair files as healthy..." -ForegroundColor Cyan $verifyOutput = & dism.exe /Online /Cleanup-Image /CheckHealth 2>&1 | Out-String $verifyExitCode = $LASTEXITCODE $checkStatus = Get-DISMCheckHealthStatus -Output $verifyOutput -ExitCode $verifyExitCode $script:log += "DISM post-repair /CheckHealth status: $checkStatus (exit code $verifyExitCode)" + [Environment]::NewLine + $verifyOutput $result = Get-DISMRepairVerificationStatus -RepairExitCode $repairExitCode -CheckStatus $checkStatus switch ($result) { 'Repaired' { Add-MaintenanceResult -Name 'Windows repair' -Status PASS -Summary 'Windows found damage, DISM ran a repair, and a follow-up check reported no remaining damage.' Write-Host "Windows reports that the repair worked. SFC will check system files next." -ForegroundColor Green } 'Unresolved' { Add-MaintenanceResult -Name 'Windows repair' -Status WARN -Summary 'DISM finished, but Windows still reports damage.' -Action 'Review the technical log and ask a technician to check Windows servicing or recovery options.' Write-Host "DISM finished, but Windows still reports damage." -ForegroundColor Yellow } default { Add-MaintenanceResult -Name 'Windows repair' -Status CHECK -Summary 'DISM finished, but the follow-up check could not confirm the repair.' -Action 'Review the DISM results in the technical log before assuming Windows is repaired.' Write-Host "The repair finished, but YaugerAIO could not confirm the result." -ForegroundColor Yellow } } } catch { Add-MaintenanceResult -Name 'Windows repair' -Status FAIL -Summary 'The repair or its follow-up check could not finish.' -Action 'Review the technical log and Windows servicing status.' Log "DISM /RestoreHealth could not finish: $_" "Red" } } # SFC messages vary by Windows version and language. Unknown output isn't a pass. function Get-SFCScanStatus { param( [string]$Output, [int]$ExitCode ) if ($ExitCode -ne 0) { return 'Failed' } if ($Output -match 'found corrupt files but was unable to fix some of them') { return 'Unresolved' } if ($Output -match 'found corrupt files and successfully repaired them') { return 'Repaired' } if ($Output -match 'did not find any integrity violations') { return 'Healthy' } if ($Output -match 'could not perform the requested operation|could not start the repair service|system repair pending') { return 'Failed' } return 'Unknown' } function Run-SFCScan { Write-Host "Executing SFC Scan..." -ForegroundColor Cyan try { # Show SFC's output as it runs, and keep a copy for the technical log. $sfcLines = @() & sfc.exe /scannow 2>&1 | Tee-Object -Variable sfcLines $exitCode = $LASTEXITCODE $sfcOutput = $sfcLines | Out-String $status = Get-SFCScanStatus -Output $sfcOutput -ExitCode $exitCode $script:log += "SFC /scannow status: $status (exit code $exitCode)`n$sfcOutput`n" switch ($status) { 'Healthy' { Add-MaintenanceResult -Name 'SFC system files' -Status PASS -Summary 'Windows Resource Protection found no integrity violations.' Write-Host "SFC found no integrity violations." -ForegroundColor Green } 'Repaired' { Add-MaintenanceResult -Name 'SFC system files' -Status PASS -Summary 'SFC reports that corrupt system files were repaired.' Write-Host "SFC reports that corrupt system files were repaired." -ForegroundColor Green } 'Unresolved' { Add-MaintenanceResult -Name 'SFC system files' -Status WARN -Summary 'SFC could not repair some corrupt system files.' -Action 'Review CBS.log and try DISM /RestoreHealth followed by sfc /scannow.' Write-Host "SFC could not repair all corrupt files. Review CBS.log for details." -ForegroundColor Yellow } 'Failed' { Add-MaintenanceResult -Name 'SFC system files' -Status FAIL -Summary "SFC failed or could not finish (exit code $exitCode)." -Action 'Check CBS.log and rerun sfc /scannow from an elevated prompt.' Write-Host "SFC did not complete successfully (exit code $exitCode). Review the output in the log." -ForegroundColor Red } default { Add-MaintenanceResult -Name 'SFC system files' -Status CHECK -Summary 'SFC completed with unrecognized output.' -Action 'Read the SFC section of the technical log before assuming files are healthy.' Write-Host "SFC completed, but its result was not recognized. Review the output in the log." -ForegroundColor Yellow } } } catch { Add-MaintenanceResult -Name 'SFC system files' -Status FAIL -Summary 'SFC could not start or process its output.' -Action 'Review the technical log and try SFC in an elevated prompt.' Log "SFC scan could not run: $_" "Red" } } # Cleanup tasks function Clear-BrowserCaches { try { $chromePath = "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Cache" if (Test-Path $chromePath) { Remove-Item "$chromePath\*" -Recurse -Force -ErrorAction SilentlyContinue Log "Attempted Chrome default-profile cache cleanup; locked files may remain." "Cyan" } else { Log "Chrome default-profile cache folder not found; other profiles were not checked." "Cyan" } $edgePath = "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Cache" if (Test-Path $edgePath) { Remove-Item "$edgePath\*" -Recurse -Force -ErrorAction SilentlyContinue Log "Attempted Edge default-profile cache cleanup; locked files may remain." "Cyan" } else { Log "Edge default-profile cache folder not found; other profiles were not checked." "Cyan" } $bravePath = "$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\User Data\Default\Cache" if (Test-Path $bravePath) { Remove-Item "$bravePath\*" -Recurse -Force -ErrorAction SilentlyContinue Log "Attempted Brave default-profile cache cleanup; locked files may remain." "Cyan" } else { Log "Brave default-profile cache folder not found; other profiles were not checked." "Cyan" } if ((Test-Path "$env:LOCALAPPDATA\Opera Software") -or (Test-Path "$env:APPDATA\Opera Software")) { $operaStableLocal = "$env:LOCALAPPDATA\Opera Software\Opera Stable\Cache" $operaStableRoaming = "$env:APPDATA\Opera Software\Opera Stable\Cache" if (Test-Path $operaStableLocal) { Remove-Item "$operaStableLocal\*" -Recurse -Force -ErrorAction SilentlyContinue } if (Test-Path $operaStableRoaming) { Remove-Item "$operaStableRoaming\*" -Recurse -Force -ErrorAction SilentlyContinue } $operaGXLocal = "$env:LOCALAPPDATA\Opera Software\Opera GX\Cache" $operaGXRoaming = "$env:APPDATA\Opera Software\Opera GX\Cache" if (Test-Path $operaGXLocal) { Remove-Item "$operaGXLocal\*" -Recurse -Force -ErrorAction SilentlyContinue } if (Test-Path $operaGXRoaming) { Remove-Item "$operaGXRoaming\*" -Recurse -Force -ErrorAction SilentlyContinue } Log "Attempted Opera cache cleanup; some folders or locked files may have been skipped." "Cyan" } else { Log "Opera cache folders not found in the checked profile locations." "Cyan" } if (Test-Path "$env:APPDATA\Mozilla\Firefox") { $firefoxProfilesPath = "$env:APPDATA\Mozilla\Firefox\Profiles" if (Test-Path $firefoxProfilesPath) { $profiles = Get-ChildItem -Path $firefoxProfilesPath -Directory -ErrorAction SilentlyContinue foreach ($profile in $profiles) { $cache2Path = (Join-Path $profile.FullName "cache2") $cachePath = (Join-Path $profile.FullName "cache") if (Test-Path $cache2Path) { Remove-Item "$cache2Path\*" -Recurse -Force -ErrorAction SilentlyContinue } if (Test-Path $cachePath) { Remove-Item "$cachePath\*" -Recurse -Force -ErrorAction SilentlyContinue } } } Log "Attempted Firefox profile cache cleanup; some cache folders or locked files may have been skipped." "Cyan" } else { Log "Firefox profile cache folders not found in the checked location." "Cyan" } Add-MaintenanceResult -Name 'Browser caches' -Status CHECK -Summary 'Tried to clear selected browser caches, but some files and profiles may not have been checked.' -Action 'Close browsers before running YaugerAIO again if you still need to clear their caches.' } catch { Add-MaintenanceResult -Name 'Browser caches' -Status FAIL -Summary 'Browser cache cleanup encountered an error.' -Action 'Close running browsers, then retry; see the technical log.' Log "Failed to clear browser caches: $_" "Red" } } function Run-CleanMgr { try { Write-Host -NoNewline -ForegroundColor Cyan "Cleaning $selectedDrive Drive" $process = Start-Process -FilePath "cleanmgr.exe" -ArgumentList "/d ${selectedDrive} /sagerun:1" -WindowStyle Minimized -PassThru $dotCount = 0 while (-not $process.HasExited) { $dotCount++ if ($dotCount -gt 3) { $dotCount = 1 } Write-Host -NoNewline "`rCleaning $selectedDrive Drive" -ForegroundColor Cyan Write-Host -NoNewline ("." * $dotCount) -ForegroundColor Cyan Start-Sleep -Milliseconds 100 } Write-Host "" if ($process.ExitCode -eq 0) { Add-MaintenanceResult -Name 'Disk Cleanup' -Status CHECK -Summary 'Disk Cleanup exited successfully; actual cleanup depends on its saved configuration.' -Action 'If storage did not improve, review Disk Cleanup settings and select cleanup categories manually.' Log "✔ Disk Cleanup executed on ${selectedDrive}." "Cyan" } else { Add-MaintenanceResult -Name 'Disk Cleanup' -Status FAIL -Summary "Disk Cleanup exited with code $($process.ExitCode)." -Action 'Open Windows Disk Cleanup and inspect the selected cleanup profile.' Log "Disk Cleanup exited with code $($process.ExitCode)." "Red" } Write-Host "" } catch { Add-MaintenanceResult -Name 'Disk Cleanup' -Status FAIL -Summary 'Disk Cleanup failed to launch or complete.' -Action 'Run Disk Cleanup manually from Windows.' Log "Disk Cleanup failed on ${selectedDrive}: $_" "Red" } } function Get-WindowsTempSize { try { $tempPath = $env:TEMP if ([string]::IsNullOrWhiteSpace($tempPath) -or -not (Test-Path -LiteralPath $tempPath -PathType Container)) { Log "Unable to calculate Windows Temp size: TEMP directory is unavailable." "Yellow" return 0 } # Count hidden files too, but don't count directory entries as files. $files = Get-ChildItem -LiteralPath $tempPath -Recurse -File -Force -ErrorAction SilentlyContinue $sizeBytes = ($files | Measure-Object -Property Length -Sum).Sum if (-not $sizeBytes) { $sizeBytes = 0 } return [math]::Round($sizeBytes / 1MB, 2) } catch { Log "Failed to calculate Windows Temp size: $_" "Red" return 0 } } function Clear-WindowsTemp { $script:tempCleanupOutcome = 'CHECK' try { $tempPath = $env:TEMP if ([string]::IsNullOrWhiteSpace($tempPath) -or -not (Test-Path -LiteralPath $tempPath -PathType Container)) { $script:tempCleanupOutcome = 'SKIP' Log "Skipped Windows Temp cleanup: TEMP directory is unavailable." "Yellow" return } $resolvedPath = (Resolve-Path -LiteralPath $tempPath -ErrorAction Stop).ProviderPath $pathRoot = [System.IO.Path]::GetPathRoot($resolvedPath) if ([string]::IsNullOrWhiteSpace($pathRoot) -or $resolvedPath.TrimEnd('\', '/') -eq $pathRoot.TrimEnd('\', '/')) { $script:tempCleanupOutcome = 'FAIL' Log "Skipped Windows Temp cleanup: TEMP must not be a filesystem root." "Red" return } $tempDirectory = Get-Item -LiteralPath $resolvedPath -Force -ErrorAction Stop if (($tempDirectory.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { $script:tempCleanupOutcome = 'FAIL' Log "Skipped Windows Temp cleanup: TEMP must not be a symbolic link or junction." "Red" return } $cleanupErrors = @() $skippedLinks = 0 foreach ($item in Get-ChildItem -LiteralPath $resolvedPath -Force -ErrorAction Stop) { if (($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { $skippedLinks++ Log "Skipped link or junction in Windows Temp: $($item.Name)" "Yellow" continue } Remove-Item -LiteralPath $item.FullName -Recurse -Force -ErrorAction SilentlyContinue -ErrorVariable +cleanupErrors } if ($cleanupErrors.Count -gt 0 -or $skippedLinks -gt 0) { $script:tempCleanupOutcome = 'WARN' Log "Windows Temp cleanup could not remove $($cleanupErrors.Count) item(s); skipped $skippedLinks link(s)." "Yellow" } else { $script:tempCleanupOutcome = 'PASS' } } catch { $script:tempCleanupOutcome = 'FAIL' Log "Failed to clear Windows Temp files: $_" "Red" } } # Wrap longer console messages without splitting words. function Wrap-Text { param( [Parameter(Mandatory=$true)] [string]$Text, [int]$Width = 130 ) $words = $Text -split '\s+' $line = "" $result = "" foreach ($word in $words) { if (($line.Length + $word.Length + 1) -gt $Width) { $result += $line.TrimEnd() + "`n" $line = $word + " " } else { $line += $word + " " } } if ($line) { $result += $line.TrimEnd() } return $result } # Guided maintenance run Show-Banner Write-Host "During this session, we will:" -ForegroundColor White Write-Host "- Optionally run Disk Cleanup." -ForegroundColor White Write-Host "- Optionally clear Windows Temp Files." -ForegroundColor White Write-Host "- Check available disk space on ${selectedDrive}." -ForegroundColor White Write-Host "- Check current CPU usage." -ForegroundColor White Write-Host "- Flush the DNS cache." -ForegroundColor White Write-Host "- Check RAM usage." -ForegroundColor White Write-Host "- Check GPU driver(s) and version(s)." -ForegroundColor White Write-Host "- Log system uptime." -ForegroundColor White Write-Host "- Clear browser caches. (Chrome, Edge, Brave, Opera, Firefox)" -ForegroundColor White Write-Host "- DISM Scan for Component Store Corruption." -ForegroundColor White Write-Host "- SFC Scan to find and fix Windows System Files." -ForegroundColor White Write-Host "- Check for and install Windows Updates." -ForegroundColor White Write-Host "" -ForegroundColor White Focus-Console Start-Sleep -Seconds 1 Write-Host "Please close all applications and browsers for a clean, smooth Cache Smash.™" -ForegroundColor Yellow Start-Sleep -Seconds 2 Write-Host "Please press Enter to begin." -ForegroundColor Yellow [void][System.Console]::ReadKey($true) Write-Host "" # Disk Cleanup (optional) Write-Host "Would you like to run Disk Cleanup? (Y/N)" -ForegroundColor Yellow $key = [Console]::ReadKey($true) if ($key.KeyChar.ToString().ToUpper() -eq "Y") { $script:runDiskCleanup = $true Log "✔ User has opted to run Disk Cleanup." "Yellow" Run-CleanMgr } else { Add-MaintenanceResult -Name 'Disk Cleanup' -Status SKIP -Summary 'Skipped at user request.' Log "✖ User has opted not to run Disk Cleanup." "Yellow" } Write-Host "" # Windows Temp cleanup (optional) $tempSize = Get-WindowsTempSize if ($tempSize -gt 0) { if ($tempSize -gt 1000) { $displaySize = ("{0:N2} GB" -f ($tempSize / 1024)) } else { $displaySize = ("{0:N2} MB" -f $tempSize) } Write-Host "" Write-Host "You can reclaim $displaySize from Windows Temp files. Interested? Y/N" -ForegroundColor Yellow Write-Host "" $tempChoice = [Console]::ReadKey($true) if ($tempChoice.KeyChar.ToString().ToUpper() -eq "Y") { # Offer to close apps that may be holding temp files open. $tempLockingApps = Get-TempLockingProcesses if ($tempLockingApps.Count -gt 0) { Write-Host "`nThe following applications are currently running and may block temp file clearance:" -ForegroundColor Yellow foreach ($app in $tempLockingApps) { Write-Host "- $($app.FriendlyName)" -ForegroundColor Yellow } Write-Host "" Write-Host "Would you like to automatically close these applications before clearing temp files? (Y/N)" -ForegroundColor Yellow $retryKey = [Console]::ReadKey($true) if ($retryKey.KeyChar.ToString().ToUpper() -eq "Y") { foreach ($app in $tempLockingApps) { Write-Host "Closing $($app.FriendlyName)..." -ForegroundColor Cyan Get-Process | Where-Object { $_.ProcessName.ToLower() -eq $app.ProcessName } | Stop-Process -Force -ErrorAction SilentlyContinue } Start-Sleep -Seconds 2 } else { Log "✖ User chose not to close running applications. Proceeding to clear available temp files." "Yellow" } } Clear-WindowsTemp switch ($script:tempCleanupOutcome) { 'PASS' { Add-MaintenanceResult -Name 'Windows Temp cleanup' -Status PASS -Summary 'Attempted cleanup completed with no reported file deletion errors.' Log "✔ Windows Temp cleanup completed." "Cyan" } 'WARN' { Add-MaintenanceResult -Name 'Windows Temp cleanup' -Status WARN -Summary 'Some temp files or links were skipped.' -Action 'Close apps using temporary files and retry if storage remains low.' Log "Windows Temp cleanup was only partially completed." "Yellow" } 'SKIP' { Add-MaintenanceResult -Name 'Windows Temp cleanup' -Status SKIP -Summary 'TEMP directory was unavailable; no files were deleted.' } default { Add-MaintenanceResult -Name 'Windows Temp cleanup' -Status FAIL -Summary 'Cleanup failed or was blocked for safety.' -Action 'Inspect TEMP path and technical log; do not manually delete unknown directories.' Log "Windows Temp cleanup was not completed." "Red" } } } else { Add-MaintenanceResult -Name 'Windows Temp cleanup' -Status SKIP -Summary 'User declined temporary-file cleanup.' Log "✖ User chose not to clear Windows Temp files." "Yellow" } } else { Add-MaintenanceResult -Name 'Windows Temp cleanup' -Status SKIP -Summary 'No removable TEMP file size was measured, or the directory was unavailable.' Log "No Windows Temp files were measured for cleanup." "Cyan" } Write-Host "" # Check free disk space. Check-DiskSpace # Sample CPU usage. Check-CPUUsage # Flush the DNS resolver cache. Flush-DNSCache # Sample memory usage. Check-RAMUsage Write-Host "" # Check the installed graphics drivers. Check-GPUDrivers Write-Host "" # Blank line between GPU and System Uptime # Check how long Windows has been running. Check-SystemUptime Write-Host "" # Blank line between uptime alerts and Cache Smash # Original Cache Smash animation. $endTime = (Get-Date).AddSeconds(5) $dotStates = @(".", "..", "...") $dotIndex = 0 while ((Get-Date) -lt $endTime) { $display = "Cache Smash" + $dotStates[$dotIndex] Write-Host -NoNewline "$display`r" -ForegroundColor Cyan Start-Sleep -Milliseconds 500 $dotIndex = ($dotIndex + 1) % $dotStates.Count } Write-Host "" Start-Sleep -Seconds 2 # Try to clear browser caches. Clear-BrowserCaches # Explain the DISM check before it starts. Write-Host "" Write-Host "We'll check Windows repair files and offer to fix any repairable damage found." -ForegroundColor Yellow Write-Host "A repair only starts if you agree to it; it can take a while." -ForegroundColor Yellow Start-Sleep -Seconds 3 Write-Host "" # Check component store health with DISM. Run-DISMCheckHealth Write-Host "" # Run the Windows system file check. Run-SFCScan Write-Host "" # Check Windows Update and attempt installation. Install-WindowsUpdates # Write both reports after the last maintenance step. Write-Host "" $spaceChangeFormatted = 'Not measured' try { $finalFree = (Get-PSDrive -Name $selectedDrive.Substring(0,1) -ErrorAction Stop).Free $spaceChangeBytes = $finalFree - $initialFreeSpace $spaceChangeAbsolute = [math]::Abs($spaceChangeBytes) $spaceChangeUnit = if ($spaceChangeAbsolute -ge 1GB) { 'GB' } else { 'MB' } $spaceChangeValue = if ($spaceChangeUnit -eq 'GB') { [math]::Round($spaceChangeAbsolute / 1GB, 2) } else { [math]::Round($spaceChangeAbsolute / 1MB, 2) } $spaceChangeFormatted = if ($spaceChangeBytes -gt 0) { "$spaceChangeValue $spaceChangeUnit more free" } elseif ($spaceChangeBytes -lt 0) { "$spaceChangeValue $spaceChangeUnit less free" } else { 'No change measured' } } catch { Log "Unable to measure final disk space: $_" "Yellow" } Log "Change in available disk space (approximate): $spaceChangeFormatted" "Cyan" $finishedAt = Get-Date Log ("Total script runtime: " + ($finishedAt - $script:StartTime).ToString()) "Cyan" Log "=============================================" "Cyan" Log "YaugerAIO maintenance workflow completed." "Blue" $technicalFileName = "YaugerAIO_Technical_$timestamp.txt" $reportText = Format-YaugerMaintenanceReport -Results $script:maintenanceResults -StartedAt $script:StartTime -FinishedAt $finishedAt -ComputerName $env:COMPUTERNAME -Drive $selectedDrive -SpaceFreed $spaceChangeFormatted -RestartRequired $script:needsRestart -TechnicalFile $technicalFileName $technicalText = @( 'YaugerAIO | Technical Diagnostic Log', ('Computer: ' + $env:COMPUTERNAME), ('Started: ' + $script:StartTime.ToString('yyyy-MM-dd HH:mm:ss')), 'For a summary and recommended actions, open the accompanying YaugerAIO_Report file.', '============================================================' ) + $script:log try { $savedFiles = Save-YaugerMaintenanceReport -ReportText $reportText -TechnicalText ($technicalText -join [Environment]::NewLine) -Timestamp $timestamp Write-Host "" Write-Host "Readable maintenance report: $($savedFiles.ReportPath)" -ForegroundColor Green Write-Host "Recorded technical details: $($savedFiles.TechnicalPath)" -ForegroundColor Cyan } catch { Write-Host "Unable to save maintenance report: $_" -ForegroundColor Red } Write-Host "" $thankYouMessage = "Thanks for trying out my Windows AIO Maintenance Tool. For feature requests or bug reports, please feel welcome to reach out or open an issue at https://github.com/Graytools/YaugerAIO." Write-Host (Wrap-Text -Text $thankYouMessage -Width 130) -ForegroundColor Blue Write-Host "" try { Stop-Transcript } catch { # A transcript isn't started on every launch. } |