How to Set Up Windows Defender Application Control (WDAC) on Windows Server 2025

Windows Defender Application Control (WDAC) is a kernel-level code integrity enforcement mechanism built into Windows Server 2025 that controls which drivers and applications are permitted to run on a system. Unlike traditional application whitelisting solutions that operate in user space, WDAC enforces policies at the hypervisor and kernel level — making it significantly more resistant to bypass than previous-generation tools. On Windows Server 2025, WDAC replaces AppLocker as the recommended solution for kernel and driver control and integrates with Virtualization-Based Security (VBS) for maximum integrity. This guide covers the full WDAC deployment lifecycle: creating an allow-list policy by scanning the existing system, converting it to a deployable binary, deploying via Group Policy, configuring audit versus enforcement mode, and diagnosing blocked events in the Windows event log.

Prerequisites

  • Windows Server 2025 (Standard or Datacenter) — WDAC is not available on Windows Server Core without additional steps
  • Local Administrator or Domain Administrator privileges
  • PowerShell 5.1 or later with the ConfigCI module (included with Windows Server 2025)
  • A reference system or “gold image” server whose installed software represents the allowed baseline
  • Group Policy Management Console for fleet deployment
  • Understanding that WDAC policies affect all users on the system, including administrators
  • A tested rollback plan — enforcing an incorrect policy can render a system unbootable

Step 1: Understand WDAC Architecture

Before creating policies, it is important to understand how WDAC is structured. A WDAC policy is an XML document that specifies allowed signers (publishers), file paths, hashes, and file attributes. At boot time, the kernel reads the compiled binary version of this policy and enforces it for every code execution — including kernel-mode drivers, which AppLocker cannot control.

# Verify the ConfigCI module is available (required for WDAC policy creation)
Get-Module -Name ConfigCI -ListAvailable

# If not available, ensure the Windows Defender Application Control feature tools are present
Get-WindowsFeature | Where-Object { $_.Name -like "*Application*Control*" }

# Check current WDAC policy status on the system
Get-CIPolicy -FilePath "$env:SystemRootSystem32CodeIntegritySIPolicy.p7b" -ErrorAction SilentlyContinue

# Check if any policy is currently enforced
$cipolicies = Get-ChildItem "$env:SystemRootSystem32CodeIntegrityCiPoliciesActive" -ErrorAction SilentlyContinue
if ($cipolicies) {
    Write-Host "Active WDAC policies found:"
    $cipolicies | Select-Object Name, LastWriteTime
} else {
    Write-Host "No active WDAC policies — system is running without code integrity enforcement"
}

Step 2: Create a WDAC Policy from a Reference System

The most reliable way to create a WDAC allow-list is to scan a fully configured reference server — one that has all required software installed and is known to be clean. The New-CIPolicy cmdlet walks the file system and generates an XML policy containing rules for every discovered executable, driver, and script.

# Create a policy by scanning the entire system (this may take 10-30 minutes)
# Level: Publisher — prefer publisher/certificate rules over hash rules for maintainability
# Fallback: FilePublisher, then Hash — if publisher info is unavailable, fall back to file-level or hash rules

New-CIPolicy -FilePath "C:WDACPoliciesAllowList_Reference.xml" `
    -Level Publisher `
    -Fallback FilePublisher, Hash `
    -UserPEs `
    -MultiplePolicyFormat `
    -ScanPath "C:" `
    -Verbose

# Review the generated policy
[xml]$policy = Get-Content "C:WDACPoliciesAllowList_Reference.xml"
Write-Host "Total rules in policy: $($policy.SiPolicy.FileRules.ChildNodes.Count)"
Write-Host "Signer rules: $($policy.SiPolicy.Signers.ChildNodes.Count)"

The -Level Publisher option creates signer-based rules that remain valid as software is updated (as long as the signing certificate doesn’t change), reducing ongoing maintenance compared to hash-based rules. Use hash rules as a fallback for unsigned software only, as they must be regenerated every time a file changes.

Step 3: Merge and Refine the Policy

The initial scan-generated policy typically needs refinement. You may need to merge it with Microsoft’s recommended block rules (which deny known-vulnerable signed binaries) and your organization’s supplemental policies.

# Download Microsoft's recommended driver block list
# Source: https://learn.microsoft.com/en-us/windows/security/application-security/application-control/windows-defender-application-control/design/microsoft-recommended-driver-block-rules
# Save as C:WDACPoliciesMicrosoft_RecommendedBlockRules.xml

