From 73f4dd908a4cb2dcb00b279ef24da3ec202e0a11 Mon Sep 17 00:00:00 2001 From: Martien Date: Fri, 10 Jul 2026 12:15:42 +0200 Subject: [PATCH] Add SCCM-WU/SCCM-Update-Install-Forensics.ps1 --- SCCM-WU/SCCM-Update-Install-Forensics.ps1 | 220 ++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 SCCM-WU/SCCM-Update-Install-Forensics.ps1 diff --git a/SCCM-WU/SCCM-Update-Install-Forensics.ps1 b/SCCM-WU/SCCM-Update-Install-Forensics.ps1 new file mode 100644 index 0000000..acf4951 --- /dev/null +++ b/SCCM-WU/SCCM-Update-Install-Forensics.ps1 @@ -0,0 +1,220 @@ +<# +.SYNOPSIS + SCCM Update Install Forensics - wie/wat heeft de patch-installatie en reboot getriggerd? +.DESCRIPTION + Doorzoekt CCM logs, WMI, event logs en registry om te bepalen of een update-installatie + user-initiated (Software Center) of deployment-enforced was, en wat de reboot triggerde. +.USAGE + Lokaal op de server draaien als admin: + .\Get-SCCMInstallForensics.ps1 -Hours 72 + Optioneel: -OutFile C:\Temp\rapport.txt +#> + +param( + [int]$Hours = 72, + [string]$LogPath = "$env:windir\CCM\Logs", + [string]$OutFile +) + +$ErrorActionPreference = 'SilentlyContinue' +$since = (Get-Date).AddHours(-$Hours) +$report = New-Object System.Collections.Generic.List[string] + +function Add-Line { param($t) $report.Add($t); Write-Host $t } +function Add-Section { param($t) Add-Line ""; Add-Line ("=" * 80); Add-Line " $t"; Add-Line ("=" * 80) } + +# CCM logtijd parser: "message" component="..." time="HH:mm:ss.fff+060" date="MM-dd-yyyy" +function Get-CMLogEntries { + param([string[]]$LogNames, [string]$Pattern) + $results = @() + foreach ($name in $LogNames) { + $files = Get-ChildItem -Path $LogPath -Filter $name -File | Sort-Object LastWriteTime + foreach ($f in $files) { + if ($f.LastWriteTime -lt $since) { continue } + $matches = Select-String -Path $f.FullName -Pattern $Pattern + foreach ($m in $matches) { + $line = $m.Line + $ts = $null + if ($line -match 'time="(\d{2}:\d{2}:\d{2})[^"]*"\s+date="(\d{2}-\d{2}-\d{4})"') { + $ts = [datetime]::ParseExact("$($Matches[2]) $($Matches[1])", "MM-dd-yyyy HH:mm:ss", $null) + } + if ($ts -and $ts -lt $since) { continue } + $msg = if ($line -match ' niemand had Software Center open." +} + +# ----------------------------------------------------------------------------- +Add-Section "3. REBOOT TRIGGER (RebootCoordinator.log)" +# ----------------------------------------------------------------------------- +$rb = Get-CMLogEntries -LogNames 'RebootCoordinator*.log' -Pattern 'Reboot|Restart|initiat|schedul|shutdown|user' +if ($rb) { $rb | ForEach-Object { Add-Line " $($_.Time) | $($_.Msg)" } } +else { Add-Line "[i] Geen reboot-activiteit in RebootCoordinator.log binnen de periode." } + +# ----------------------------------------------------------------------------- +Add-Section "4. MAINTENANCE WINDOW EVALUATIE (ServiceWindowManager.log)" +# ----------------------------------------------------------------------------- +$sw = Get-CMLogEntries -LogNames 'ServiceWindowManager*.log' -Pattern 'service window|ServiceWindow|Biggest|No service|OnIsServiceWindowAvailable|Schedule' +if ($sw) { + $noMW = $sw | Where-Object { $_.Msg -match 'No service windows exist' } + if ($noMW) { Add-Line "[!] LET OP: 'No service windows exist' gevonden -> client mag altijd installeren!" } + $sw | Select-Object -Last 30 | ForEach-Object { Add-Line " $($_.Time) | $($_.Msg)" } +} else { Add-Line "[i] Geen ServiceWindowManager entries binnen de periode." } + +Add-Line "" +Add-Line "--- Actuele maintenance windows via WMI (root\ccm\clientsdk CCM_ServiceWindow):" +$mws = Get-CimInstance -Namespace 'root\ccm\clientsdk' -ClassName CCM_ServiceWindow +if ($mws) { + foreach ($mw in $mws) { + $type = switch ($mw.Type) { 1{'All deployments'} 2{'Program'} 3{'Reboot required'} 4{'Software updates'} 5{'Task sequence'} 6{'User defined'} default{$mw.Type} } + Add-Line " ID: $($mw.ID) | Type: $type | Start: $($mw.StartTime) | Eind: $($mw.EndTime)" + } +} else { + Add-Line " [!] GEEN maintenance windows op deze client gedefinieerd." +} + +# ----------------------------------------------------------------------------- +Add-Section "5. UPDATE HANDLER / INSTALL DETAILS (UpdatesHandler.log, WUAHandler.log)" +# ----------------------------------------------------------------------------- +$uh = Get-CMLogEntries -LogNames 'UpdatesHandler*.log','WUAHandler*.log' -Pattern 'Initiating|install|Successfully completed|reboot|pending|commit' +$uh | Select-Object -Last 40 | ForEach-Object { Add-Line " $($_.Time) | $($_.Msg)" } + +# ----------------------------------------------------------------------------- +Add-Section "6. POLICY / DEADLINE CHECK (PolicyAgent.log, UpdatesStore.log)" +# ----------------------------------------------------------------------------- +$pa = Get-CMLogEntries -LogNames 'PolicyAgent*.log' -Pattern 'assignment|deadline|policy.*update|CCM_UpdateCIAssignment' +$pa | Select-Object -Last 20 | ForEach-Object { Add-Line " $($_.Time) | $($_.Msg)" } + +Add-Line "" +Add-Line "--- Actieve update assignments via WMI (deadlines):" +$assignments = Get-CimInstance -Namespace 'root\ccm\policy\machine\actualconfig' -ClassName CCM_UpdateCIAssignment +if ($assignments) { + foreach ($a in $assignments) { + $deadline = if ($a.EnforcementDeadline) { [Management.ManagementDateTimeConverter]::ToDateTime($a.EnforcementDeadline) } else { 'geen' } + Add-Line " '$($a.AssignmentName)' | Deadline: $deadline | Ignore MW (install): $($a.OverrideServiceWindows) | Ignore MW (reboot): $($a.RebootOutsideOfServiceWindows)" + if ($a.OverrideServiceWindows -or $a.RebootOutsideOfServiceWindows) { + Add-Line " [!] DEZE DEPLOYMENT NEGEERT MAINTENANCE WINDOWS!" + } + } +} else { + Add-Line " [i] Geen actieve update assignments in policy." +} + +# ----------------------------------------------------------------------------- +Add-Section "7. WINDOWS EVENT LOGS - WIE STARTTE DE REBOOT?" +# ----------------------------------------------------------------------------- +# Event 1074 = geplande/geinitieerde shutdown incl. proces en user +$ev1074 = Get-WinEvent -FilterHashtable @{ LogName='System'; Id=1074; StartTime=$since } -ErrorAction SilentlyContinue +if ($ev1074) { + Add-Line "--- Event 1074 (shutdown initiated):" + foreach ($e in $ev1074) { + Add-Line " $($e.TimeCreated) | User: $($e.Properties[6].Value) | Proces: $($e.Properties[0].Value) | Reden: $($e.Properties[2].Value) | Type: $($e.Properties[4].Value)" + } +} else { Add-Line "[i] Geen 1074 events binnen de periode." } + +$ev6008 = Get-WinEvent -FilterHashtable @{ LogName='System'; Id=6008; StartTime=$since } -ErrorAction SilentlyContinue +if ($ev6008) { + Add-Line "--- Event 6008 (unexpected shutdown):" + $ev6008 | ForEach-Object { Add-Line " $($_.TimeCreated) | $($_.Message -replace '\r?\n',' ')" } +} + +# WindowsUpdateClient events: 19=install success, 20=failure, 22=reboot required +$wuEv = Get-WinEvent -FilterHashtable @{ LogName='System'; ProviderName='Microsoft-Windows-WindowsUpdateClient'; StartTime=$since } -ErrorAction SilentlyContinue +if ($wuEv) { + Add-Line "" + Add-Line "--- WindowsUpdateClient events (geinstalleerde updates):" + $wuEv | Select-Object -First 25 | ForEach-Object { Add-Line " $($_.TimeCreated) | ID $($_.Id) | $($_.Message -replace '\r?\n',' ' -replace '\s+',' ')" } +} + +# Interactieve logons rond het install-moment (4624 type 2/10) +Add-Line "" +Add-Line "--- Interactieve logons (4624, type 2=console / 10=RDP) binnen periode:" +$logons = Get-WinEvent -FilterHashtable @{ LogName='Security'; Id=4624; StartTime=$since } -MaxEvents 2000 -ErrorAction SilentlyContinue | + Where-Object { $_.Properties[8].Value -in 2,10 } +if ($logons) { + $logons | ForEach-Object { + Add-Line " $($_.TimeCreated) | User: $($_.Properties[5].Value)\$($_.Properties[6].Value) | Type: $($_.Properties[8].Value) | Bron: $($_.Properties[18].Value)" + } +} else { Add-Line " [i] Geen interactieve logons gevonden (of Security log te kort/geen rechten)." } + +# ----------------------------------------------------------------------------- +Add-Section "8. CONCLUSIE-INDICATOREN" +# ----------------------------------------------------------------------------- +$conclusions = @() +if ($userInit) { $conclusions += "[!] Bewijs voor USER-INITIATED install via Software Center gevonden (sectie 1)." } +if ($scFiles) { $conclusions += "[!] Software Center was open bij minimaal 1 user (sectie 2) - correleer tijdstippen." } +if (-not $mws) { $conclusions += "[!] Geen maintenance windows op de client - enforced installs mochten op elk moment draaien (sectie 4)." } +if ($assignments | Where-Object { $_.OverrideServiceWindows -or $_.RebootOutsideOfServiceWindows }) { + $conclusions += "[!] Er is een deployment die MW's negeert (sectie 6)." +} +$deadlinePassed = $assignments | Where-Object { $_.EnforcementDeadline -and ([Management.ManagementDateTimeConverter]::ToDateTime($_.EnforcementDeadline) -lt (Get-Date)) } +if ($deadlinePassed) { $conclusions += "[i] Er zijn assignments met een verstreken deadline - enforced install mogelijk direct na collection-membership of policy refresh." } +$sccmReboot = $ev1074 | Where-Object { $_.Properties[0].Value -match 'CcmExec|wuauclt|TrustedInstaller|CcmRestart' } +if ($sccmReboot) { $conclusions += "[i] Reboot geinitieerd door SCCM/WU proces bevestigd via event 1074." } +$userReboot = $ev1074 | Where-Object { $_.Properties[6].Value -notmatch 'SYSTEM|^$' } +if ($userReboot) { $conclusions += "[!] Er is een reboot geinitieerd onder een USER-account (geen SYSTEM) - zie 1074 events." } + +if ($conclusions) { $conclusions | ForEach-Object { Add-Line $_ } } +else { Add-Line "[i] Geen duidelijke indicatoren. Vergelijk handmatig de tijdstippen in secties 1, 3 en 7." } + +# ----------------------------------------------------------------------------- +if ($OutFile) { + $report | Out-File -FilePath $OutFile -Encoding UTF8 + Write-Host "`nRapport weggeschreven naar: $OutFile" -ForegroundColor Green +} \ No newline at end of file