diff --git a/HealthCheck/PCHealthCheck.ps1 b/HealthCheck/PCHealthCheck.ps1 index d723d9d..96fa516 100644 --- a/HealthCheck/PCHealthCheck.ps1 +++ b/HealthCheck/PCHealthCheck.ps1 @@ -87,7 +87,7 @@ PC Health Check - Remote Edition Reads computer names from a text file and checks each one. .NOTES - Author: Enterprise Admin + Author: Kevin Simmons Version: 3.0 Date: 2026-10-04 diff --git a/PCRemediation/Invoke-PCRemediation.ps1 b/PCRemediation/Invoke-PCRemediation.ps1 new file mode 100644 index 0000000..d734ca4 --- /dev/null +++ b/PCRemediation/Invoke-PCRemediation.ps1 @@ -0,0 +1,438 @@ +<#================================================================================= +PC Remediation & Cleanup - Remote Edition +================================================================================= +.SYNOPSIS + Performs system cleanup, maintenance, and service remediation on local or remote Windows computers. + +.DESCRIPTION + This script provides targeted manual remediation tools to free up disk space, restart stopped + critical services, clear update caches, purge temporary logs, and handle pending reboot flags. + + Compatible with Windows PowerShell 5.1 and supports local/remote CIM sessions (DCOM and WinRM). + +.PARAMETER ComputerName + Specifies target computer(s). Default: Localhost. + +.PARAMETER Credential + PSCredential for remote connections. + +.PARAMETER LogToFile + Logs output to Desktop\PCRemediation. + +.PARAMETER LogPath + Specifies custom log path. Default: Desktop\PCRemediation. + +.PARAMETER Silent + Runs script without interactive prompts or console output. + +.PARAMETER ExportHTML + Generates an HTML report of remediation tasks performed. + +.PARAMETER RunAllRemediations + Executes all cleanup and maintenance tasks without interactive prompts. + +.PARAMETER Help + Displays detailed help information. + +.EXAMPLE + .\Invoke-PCRemediation.ps1 + Runs interactive remediation menu on the local computer. + +.EXAMPLE + .\Invoke-PCRemediation.ps1 -ComputerName PC01,PC02 -RunAllRemediations -LogToFile + Executes all remediation tasks on remote computers non-interactively and logs results. + +.EXAMPLE + .\Invoke-PCRemediation.ps1 -ComputerName PC03 -Credential (Get-Credential) -ExportHTML + Runs all remediations on remote computer with credentials and exports HTML report. + +.EXAMPLE + .\Invoke-PCRemediation.ps1 -ComputerName PC04 -Silent + Runs all remediations silently on remote computer. + +.EXAMPLE + .\Invoke-PCRemediation.ps1 -Help + Displays detailed help information. + +.AUTHOR + Kevin Simmons +================================================================================ #> + +param ( + [Parameter(Mandatory=$false)] + [string[]]$ComputerName, + + [Parameter(Mandatory=$false)] + [pscredential]$Credential, + + [Parameter(Mandatory=$false)] + [switch]$LogToFile, + + [Parameter(Mandatory=$false)] + [string]$LogPath, + + [Parameter(Mandatory=$false)] + [switch]$Silent, + + [Parameter(Mandatory=$false)] + [switch]$ExportHTML, + + [Parameter(Mandatory=$false)] + [switch]$RunAllRemediations, + + [Parameter(Mandatory=$false)] + [switch]$Help +) + +if ($Help) { + Get-Help $MyInvocation.MyCommand.Path -Detailed + exit +} + +if (-not $LogPath) { + $LogPath = "$([Environment]::GetFolderPath('Desktop'))\PCRemediation" +} + +$IsLocal = (-not $ComputerName) -or ($ComputerName -contains $env:COMPUTERNAME) + +if ($IsLocal) { + $logFile = "$LogPath\PCRemediation_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" +} else { + $logFile = "$LogPath\PCRemediation_$(Get-Date -Format 'yyyyMMdd_HHmmss')_$(($ComputerName -join '_')).log" +} + +$htmlReportFile = "$LogPath\PCRemediation_$(Get-Date -Format 'yyyyMMdd_HHmmss').html" +$htmlReport = $null + +if ($ExportHTML) { + $htmlReport = @" + + +
+ +[$TargetComputer] $Message
" + } +} + +function Get-CimSessionForComputer { + param ( + [Parameter(Mandatory=$true)][string]$TargetComputer, + [Parameter(Mandatory=$false)][pscredential]$Credential + ) + + $sessionOptions = New-CimSessionOption -Protocol Wsman + try { + return New-CimSession -ComputerName $TargetComputer -SessionOption $sessionOptions -Credential $Credential -ErrorAction Stop + } catch { + $sessionOptions = New-CimSessionOption -Protocol Dcom + try { + return New-CimSession -ComputerName $TargetComputer -SessionOption $sessionOptions -Credential $Credential -ErrorAction Stop + } catch { + Write-ColorOutput "Failed connection to ${TargetComputer}: $_" "Red" "Critical" $TargetComputer + return $null + } + } +} + +function Show-Banner { + param ([string]$TargetComputer = $env:COMPUTERNAME) + if (-not $Silent) { + Clear-Host + Write-Host "=======================================" -ForegroundColor Cyan + Write-Host " ENHANCED PC REMEDIATION " -ForegroundColor Cyan + Write-Host "=======================================" -ForegroundColor Cyan + Write-Host "System: $TargetComputer" -ForegroundColor Cyan + Write-Host "User: $env:USERNAME" -ForegroundColor Cyan + Write-Host "Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Cyan + Write-Host "=======================================" -ForegroundColor Cyan + } +} + +function Show-Menu { + if (-not $Silent) { + Write-Host "`nSelect Remediation Action:" -ForegroundColor Yellow + Write-Host "1. Clear Windows Update Download Cache" -ForegroundColor White + Write-Host "2. Purge System & User Temp Files" -ForegroundColor White + Write-Host "3. Clear Windows Error Reporting (WER) Dumps" -ForegroundColor White + Write-Host "4. Flush DNS Cache & Reset Network Interfaces" -ForegroundColor White + Write-Host "5. Restart Automatic Services That Are Stopped" -ForegroundColor White + Write-Host "6. Empty Recycle Bin (All Users)" -ForegroundColor White + Write-Host "7. Run Component Store Cleanup (DISM)" -ForegroundColor White + Write-Host "8. Execute Full System Remediation (All Items)" -ForegroundColor Green + Write-Host "Q. Quit" -ForegroundColor Red + } +} + +function Clear-WindowsUpdateCache { + param ([string]$TargetComputer = $env:COMPUTERNAME, [Microsoft.Management.Infrastructure.CimSession]$CimSession = $null) + Write-ColorOutput "`nClearing Windows Update Cache..." "Yellow" "Info" $TargetComputer + + try { + if (-not $CimSession -and $TargetComputer -eq $env:COMPUTERNAME) { + Stop-Service -Name "wuauserv", "bits" -Force -ErrorAction SilentlyContinue + $cachePath = "$env:SystemRoot\SoftwareDistribution\Download\*" + Remove-Item -Path $cachePath -Recurse -Force -ErrorAction SilentlyContinue + Start-Service -Name "wuauserv", "bits" -ErrorAction SilentlyContinue + } else { + Invoke-CimMethod -CimSession $CimSession -ClassName Win32_Process -MethodName Create -Arguments @{ + CommandLine = 'cmd.exe /c net stop wuauserv & net stop bits & del /f /s /q %systemroot%\SoftwareDistribution\Download\* & net start wuauserv & net start bits' + } | Out-Null + } + Write-ColorOutput "Windows Update cache successfully purged." "Green" "Good" $TargetComputer + } catch { + Write-ColorOutput "Failed to clear Windows Update cache: $_" "Red" "Critical" $TargetComputer + } +} + +function Clear-TempFiles { + param ([string]$TargetComputer = $env:COMPUTERNAME, [Microsoft.Management.Infrastructure.CimSession]$CimSession = $null) + Write-ColorOutput "`nPurging Temporary Files..." "Yellow" "Info" $TargetComputer + + try { + if (-not $CimSession -and $TargetComputer -eq $env:COMPUTERNAME) { + $tempPaths = @( + "$env:SystemRoot\Temp\*", + "$env:LOCALAPPDATA\Temp\*", + "C:\Users\*\AppData\Local\Temp\*" + ) + foreach ($path in $tempPaths) { + Remove-Item -Path $path -Recurse -Force -ErrorAction Stop + } + } else { + Invoke-CimMethod -CimSession $CimSession -ClassName Win32_Process -MethodName Create -Arguments @{ + CommandLine = 'cmd.exe /c del /f /s /q %systemroot%\Temp\* & del /f /s /q %TEMP%\*' + } | Out-Null + } + Write-ColorOutput "System and user temporary files purged." "Green" "Good" $TargetComputer + } catch { + Write-ColorOutput "Failed to purge temp files: $_" "Red" "Critical" $TargetComputer + } +} + +function Clear-WerDumps { + param ([string]$TargetComputer = $env:COMPUTERNAME, [Microsoft.Management.Infrastructure.CimSession]$CimSession = $null) + Write-ColorOutput "`nClearing Windows Error Reporting (WER) Dumps..." "Yellow" "Info" $TargetComputer + + try { + if (-not $CimSession -and $TargetComputer -eq $env:COMPUTERNAME) { + $werPaths = @( + "$env:ProgramData\Microsoft\Windows\WER\*", + "$env:LOCALAPPDATA\CrashDumps\*" + ) + foreach ($path in $werPaths) { + Remove-Item -Path $path -Recurse -Force -ErrorAction Stop + } + } else { + Invoke-CimMethod -CimSession $CimSession -ClassName Win32_Process -MethodName Create -Arguments @{ + CommandLine = 'cmd.exe /c del /f /s /q %ProgramData%\Microsoft\Windows\WER\*' + } | Out-Null + } + Write-ColorOutput "WER Crash Dumps cleared." "Green" "Good" $TargetComputer + } catch { + Write-ColorOutput "Failed to clear WER Dumps: $_" "Red" "Critical" $TargetComputer + } +} + +function Reset-NetworkStack { + param ([string]$TargetComputer = $env:COMPUTERNAME, [Microsoft.Management.Infrastructure.CimSession]$CimSession = $null) + Write-ColorOutput "`nFlushing DNS Cache and Resetting Network Stack..." "Yellow" "Info" $TargetComputer + + try { + if (-not $CimSession -and $TargetComputer -eq $env:COMPUTERNAME) { + Clear-DnsClientCache + ipconfig /flushdns | Out-Null + } else { + Invoke-CimMethod -CimSession $CimSession -ClassName Win32_Process -MethodName Create -Arguments @{ + CommandLine = 'cmd.exe /c ipconfig /flushdns' + } | Out-Null + } + Write-ColorOutput "DNS Cache successfully flushed." "Green" "Good" $TargetComputer + } catch { + Write-ColorOutput "Failed to reset network stack: $_" "Red" "Critical" $TargetComputer + } +} + +function Repair-StoppedAutomaticServices { + param ([string]$TargetComputer = $env:COMPUTERNAME, [Microsoft.Management.Infrastructure.CimSession]$CimSession = $null) + Write-ColorOutput "`nRestarting Stopped Automatic Services..." "Yellow" "Info" $TargetComputer + + try { + if (-not $CimSession -and $TargetComputer -eq $env:COMPUTERNAME) { + $stoppedServices = Get-Service | Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" -and $_.Name -notmatch "gupdate|RemoteRegistry" } + foreach ($svc in $stoppedServices) { + Start-Service -Name $svc.Name -ErrorAction SilentlyContinue + Write-ColorOutput " Attempted start on service: $($svc.DisplayName)" "White" "Info" $TargetComputer + } + } else { + $stoppedCimServices = Get-CimInstance -ClassName Win32_Service -CimSession $CimSession | + Where-Object { $_.StartMode -eq "Auto" -and $_.State -ne "Running" -and $_.Name -notmatch "gupdate|RemoteRegistry" } + foreach ($svc in $stoppedCimServices) { + Invoke-CimMethod -InputObject $svc -MethodName "StartService" | Out-Null + Write-ColorOutput " Attempted start on remote service: $($svc.Name)" "White" "Info" $TargetComputer + } + } + Write-ColorOutput "Automatic service repair cycle complete." "Green" "Good" $TargetComputer + } catch { + Write-ColorOutput "Failed to restart automatic services: $_" "Red" "Critical" $TargetComputer + } +} + +function Clear-RecycleBinFiles { + param ([string]$TargetComputer = $env:COMPUTERNAME, [Microsoft.Management.Infrastructure.CimSession]$CimSession = $null) + Write-ColorOutput "`nEmptying Recycle Bin..." "Yellow" "Info" $TargetComputer + + try { + if (-not $CimSession -and $TargetComputer -eq $env:COMPUTERNAME) { + Clear-RecycleBin -Force -ErrorAction SilentlyContinue + } else { + Invoke-CimMethod -CimSession $CimSession -ClassName Win32_Process -MethodName Create -Arguments @{ + CommandLine = 'powershell.exe -Command "Clear-RecycleBin -Force -ErrorAction SilentlyContinue"' + } | Out-Null + } + Write-ColorOutput "Recycle Bin emptied." "Green" "Good" $TargetComputer + } catch { + Write-ColorOutput "Failed to empty Recycle Bin: $_" "Red" "Critical" $TargetComputer + } +} + +function Invoke-DismCleanup { + param ([string]$TargetComputer = $env:COMPUTERNAME, [Microsoft.Management.Infrastructure.CimSession]$CimSession = $null) + Write-ColorOutput "`nRunning DISM Component Store Cleanup..." "Yellow" "Info" $TargetComputer + + try { + if (-not $CimSession -and $TargetComputer -eq $env:COMPUTERNAME) { + dism.exe /Online /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null + } else { + Invoke-CimMethod -CimSession $CimSession -ClassName Win32_Process -MethodName Create -Arguments @{ + CommandLine = 'dism.exe /Online /Cleanup-Image /StartComponentCleanup' + } | Out-Null + } + Write-ColorOutput "DISM Component Store Cleanup initiated successfully." "Green" "Good" $TargetComputer + } catch { + Write-ColorOutput "Failed to execute DISM Cleanup: $_" "Red" "Critical" $TargetComputer + } +} + +function Run-AllRemediations { + param ([string]$TargetComputer = $env:COMPUTERNAME) + + Write-ColorOutput "`n===== Running All Remediation Tasks =====" "Cyan" "Info" $TargetComputer + + $cimSession = $null + if ($TargetComputer -ne $env:COMPUTERNAME) { + $cimSession = Get-CimSessionForComputer -TargetComputer $TargetComputer -Credential $Credential + } + + try { + Clear-WindowsUpdateCache -TargetComputer $TargetComputer -CimSession $cimSession + Clear-TempFiles -TargetComputer $TargetComputer -CimSession $cimSession + Clear-WerDumps -TargetComputer $TargetComputer -CimSession $cimSession + Reset-NetworkStack -TargetComputer $TargetComputer -CimSession $cimSession + Repair-StoppedAutomaticServices -TargetComputer $TargetComputer -CimSession $cimSession + Clear-RecycleBinFiles -TargetComputer $TargetComputer -CimSession $cimSession + Invoke-DismCleanup -TargetComputer $TargetComputer -CimSession $cimSession + + Write-ColorOutput "`nRemediation Cycle Completed!" "Cyan" "Good" $TargetComputer + } finally { + if ($cimSession) { Remove-CimSession $cimSession } + } +} + +# Execution Logic +if ($RunAllRemediations -or $Silent) { + if ($ComputerName) { + foreach ($computer in $ComputerName) { + Show-Banner -TargetComputer $computer + Run-AllRemediations -TargetComputer $computer + } + } else { + Show-Banner + Run-AllRemediations + } + exit +} + +# Interactive Loop +Show-Banner +$exitRequested = $false + +while (-not $exitRequested) { + Show-Menu + $choice = Read-Host "Enter your choice" + + switch ($choice.ToUpper()) { + "1" { Clear-WindowsUpdateCache } + "2" { Clear-TempFiles } + "3" { Clear-WerDumps } + "4" { Reset-NetworkStack } + "5" { Repair-StoppedAutomaticServices } + "6" { Clear-RecycleBinFiles } + "7" { Invoke-DismCleanup } + "8" { Run-AllRemediations } + "Q" { Write-ColorOutput "Exiting script..." "Cyan"; $exitRequested = $true } + default { Write-ColorOutput "Invalid selection." "Red" "Critical" } + } + + if (-not $exitRequested) { + Write-ColorOutput "`nPress any key to continue..." "Yellow" + $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") + Clear-Host + Show-Banner + } +}