Files
awoooi/agent99-windows-update-policy.ps1

105 lines
3.9 KiB
PowerShell

param(
[string]$EvidenceDir = "C:\Wooo\Agent99\evidence"
)
$ErrorActionPreference = "Stop"
New-Item -ItemType Directory -Force -Path $EvidenceDir | Out-Null
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
$evidencePath = Join-Path $EvidenceDir "agent99-windows-update-policy-$stamp.json"
$logPath = Join-Path $EvidenceDir "agent99-windows-update-policy-$stamp.log"
function Write-PolicyLog {
param([string]$Message)
"$(Get-Date -Format o) $Message" | Tee-Object -FilePath $logPath -Append | Out-Null
}
function Get-RegistrySnapshot {
param([string]$Path)
if (-not (Test-Path $Path)) {
return [pscustomobject]@{ path = $Path; exists = $false; values = @{} }
}
$item = Get-ItemProperty -Path $Path
$values = @{}
foreach ($prop in $item.PSObject.Properties) {
if ($prop.Name -notmatch "^PS") {
$values[$prop.Name] = $prop.Value
}
}
[pscustomobject]@{ path = $Path; exists = $true; values = $values }
}
$windowsUpdatePolicy = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
$auPolicy = Join-Path $windowsUpdatePolicy "AU"
$uxSettings = "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings"
$before = @(
Get-RegistrySnapshot $windowsUpdatePolicy
Get-RegistrySnapshot $auPolicy
Get-RegistrySnapshot $uxSettings
)
Write-PolicyLog "apply_start windows_update_no_surprise_reboot"
New-Item -Path $windowsUpdatePolicy -Force | Out-Null
New-Item -Path $auPolicy -Force | Out-Null
New-Item -Path $uxSettings -Force | Out-Null
# Keep Windows Update enabled, but prevent unattended surprise restarts.
New-ItemProperty -Path $auPolicy -Name "NoAutoRebootWithLoggedOnUsers" -Value 1 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $auPolicy -Name "AlwaysAutoRebootAtScheduledTime" -Value 0 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $auPolicy -Name "AUOptions" -Value 3 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $auPolicy -Name "ScheduledInstallDay" -Value 0 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $auPolicy -Name "ScheduledInstallTime" -Value 3 -PropertyType DWord -Force | Out-Null
# Make the interactive active-hours UI reflect an ops-safe window.
New-ItemProperty -Path $uxSettings -Name "ActiveHoursStart" -Value 0 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $uxSettings -Name "ActiveHoursEnd" -Value 23 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $uxSettings -Name "SmartActiveHoursState" -Value 0 -PropertyType DWord -Force | Out-Null
$after = @(
Get-RegistrySnapshot $windowsUpdatePolicy
Get-RegistrySnapshot $auPolicy
Get-RegistrySnapshot $uxSettings
)
$pendingRebootKeys = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired",
"HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager"
)
$pending = @()
foreach ($key in $pendingRebootKeys) {
if ($key -like "*Session Manager") {
$value = (Get-ItemProperty -Path $key -Name "PendingFileRenameOperations" -ErrorAction SilentlyContinue).PendingFileRenameOperations
$pending += [pscustomobject]@{ path = $key; signal = "PendingFileRenameOperations"; present = [bool]$value }
} else {
$pending += [pscustomobject]@{ path = $key; signal = "key_exists"; present = [bool](Test-Path $key) }
}
}
try {
gpupdate.exe /target:computer /force | Out-File -FilePath $logPath -Append -Encoding UTF8
$gpupdateOk = $true
} catch {
$gpupdateOk = $false
Write-PolicyLog "gpupdate_failed error=$($_.Exception.Message)"
}
$result = [pscustomobject]@{
timestamp = (Get-Date -Format o)
applied = $true
policy = "windows_update_no_surprise_reboot"
gpupdateOk = $gpupdateOk
before = $before
after = $after
pendingRebootSignals = $pending
evidenceLog = $logPath
}
$result | ConvertTo-Json -Depth 8 | Set-Content -Encoding UTF8 $evidencePath
Write-PolicyLog "apply_complete evidence=$evidencePath"
Write-Host $evidencePath