<# .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 }