This commit is contained in:
Joeri 2026-02-07 11:19:57 +01:00
parent 21ac65f282
commit 66188144d2

View file

@ -18,7 +18,7 @@ $LogDirectory = "C:\Quest"
$LogDate = Get-Date -Format "yyyy-MM-dd" $LogDate = Get-Date -Format "yyyy-MM-dd"
$RandomSuffix = Get-Random -Minimum 1000 -Maximum 9999 $RandomSuffix = Get-Random -Minimum 1000 -Maximum 9999
$LogFile = Join-Path $LogDirectory "cleanup-${LogDate}-${RandomSuffix}.log" $LogFile = Join-Path $LogDirectory "cleanup-${LogDate}-${RandomSuffix}.log"
$WebhookUrl = "https://n8n.questcomputers.be/webhook/99077b7d-140f-477e-9241-2b9581ddaf34" $WebhookUrl = "https://n8n.questcomputers.be/webhook-test/99077b7d-140f-477e-9241-2b9581ddaf34"
$ScriptStartTime = Get-Date $ScriptStartTime = Get-Date
# Configuration - Timeout for winget (in seconds) # Configuration - Timeout for winget (in seconds)
@ -32,7 +32,7 @@ if (-not (Test-Path $LogDirectory)) {
# Initialize structured data object # Initialize structured data object
$ScriptResult = [ordered]@{ $ScriptResult = [ordered]@{
"script_name" = "System Cleanup Script" "script_name" = "System Cleanup Script"
"version" = "1.1" "version" = "1.2"
"execution_info" = @{ "execution_info" = @{
"started_at" = $ScriptStartTime.ToString("yyyy-MM-dd HH:mm:ss") "started_at" = $ScriptStartTime.ToString("yyyy-MM-dd HH:mm:ss")
"computer_name" = $env:COMPUTERNAME "computer_name" = $env:COMPUTERNAME
@ -338,7 +338,6 @@ if ($ScriptResult.app_updates.winget_available) {
# Create temporary files for output # Create temporary files for output
$WingetOutputFile = Join-Path $LogDirectory "winget_output.txt" $WingetOutputFile = Join-Path $LogDirectory "winget_output.txt"
$WingetErrorFile = Join-Path $LogDirectory "winget_error.txt"
# Start winget process with timeout # Start winget process with timeout
$wingetProcess = Start-Process -FilePath "winget" -ArgumentList "upgrade --all --silent --accept-package-agreements --accept-source-agreements" -NoNewWindow -Wait -PassThru -ErrorAction Stop $wingetProcess = Start-Process -FilePath "winget" -ArgumentList "upgrade --all --silent --accept-package-agreements --accept-source-agreements" -NoNewWindow -Wait -PassThru -ErrorAction Stop
@ -349,9 +348,13 @@ if ($ScriptResult.app_updates.winget_available) {
Write-Log "Winget exit code: $WingetExitCode" -Level "INFO" Write-Log "Winget exit code: $WingetExitCode" -Level "INFO"
Write-Log "Winget duration: $($WingetDuration.ToString('F2')) seconds" -Level "INFO" Write-Log "Winget duration: $($WingetDuration.ToString('F2')) seconds" -Level "INFO"
# Read output if available # Parse winget output from log file if available
if (Test-Path $WingetOutputFile) { $wingetOutput = Get-Content $LogFile | Select-String -Pattern "winget|Winget|Upgrading|Upgraded" -Context 2 | Out-String
$wingetOutput = Get-Content $WingetOutputFile -Raw $wingetOutput | Out-File -FilePath $LogFile -Append -Encoding UTF8
# Alternative: Try to capture output directly
try {
$wingetOutput = winget upgrade --all --silent 2>&1 | Out-String
$wingetOutput | Out-File -FilePath $LogFile -Append -Encoding UTF8 $wingetOutput | Out-File -FilePath $LogFile -Append -Encoding UTF8
# Parse winget output to extract updated apps # Parse winget output to extract updated apps
@ -374,16 +377,14 @@ if ($ScriptResult.app_updates.winget_available) {
$ScriptResult.app_updates.apps_updated = $AppsUpdated $ScriptResult.app_updates.apps_updated = $AppsUpdated
$ScriptResult.app_updates.apps_checked = $AppsUpdated.Count $ScriptResult.app_updates.apps_checked = $AppsUpdated.Count
} catch {
# Clean up temporary files Write-Log "Could not capture winget output: $($_.Exception.Message)" -Level "WARNING"
Remove-Item $WingetOutputFile -ErrorAction SilentlyContinue
Remove-Item $WingetErrorFile -ErrorAction SilentlyContinue
} }
if ($AppsUpdated.Count -gt 0) { if ($AppsUpdated.Count -gt 0) {
Write-Log "Application updates completed. Updated $($AppsUpdated.Count) application(s): $($AppsUpdated -join ', ')" -Level "INFO" Write-Log "Application updates completed. Updated $($AppsUpdated.Count) application(s): $($AppsUpdated -join ', ')" -Level "INFO"
} else { } else {
Write-Log "Application updates completed. All applications are up to date." -Level "INFO" Write-Log "Application updates completed. All applications are up to date or winget output could not be parsed." -Level "INFO"
} }
} catch { } catch {
# Check if it's a timeout # Check if it's a timeout
@ -405,113 +406,134 @@ Write-Log "Application updates section completed." -Level "INFO"
"" | Out-File -FilePath $LogFile -Append -Encoding UTF8 "" | Out-File -FilePath $LogFile -Append -Encoding UTF8
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
# Updating Windows # Updating Windows (using Windows Update API directly)
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
Write-Log "================================================================================" -Level "INFO" Write-Log "================================================================================" -Level "INFO"
Write-Log "SECTION: Windows Updates" -Level "INFO" Write-Log "SECTION: Windows Updates" -Level "INFO"
Write-Log "================================================================================" -Level "INFO" Write-Log "================================================================================" -Level "INFO"
# Install NuGet provider # Load Windows Update COM object
Write-Log "Installing NuGet package provider..." -Level "INFO" Write-Log "Initializing Windows Update service..." -Level "INFO"
try { try {
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction Stop | Out-Null $UpdateSession = New-Object -ComObject "Microsoft.Update.Session"
$ScriptResult.windows_updates.nuget_installed = $true $UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
Write-Log "NuGet package provider installed successfully." -Level "INFO" $UpdateDownloader = $UpdateSession.CreateUpdateDownloader()
} catch { $UpdateInstaller = $UpdateSession.CreateUpdateInstaller()
Write-Log "Failed to install NuGet: $($_.Exception.Message)" -Level "ERROR"
}
# Install PSWindowsUpdate module Write-Log "Searching for available Windows updates..." -Level "INFO"
Write-Log "Installing PSWindowsUpdate module..." -Level "INFO"
try {
Install-Module PSWindowsUpdate -Force -AllowClobber -ErrorAction Stop | Out-Null
$ScriptResult.windows_updates.pswindowsupdate_installed = $true
Write-Log "PSWindowsUpdate module installed successfully." -Level "INFO"
} catch {
Write-Log "Failed to install PSWindowsUpdate: $($_.Exception.Message)" -Level "ERROR"
}
# Import PSWindowsUpdate module # Search for updates
if ($ScriptResult.windows_updates.pswindowsupdate_installed) { $SearchResult = $UpdateSearcher.Search("IsInstalled=0 and Type='Software'")
try { $AvailableUpdates = $SearchResult.Updates
Write-Log "Importing PSWindowsUpdate module..." -Level "INFO"
Import-Module PSWindowsUpdate -ErrorAction Stop
Write-Log "PSWindowsUpdate module imported successfully." -Level "INFO"
} catch {
Write-Log "Failed to import PSWindowsUpdate: $($_.Exception.Message)" -Level "ERROR"
}
}
# Check for and install Windows updates $ScriptResult.windows_updates.updates_available = $AvailableUpdates.Count
if ($ScriptResult.windows_updates.pswindowsupdate_installed) { Write-Log "Found $($AvailableUpdates.Count) Windows update(s)." -Level "INFO"
try {
Write-Log "Checking for and installing Windows updates..." -Level "INFO"
# Run Windows update check and install in one command if ($AvailableUpdates.Count -gt 0) {
$UpdateOutput = Get-WindowsUpdate -AcceptAll -Install -IgnoreUser -IgnoreReboot -ErrorAction Stop 2>&1 | Out-String # Prepare download collection
$UpdateOutput | Out-File -FilePath $LogFile -Append -Encoding UTF8 $UpdatesToDownload = New-Object -ComObject "Microsoft.Update.UpdateColl"
# Parse the output to extract KB numbers and titles foreach ($Update in $AvailableUpdates) {
$UpdateLines = $UpdateOutput -split "`n"
$UpdatesFound = $false
$UpdatesInstalled = @()
foreach ($line in $UpdateLines) {
# Look for KB pattern in the output
if ($line -match "KB\d+" -or $line -match "Update for|Security Update|Cumulative Update") {
$UpdatesFound = $true
# Extract KB number
$kbMatch = [regex]::Match($line, 'KB\d+')
$kb = if ($kbMatch.Success) { $kbMatch.Value } else { "Unknown" }
# Extract title (remove KB number and extra spaces)
$title = $line -replace 'KB\d+\s*' -replace '\s+' -replace '^\s*|\s*$' -trim
if ($title -and $title.Length -gt 3) {
$UpdateInfo = @{ $UpdateInfo = @{
"kb" = $kb "kb" = if ($Update.KBArticleIDs.Count -gt 0) { "KB$($Update.KBArticleIDs[0])" } else { "Unknown" }
"title" = $title "title" = $Update.Title
"description" = "" "description" = $Update.Description
"size_mb" = 0 "size_mb" = [math]::Round($Update.MaxDownloadSize / 1MB, 2)
"is_downloaded" = $true "is_downloaded" = $false
"reboot_required" = $false "reboot_required" = ($Update.InstallationBehavior.RebootBehavior -ne "NeverRequiresReboot")
} }
$UpdatesInstalled += $UpdateInfo $ScriptResult.windows_updates.updates_installed += $UpdateInfo
# Add to download collection
$UpdatesToDownload.Add($Update) | Out-Null
} }
# Download updates
Write-Log "Downloading $($AvailableUpdates.Count) update(s)..." -Level "INFO"
$UpdateDownloader.Updates = $UpdatesToDownload
$DownloadResult = $UpdateDownloader.Download()
# Check download result
$DownloadResultCode = switch ($DownloadResult.ResultCode) {
0 { "NotStarted" }
1 { "InProgress" }
2 { "Succeeded" }
3 { "SucceededWithErrors" }
4 { "Failed" }
5 { "Aborted" }
default { "Unknown" }
}
Write-Log "Download result: $DownloadResultCode" -Level "INFO"
if ($DownloadResult.ResultCode -in @(2, 3)) {
# Prepare installation collection
$UpdatesToInstall = New-Object -ComObject "Microsoft.Update.UpdateColl"
foreach ($Update in $AvailableUpdates) {
if ($Update.IsDownloaded) {
$UpdatesToInstall.Add($Update) | Out-Null
} }
} }
$ScriptResult.windows_updates.updates_installed = $UpdatesInstalled # Install updates
$ScriptResult.windows_updates.updates_available = $UpdatesInstalled.Count Write-Log "Installing $($UpdatesToInstall.Count) update(s)..." -Level "INFO"
$UpdateInstaller.Updates = $UpdatesToInstall
$InstallResult = $UpdateInstaller.Install()
# Check if reboot is required from output # Check installation result
if ($UpdateOutput -match "reboot|Reboot|restart|restart your computer") { $InstallResultCode = switch ($InstallResult.ResultCode) {
0 { "NotStarted" }
1 { "InProgress" }
2 { "Succeeded" }
3 { "SucceededWithErrors" }
4 { "Failed" }
5 { "Aborted" }
default { "Unknown" }
}
Write-Log "Installation result: $InstallResultCode" -Level "INFO"
# Check if reboot is required
if ($InstallResult.RebootRequired) {
$ScriptResult.windows_updates.reboot_required = $true $ScriptResult.windows_updates.reboot_required = $true
} }
# Update status for downloaded updates
for ($i = 0; $i -lt $ScriptResult.windows_updates.updates_installed.Count; $i++) {
$ScriptResult.windows_updates.updates_installed[$i].is_downloaded = $true
}
}
Write-Log "Windows updates installation completed." -Level "INFO" Write-Log "Windows updates installation completed." -Level "INFO"
if ($UpdatesInstalled.Count -gt 0) { if ($ScriptResult.windows_updates.updates_installed.Count -gt 0) {
Write-Log "Installed $($UpdatesInstalled.Count) update(s):" -Level "INFO" Write-Log "Installed $($ScriptResult.windows_updates.updates_installed.Count) update(s):" -Level "INFO"
foreach ($update in $UpdatesInstalled) { foreach ($update in $ScriptResult.windows_updates.updates_installed) {
Write-Log " - $($update.kb): $($update.title)" -Level "INFO" Write-Log " - $($update.kb): $($update.title)" -Level "INFO"
} }
} else {
Write-Log "No Windows updates were needed or found." -Level "INFO"
} }
if ($ScriptResult.windows_updates.reboot_required) { if ($ScriptResult.windows_updates.reboot_required) {
Write-Log "A system reboot is required to complete the updates." -Level "WARNING" Write-Log "A system reboot is required to complete the updates." -Level "WARNING"
} }
} else {
Write-Log "No Windows updates were needed." -Level "INFO"
}
# Clean up COM objects
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($UpdateInstaller) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($UpdateDownloader) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($UpdateSearcher) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($UpdateSession) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
} catch { } catch {
$ErrorMsg = "Failed to install Windows updates: $($_.Exception.Message)" $ErrorMsg = "Failed to install Windows updates: $($_.Exception.Message)"
$ScriptResult.windows_updates.errors += $ErrorMsg $ScriptResult.windows_updates.errors += $ErrorMsg
Write-Log $ErrorMsg -Level "ERROR" Write-Log $ErrorMsg -Level "ERROR"
} }
} else {
Write-Log "PSWindowsUpdate not installed. Skipping Windows updates." -Level "INFO"
}
Write-Log "Windows updates section completed." -Level "INFO" Write-Log "Windows updates section completed." -Level "INFO"
"" | Out-File -FilePath $LogFile -Append -Encoding UTF8 "" | Out-File -FilePath $LogFile -Append -Encoding UTF8