# Merge your allow-list with Microsoft's block rules
Merge-CIPolicy -PolicyPaths `
    "C:WDACPoliciesAllowList_Reference.xml", `
    "C:WDACPoliciesMicrosoft_RecommendedBlockRules.xml" `
    -OutputFilePath "C:WDACPoliciesMergedPolicy.xml"

# Set the policy to Audit Mode first (rule option 3 = enabled means audit mode)
Set-RuleOption -FilePath "C:WDACPoliciesMergedPolicy.xml" -Option 3

# Disable unsigned system integrity policy requirement (option 6) for initial deployment
Set-RuleOption -FilePath "C:WDACPoliciesMergedPolicy.xml" -Option 6

# Set policy name and version for tracking
Set-CIPolicyVersion -FilePath "C:WDACPoliciesMergedPolicy.xml" -Version "1.0.0.1"

# Review all current policy options
Get-CIPolicy -FilePath "C:WDACPoliciesMergedPolicy.xml" | Select-Object PolicyID, PolicyName, PolicyOptions

Step 4: Add Signer Rules for Specific Publishers

When specific commercial software or in-house tools need to be explicitly allowed by publisher certificate rather than relying on the scan-generated rules, use Add-SignerRule to grant permission based on the publisher’s digital signature.

# Extract signing certificate info from a trusted executable
$exe = "C:Program FilesYourAppYourApp.exe"
$sig = Get-AuthenticodeSignature -FilePath $exe

if ($sig.Status -eq "Valid") {
    Write-Host "Publisher: $($sig.SignerCertificate.Subject)"
    Write-Host "Thumbprint: $($sig.SignerCertificate.Thumbprint)"
} else {
    Write-Warning "File is unsigned or signature is invalid"
}

# Add a signer rule for all files signed by this publisher
# First, generate a catalog file or reference the executable directly
$certFile = "C:WDACPoliciesTrustedPublisher.cer"
$sig.SignerCertificate | Export-Certificate -FilePath $certFile -Type CERT

# Create a policy fragment for the publisher
$publisherPolicy = "C:WDACPoliciesPublisher_YourApp.xml"
New-CIPolicy -FilePath $publisherPolicy `
    -Level Publisher `
    -ScanPath "C:Program FilesYourApp" `
    -UserPEs

