$ErrorActionPreference = 'Stop' $outDir = 'C:\install\missing_updates' if (-not (Test-Path $outDir)) { New-Item -Path $outDir -ItemType Directory -Force | Out-Null } $logFile = Join-Path $outDir 'missing_updates.txt' if (Test-Path $logFile) { Remove-Item $logFile -Force } function Log { param([string]$Text) Write-Host $Text Add-Content -Path $logFile -Value $Text } Log "Online WU scan (buiten WSUS om) - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Log "Server: $env:COMPUTERNAME" Log ('=' * 70) # --- Online scan tegen Microsoft Update, WSUS negeren --- Log "" Log "Scannen tegen Microsoft Update (kan een paar minuten duren)..." $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $searcher.ServerSelection = 3 # ssOthers = expliciet niet WSUS $searcher.ServiceID = '7971f918-a847-4430-9279-4a52d1efe18d' # Microsoft Update $result = $searcher.Search("IsInstalled=0 AND Type='Software' AND IsHidden=0") $count = $result.Updates.Count Log "Aantal ontbrekende updates gevonden: $count" Log ('=' * 70) if ($count -eq 0) { Log "" Log "Geen ontbrekende updates. Server is up-to-date volgens Microsoft Update." return } # --- Per update: details loggen en download-URL's verzamelen --- $downloadList = @() for ($i = 0; $i -lt $count; $i++) { $u = $result.Updates.Item($i) $kb = ($u.KBArticleIDs | ForEach-Object { "KB$_" }) -join ', ' Log "" Log ("-" * 70) Log "[$($i+1)/$count] $($u.Title)" Log " KB: $kb" Log " Ernst: $($u.MsrcSeverity)" Log " UpdateID: $($u.Identity.UpdateID)" # Download-URL's uit de bundle halen (bundelt vaak de echte .msu) $bundled = $u.BundledUpdates if ($bundled.Count -eq 0) { # Geen bundle, probeer update zelf foreach ($c in $u.DownloadContents) { if ($c.DownloadUrl) { Log " URL: $($c.DownloadUrl)" $downloadList += [PSCustomObject]@{ KB = $kb; Url = $c.DownloadUrl } } } } else { for ($b = 0; $b -lt $bundled.Count; $b++) { $bu = $bundled.Item($b) foreach ($c in $bu.DownloadContents) { if ($c.DownloadUrl) { Log " URL: $($c.DownloadUrl)" $downloadList += [PSCustomObject]@{ KB = $kb; Url = $c.DownloadUrl } } } } } } # --- Download-URL's apart wegschrijven --- $urlFile = Join-Path $outDir 'download_urls.txt' $downloadList | ForEach-Object { $_.Url } | Sort-Object -Unique | Set-Content $urlFile Log "" Log ('=' * 70) Log "Alle download-URL's staan in: $urlFile" # --- Bestanden downloaden via BITS --- Log "" Log "Downloaden naar $outDir ..." $n = 0 foreach ($item in ($downloadList | Sort-Object Url -Unique)) { $n++ $fileName = Split-Path $item.Url -Leaf # Zorg dat de bestandsnaam het KB-nummer bevat voor herkenbaarheid $kbClean = ($item.KB -replace '[, ]', '_') $dest = Join-Path $outDir "$($kbClean)__$fileName" Log " [$n] $fileName" try { Start-BitsTransfer -Source $item.Url -Destination $dest -ErrorAction Stop $size = [math]::Round((Get-Item $dest).Length / 1MB, 1) Log " OK ($size MB)" } catch { Log " MISLUKT: $($_.Exception.Message)" Log " Handmatig downloaden via bovenstaande URL." } } Log "" Log ('=' * 70) Log "Klaar: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Log "Rapport: $logFile" Log "URL-lijst: $urlFile" Log "Bestanden: $outDir"