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