# Merge the publisher policy into the main policy
Merge-CIPolicy -PolicyPaths `
    "C:WDACPoliciesMergedPolicy.xml", `
    $publisherPolicy `
    -OutputFilePath "C:WDACPoliciesMergedPolicy.xml"

Step 5: Convert the XML Policy to Binary Format

Windows enforces the binary (.p7b) compiled version of the WDAC policy, not the XML source. The ConvertFrom-CIPolicy cmdlet performs this compilation. Always keep the XML source under version control — it cannot be easily reconstructed from the binary.

# Compile the XML policy to binary
$xmlPolicy = "C:WDACPoliciesMergedPolicy.xml"
$binaryPolicy = "C:WDACPoliciesMergedPolicy.bin"

ConvertFrom-CIPolicy -XmlFilePath $xmlPolicy -BinaryFilePath $binaryPolicy

# Verify the binary was created
if (Test-Path $binaryPolicy) {
    $file = Get-Item $binaryPolicy
    Write-Host "Binary policy created: $($file.FullName) ($($file.Length) bytes)"
} else {
    Write-Error "Binary policy creation failed"
}

# For Multiple Policy Format (Windows Server 2019+), the binary must be named
# using the PolicyID GUID from the XML
[xml]$policyXml = Get-Content $xmlPolicy
$policyId = $policyXml.SiPolicy.PolicyID.TrimStart("{").TrimEnd("}")
$guidBinary = "C:WDACPolicies{$policyId}.cip"
ConvertFrom-CIPolicy -XmlFilePath $xmlPolicy -BinaryFilePath $guidBinary
Write-Host "GUID-named binary: $guidBinary"

Step 6: Deploy the Policy in Audit Mode via Group Policy

Audit Mode logs blocked events without actually preventing execution. Always deploy in Audit Mode first and monitor for at least one full business cycle (one to two weeks) before switching to Enforcement Mode. This surfaces any legitimate software that your policy would incorrectly block.

# Option 1: Deploy locally for testing
$cipolicyDest = "$env:SystemRootSystem32CodeIntegrityCiPoliciesActive"
if (-not (Test-Path $cipolicyDest)) {
    New-Item -Path $cipolicyDest -ItemType Directory -Force
}
Copy-Item -Path $guidBinary -Destination $cipolicyDest -Force

# Option 2: Deploy via Group Policy (recommended for fleet)
# In GPMC:
# Computer Configuration → Windows Settings → Security Settings →
#   Application Control Policies → Windows Defender Application Control
# Create a new GPO and set the path to the .cip binary on a SYSVOL share

# Copy the binary to a SYSVOL share for GPO deployment
$sysvolShare = "\$env:USERDNSDOMAINSYSVOL$env:USERDNSDOMAINWDACPolicies"
New-Item -Path $sysvolShare -ItemType Directory -Force -ErrorAction SilentlyContinue
Copy-Item -Path $guidBinary -Destination $sysvolShare -Force

# Refresh policies on a test server to pick up the new GPO
Invoke-Command -ComputerName "SRV-TEST01" -ScriptBlock { gpupdate /force }

# A reboot is required for code integrity policy changes to take effect
Restart-Computer -ComputerName "SRV-TEST01" -Confirm

Step 7: Analyze Audit Mode Events

While running in Audit Mode, WDAC logs every would-be block to the Microsoft-Windows-CodeIntegrity/Operational event log. Event ID 3076 indicates a file that would be blocked in Enforcement Mode; Event ID 3089 provides signature information. Review these events carefully before switching to enforcement.

# Query WDAC audit events (Event ID 3076 = would-be block in audit mode)
Get-WinEvent -LogName "Microsoft-Windows-CodeIntegrity/Operational" |
    Where-Object { $_.Id -eq 3076 } |
    Select-Object TimeCreated, Message |
    Sort-Object TimeCreated -Descending |
    Format-Table -Wrap

# Extract file paths from audit events for policy refinement
Get-WinEvent -LogName "Microsoft-Windows-CodeIntegrity/Operational" |
    Where-Object { $_.Id -eq 3076 } |
    ForEach-Object {
        if ($_.Message -match "File Name:s+(.+)") {
            $Matches[1].Trim()
        }
    } | Sort-Object -Unique | Out-File "C:WDACPoliciesAuditBlockList.txt"

Write-Host "Audit block list saved. Review and add necessary files to policy before enforcement."

Step 8: Switch to Enforcement Mode

After validating that the audit logs contain no legitimate software being blocked, remove the Audit Mode rule option and redeploy the policy. From this point forward, any code not matching a policy rule will be denied execution.

# Remove Audit Only option (Option 3) to switch to Enforcement Mode
$xmlPolicy = "C:WDACPoliciesMergedPolicy.xml"
Set-RuleOption -FilePath $xmlPolicy -Option 3 -Delete

# Recompile to binary
[xml]$policyXml = Get-Content $xmlPolicy
$policyId = $policyXml.SiPolicy.PolicyID.TrimStart("{").TrimEnd("}")
$enforcedBinary = "C:WDACPoliciesEnforced_{$policyId}.cip"
ConvertFrom-CIPolicy -XmlFilePath $xmlPolicy -BinaryFilePath $enforcedBinary

# Deploy the enforcement binary (replace the audit binary)
Copy-Item -Path $enforcedBinary `
    -Destination "$env:SystemRootSystem32CodeIntegrityCiPoliciesActive{$policyId}.cip" `
    -Force

Write-Host "Enforcement policy deployed. Reboot required to activate."
# Restart-Computer

Conclusion

Windows Defender Application Control on Windows Server 2025 provides kernel-level code integrity enforcement that is fundamentally more robust than user-space application whitelisting. By generating allow-list policies from a trusted reference system, building publisher-based signer rules for maintainability, deploying in Audit Mode to identify gaps before enforcement, and routing event log data through systematic review, you can confidently move to full enforcement without disrupting legitimate workloads. WDAC is not a set-and-forget control — as new software is deployed, supplemental policies must be created and merged. Establish a change management process for policy updates, maintain all XML source files in version control, and always test policy changes in a staging environment before production deployment. When combined with BitLocker, Secure Boot, and Credential Guard, WDAC completes a comprehensive defense-in-depth posture for Windows Server 2025.