67 lines
3.7 KiB
PowerShell
67 lines
3.7 KiB
PowerShell
param([ValidateSet('install','audit','uninstall')][string]$Action = 'audit')
|
|
$ErrorActionPreference = 'Stop'
|
|
$stateDir = Join-Path $env:ProgramData 'Comitari\HardeningSafe'
|
|
$stateFile = Join-Path $stateDir 'original-values.json'
|
|
# Only low-impact, machine-wide controls. Deliberately excludes Windows LAPS, accounts,
|
|
# services, remote management, networking, firewall policy, BitLocker and Defender.
|
|
$settings = @(
|
|
@{ Path='HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer'; Name='NoAutorun'; Value=1 },
|
|
@{ Path='HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer'; Name='NoDriveTypeAutoRun'; Value=255 },
|
|
@{ Path='HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer'; Name='NoAutoplayfornonVolume'; Value=1 },
|
|
@{ Path='HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization'; Name='NoLockScreenCamera'; Value=1 },
|
|
@{ Path='HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization'; Name='NoLockScreenSlideshow'; Value=1 }
|
|
)
|
|
function Read-Value($path, $name) {
|
|
if (-not (Test-Path -LiteralPath $path)) { return @{ Exists=$false; Value=$null } }
|
|
$key = Get-Item -LiteralPath $path
|
|
if ($key.GetValueNames() -contains $name) {
|
|
return @{ Exists=$true; Value=[int]$key.GetValue($name) }
|
|
}
|
|
return @{ Exists=$false; Value=$null }
|
|
}
|
|
function Write-Value($path, $name, [int]$value) {
|
|
if (-not (Test-Path -LiteralPath $path)) { New-Item -Path $path -Force | Out-Null }
|
|
New-ItemProperty -LiteralPath $path -Name $name -Value $value -PropertyType DWord -Force | Out-Null
|
|
}
|
|
if ($Action -eq 'install') {
|
|
if (-not (Test-Path -LiteralPath $stateDir)) { New-Item -Path $stateDir -ItemType Directory -Force | Out-Null }
|
|
if (-not (Test-Path -LiteralPath $stateFile)) {
|
|
$original = @()
|
|
foreach ($setting in $settings) {
|
|
$old = Read-Value $setting.Path $setting.Name
|
|
$original += [pscustomobject]@{ Path=$setting.Path; Name=$setting.Name; Exists=$old.Exists; Value=$old.Value; Target=$setting.Value }
|
|
}
|
|
$json = ConvertTo-Json -InputObject @($original) -Depth 5
|
|
$temp = "$stateFile.tmp"
|
|
[System.IO.File]::WriteAllText($temp, $json, [System.Text.UTF8Encoding]::new($false))
|
|
Move-Item -LiteralPath $temp -Destination $stateFile -Force
|
|
}
|
|
foreach ($setting in $settings) { Write-Value $setting.Path $setting.Name $setting.Value }
|
|
Write-Output 'Installed conservative hardening; LapsByWAPT untouched.'
|
|
}
|
|
elseif ($Action -eq 'audit') {
|
|
$failed = @()
|
|
foreach ($setting in $settings) {
|
|
$actual = Read-Value $setting.Path $setting.Name
|
|
if (-not $actual.Exists -or $actual.Value -ne $setting.Value) { $failed += "$($setting.Name) != $($setting.Value)" }
|
|
}
|
|
if ($failed.Count -gt 0) { $failed | ForEach-Object { Write-Output $_ }; exit 1 }
|
|
Write-Output 'All 5 settings compliant.'
|
|
}
|
|
else {
|
|
if (-not (Test-Path -LiteralPath $stateFile)) { throw 'No saved baseline; refusing blind rollback.' }
|
|
$original = @(Get-Content -LiteralPath $stateFile -Raw | ConvertFrom-Json)
|
|
foreach ($item in $original) {
|
|
$current = Read-Value $item.Path $item.Name
|
|
# Do not override later changes made by GPO or another administrator.
|
|
if (-not $current.Exists -or $current.Value -ne [int]$item.Target) {
|
|
Write-Output "Skipped externally changed setting: $($item.Name)"
|
|
continue
|
|
}
|
|
if ($item.Exists) { Write-Value $item.Path $item.Name ([int]$item.Value) }
|
|
else { Remove-ItemProperty -LiteralPath $item.Path -Name $item.Name -ErrorAction Stop }
|
|
}
|
|
# Keep baseline for forensic trace; uninstall is idempotent only for settings still matching targets.
|
|
Write-Output 'Conditional rollback completed; LapsByWAPT untouched.'
|
|
}
|