Public/Test-DefenderDcPolicy.ps1

function Test-DefenderDcPolicy {
<#
.SYNOPSIS
    Verify the Defender Device Control policy state on this machine.
 
.DESCRIPTION
    Reads registry + Defender engine state, prints PASS/FAIL per check,
    returns boolean (true if all checks passed). Prints a dynamic-test
    recipe the operator can drive with a real USB stick.
 
.PARAMETER ExpectMode
    Audit, Enforce, or Off. The state we expect to find. Default Audit.
 
.EXAMPLE
    Test-DefenderDcPolicy -ExpectMode Audit
 
    Verify policy is in Audit mode and the engine has consumed it.
 
.EXAMPLE
    Test-DefenderDcPolicy -ExpectMode Off
 
    Verify the policy has been fully removed.
 
.LINK
    https://lukeevanstech.github.io/defender-device-control-unmanaged/
#>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [ValidateSet('Audit','Enforce','Off')]
        [string] $ExpectMode = 'Audit'
    )

    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'

    $script:dcFailures = 0

    function Assert-DcCondition {
        param([string]$Name, [scriptblock]$Test, [string]$ExpectedMsg)
        try {
            if (& $Test) { Write-Host " PASS $Name" -ForegroundColor Green }
            else { Write-Host " FAIL $Name ($ExpectedMsg)" -ForegroundColor Red; $script:dcFailures++ }
        } catch {
            Write-Host " FAIL $Name (threw: $($_.Exception.Message))" -ForegroundColor Red
            $script:dcFailures++
        }
    }

    Write-Host "Defender Device Control verification (ExpectMode=$ExpectMode)" -ForegroundColor Yellow

    $status = Get-DcComputerStatus
    Assert-DcCondition 'Defender AM service enabled' { $status.AMServiceEnabled } 'AMServiceEnabled must be True'
    Assert-DcCondition 'Defender antivirus enabled'  { $status.AntivirusEnabled } 'AntivirusEnabled must be True'

    if ($ExpectMode -eq 'Off') {
        Assert-DcCondition 'Device Control root key absent' {
            -not (Test-Path -LiteralPath $script:DcRoot)
        } "$script:DcRoot should not exist"

        $featProp = Get-ItemProperty -LiteralPath $script:DcFeatures -Name DeviceControlEnabled -ErrorAction SilentlyContinue
        Assert-DcCondition 'Features\DeviceControlEnabled absent or 0' {
            $null -eq $featProp -or $featProp.DeviceControlEnabled -eq 0
        } 'Features\DeviceControlEnabled must be 0 or absent'

        Assert-DcCondition 'Engine reports DeviceControlState=Disabled' {
            $prop = $status.PSObject.Properties['DeviceControlState']
            $null -eq $prop -or $prop.Value -eq 'Disabled' -or $prop.Value -eq 0
        } 'engine view should reflect that DC is disabled'

    } else {

        Assert-DcCondition 'Features\DeviceControlEnabled = 1' {
            $p = Get-ItemProperty -LiteralPath $script:DcFeatures -Name DeviceControlEnabled -ErrorAction SilentlyContinue
            $null -ne $p -and $p.DeviceControlEnabled -eq 1
        } 'must be 1'

        Assert-DcCondition 'Device Control\DefaultEnforcement = 1 (Allow)' {
            $p = Get-ItemProperty -LiteralPath $script:DcRoot -Name DefaultEnforcement -ErrorAction SilentlyContinue
            $null -ne $p -and $p.DefaultEnforcement -eq 1
        } 'must be 1'

        Assert-DcCondition 'SecuredDevicesConfiguration scopes Removable + CdRom + Wpd' {
            $p = Get-ItemProperty -LiteralPath $script:DcRoot -Name SecuredDevicesConfiguration -ErrorAction SilentlyContinue
            $null -ne $p -and $p.SecuredDevicesConfiguration -eq 'RemovableMediaDevices|CdRomDevices|WpdDevices'
        } 'must be exactly "RemovableMediaDevices|CdRomDevices|WpdDevices"'

        $script:groupsRegVal = $null
        Assert-DcCondition 'Policy Groups\PolicyGroups REG_SZ exists' {
            $p = Get-ItemProperty -LiteralPath $script:DcGroupsKey -Name PolicyGroups -ErrorAction SilentlyContinue
            $script:groupsRegVal = if ($null -ne $p) { $p.PolicyGroups } else { $null }
            $null -ne $script:groupsRegVal -and $script:groupsRegVal -ne ''
        } 'must be a non-empty string'

        Assert-DcCondition 'Policy Groups XML file exists at registered path' {
            $null -ne $script:groupsRegVal -and (Test-Path -LiteralPath $script:groupsRegVal -PathType Leaf)
        } 'file at the registered path must exist'

        Assert-DcCondition 'Policy Groups XML parses as 3 Group records' {
            if ($null -eq $script:groupsRegVal -or -not (Test-Path -LiteralPath $script:groupsRegVal -PathType Leaf)) { return $false }
            @(Read-DcPolicyXml -Path $script:groupsRegVal).Count -eq 3
        } 'must contain exactly 3 <Group> elements'

        $script:rulesRegVal = $null
        Assert-DcCondition 'Policy Rules\PolicyRules REG_SZ exists' {
            $p = Get-ItemProperty -LiteralPath $script:DcRulesKey -Name PolicyRules -ErrorAction SilentlyContinue
            $script:rulesRegVal = if ($null -ne $p) { $p.PolicyRules } else { $null }
            $null -ne $script:rulesRegVal -and $script:rulesRegVal -ne ''
        } 'must be a non-empty string'

        Assert-DcCondition 'Policy Rules XML file exists at registered path' {
            $null -ne $script:rulesRegVal -and (Test-Path -LiteralPath $script:rulesRegVal -PathType Leaf)
        } 'file at the registered path must exist'

        Assert-DcCondition 'Policy Rules XML parses as 3 PolicyRule records' {
            if ($null -eq $script:rulesRegVal -or -not (Test-Path -LiteralPath $script:rulesRegVal -PathType Leaf)) { return $false }
            @(Read-DcPolicyXml -Path $script:rulesRegVal).Count -eq 3
        } 'must contain exactly 3 <PolicyRule> elements'

        $expectedType = if ($ExpectMode -eq 'Audit') { 'AuditAllowed' } else { 'Deny' }
        Assert-DcCondition "Rules XML uses Entry Type=$expectedType (matches ExpectMode)" {
            if ($null -eq $script:rulesRegVal -or -not (Test-Path -LiteralPath $script:rulesRegVal -PathType Leaf)) { return $false }
            $items = Read-DcPolicyXml -Path $script:rulesRegVal
            foreach ($r in $items) {
                $types = @(([xml]$r.RawXml).PolicyRule.Entry.Type)
                if ($types -notcontains $expectedType) { return $false }
            }
            $true
        } "Mode $ExpectMode requires at least one $expectedType entry per rule"

        Assert-DcCondition 'Engine reports DeviceControlState != Disabled' {
            $prop = $status.PSObject.Properties['DeviceControlState']
            $null -ne $prop -and $prop.Value -ne 'Disabled' -and $prop.Value -ne 0
        } 'Get-MpComputerStatus.DeviceControlState must reflect engine consumed policy'

        Assert-DcCondition 'Engine reports DeviceControlPoliciesLastUpdated is recent (not 1601 sentinel)' {
            $prop = $status.PSObject.Properties['DeviceControlPoliciesLastUpdated']
            if ($null -eq $prop) { return $false }
            $v = $prop.Value
            if ($null -eq $v) { return $false }
            try { $dt = [datetime]$v } catch { return $false }
            $dt.Year -ge 2000
        } '1601-01-01 sentinel means engine never read the policy'
    }

    Write-Host ""
    if ($script:dcFailures -eq 0) {
        Write-Host "Static checks: ALL PASSED" -ForegroundColor Green
    } else {
        Write-Host "Static checks: $($script:dcFailures) FAILED" -ForegroundColor Red
    }

    Write-Host ""
    Write-Host "Dynamic verification (operator drives, USB stick required):" -ForegroundColor Yellow
    Write-Host " 1. Plug in a USB stick. It should mount and get a drive letter."
    Write-Host " 2. Open a file from the stick (Read should always work)."
    Write-Host " 3. Try to copy a file TO the stick."
    Write-Host " Audit mode: succeeds, Defender XDR Advanced Hunting records the event."
    Write-Host " Enforce mode: fails with 'The media is write protected', toast shows."

    return ($script:dcFailures -eq 0)
}