Damien Coles f172d00514 Initial release: Declarative AD Framework v2.1.0
Infrastructure-as-code framework for Active Directory objects and Group Policy.
Sanitized from production deployment for public sharing.
2026-02-19 17:02:42 +00:00

182 lines
6.7 KiB
PowerShell

# GPOWdac.ps1
# Lightweight WDAC (Windows Defender Application Control) policy deployment via GPO.
# Copies .p7b policy to SYSVOL and sets the registry pointer.
# Depends on: GPOCore.ps1 (Get-GPOSysvolPath)
function Set-GPOWdacPolicy {
<#
.SYNOPSIS
Deploys a WDAC policy file to a GPO.
If source is .xml, converts to .p7b with ConvertFrom-CIPolicy.
Copies .p7b to SYSVOL and sets the DeployConfigCIPolicy registry key.
#>
param(
[Parameter(Mandatory)]
[string]$GPOName,
[Parameter(Mandatory)]
[hashtable]$WDACPolicy,
[Parameter(Mandatory)]
[string]$SourceDir,
[string]$Domain = (Get-ADDomain).DNSRoot
)
Write-Host " Applying WDAC policy..." -ForegroundColor Yellow
$policyFile = $WDACPolicy.PolicyFile
$sourcePath = Join-Path $SourceDir $policyFile
if (-not (Test-Path $sourcePath)) {
throw "WDAC policy file not found: $sourcePath"
}
$sysvolPath = Get-GPOSysvolPath -GPOName $GPOName -Domain $Domain
$deviceGuardDir = Join-Path $sysvolPath 'Machine\Microsoft\Windows\DeviceGuard'
if (-not (Test-Path $deviceGuardDir)) {
New-Item -ItemType Directory -Path $deviceGuardDir -Force | Out-Null
}
$extension = [System.IO.Path]::GetExtension($sourcePath).ToLower()
if ($extension -eq '.xml') {
# Convert XML to .p7b
$destPath = Join-Path $deviceGuardDir 'SIPolicy.p7b'
ConvertFrom-CIPolicy -XmlFilePath $sourcePath -BinaryFilePath $destPath | Out-Null
Write-Host " Converted $policyFile -> SIPolicy.p7b" -ForegroundColor Green
} elseif ($extension -eq '.p7b') {
# Copy directly
$destPath = Join-Path $deviceGuardDir 'SIPolicy.p7b'
Copy-Item -Path $sourcePath -Destination $destPath -Force
Write-Host " Copied $policyFile -> SIPolicy.p7b" -ForegroundColor Green
} else {
throw "WDAC policy file must be .xml or .p7b, got: $extension"
}
# Set registry key to enable WDAC via GPO
$regKey = 'HKLM\SOFTWARE\Policies\Microsoft\Windows\DeviceGuard'
Set-GPRegistryValue -Name $GPOName -Key $regKey -ValueName 'DeployConfigCIPolicy' `
-Type DWord -Value 1 -Domain $Domain | Out-Null
Write-Host " [OK] WDAC policy deployed to $GPOName" -ForegroundColor Green
}
function Compare-GPOWdacPolicy {
<#
.SYNOPSIS
Compares desired WDAC policy against current GPO state.
Checks registry key and .p7b file existence/hash.
#>
param(
[Parameter(Mandatory)]
[string]$GPOName,
[Parameter(Mandatory)]
[hashtable]$WDACPolicy,
[Parameter(Mandatory)]
[string]$SourceDir,
[string]$Domain = (Get-ADDomain).DNSRoot
)
$diffs = @()
Write-Host " Comparing WDAC policy..." -ForegroundColor Yellow
# Check registry key
$regKey = 'HKLM\SOFTWARE\Policies\Microsoft\Windows\DeviceGuard'
$currentRegValue = $null
try {
$regResult = Get-GPRegistryValue -Name $GPOName -Key $regKey -ValueName 'DeployConfigCIPolicy' `
-Domain $Domain -ErrorAction Stop
$currentRegValue = $regResult.Value
} catch {
$currentRegValue = $null
}
if ($currentRegValue -ne 1) {
$display = if ($null -eq $currentRegValue) { '(not set)' } else { $currentRegValue }
Write-Host " [DRIFT] DeployConfigCIPolicy: $display -> 1" -ForegroundColor Red
$diffs += [PSCustomObject]@{
Type = 'WDAC'
Setting = 'DeployConfigCIPolicy'
Status = "Registry: $display -> 1"
}
} else {
Write-Host " [OK] DeployConfigCIPolicy = 1" -ForegroundColor Green
}
# Check .p7b file exists in SYSVOL
$sysvolPath = Get-GPOSysvolPath -GPOName $GPOName -Domain $Domain
$p7bPath = Join-Path $sysvolPath 'Machine\Microsoft\Windows\DeviceGuard\SIPolicy.p7b'
if (-not (Test-Path $p7bPath)) {
Write-Host " [DRIFT] Missing: SIPolicy.p7b" -ForegroundColor Red
$diffs += [PSCustomObject]@{
Type = 'WDAC'
Setting = 'SIPolicy.p7b'
Status = 'Missing'
}
} else {
# Compare file hashes if source exists
$policyFile = $WDACPolicy.PolicyFile
$sourcePath = Join-Path $SourceDir $policyFile
if (Test-Path $sourcePath) {
$sourceExt = [System.IO.Path]::GetExtension($sourcePath).ToLower()
if ($sourceExt -eq '.p7b') {
$sourceHash = (Get-FileHash -Path $sourcePath -Algorithm SHA256).Hash
$deployedHash = (Get-FileHash -Path $p7bPath -Algorithm SHA256).Hash
if ($sourceHash -ne $deployedHash) {
Write-Host " [DRIFT] SIPolicy.p7b hash mismatch (source updated)" -ForegroundColor Red
$diffs += [PSCustomObject]@{
Type = 'WDAC'
Setting = 'SIPolicy.p7b'
Status = 'Hash mismatch'
}
} else {
Write-Host " [OK] SIPolicy.p7b hash matches" -ForegroundColor Green
}
} else {
# Source is .xml -- convert to temp .p7b and compare
$tempP7b = [System.IO.Path]::GetTempFileName()
try {
ConvertFrom-CIPolicy -XmlFilePath $sourcePath -BinaryFilePath $tempP7b | Out-Null
$sourceHash = (Get-FileHash -Path $tempP7b -Algorithm SHA256).Hash
$deployedHash = (Get-FileHash -Path $p7bPath -Algorithm SHA256).Hash
if ($sourceHash -ne $deployedHash) {
Write-Host " [DRIFT] SIPolicy.p7b hash mismatch (source XML updated)" -ForegroundColor Red
$diffs += [PSCustomObject]@{
Type = 'WDAC'
Setting = 'SIPolicy.p7b'
Status = 'Hash mismatch'
}
} else {
Write-Host " [OK] SIPolicy.p7b matches source XML" -ForegroundColor Green
}
} catch {
Write-Host " [WARN] Cannot convert source XML for comparison: $($_.Exception.Message)" -ForegroundColor Yellow
} finally {
Remove-Item $tempP7b -Force -ErrorAction SilentlyContinue
}
}
} else {
Write-Host " [OK] SIPolicy.p7b exists (source file not found for hash comparison)" -ForegroundColor Green
}
}
if ($diffs.Count -eq 0) {
Write-Host " [OK] WDAC policy matches desired state" -ForegroundColor Green
} else {
Write-Host " [DRIFT] $($diffs.Count) WDAC difference(s) found" -ForegroundColor Red
}
return $diffs
}