Public/Set-DefenderDcPolicy.ps1
|
function Set-DefenderDcPolicy { <# .SYNOPSIS Apply or remove the Defender Device Control read-only USB policy on an unmanaged Windows 11 device, using the canonical Local GPO registry surface (file-path REG_SZ + XML, per WindowsDefender.admx). .DESCRIPTION Writes 5 registry values under HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\... that point Defender at policy XMLs describing the device groups and per-class deny rules. By default, ships starter XMLs covering removable storage, WPD/MTP, and optical drives. Supply -GroupsXmlPath and -RulesXmlPath to deploy your own. Requires administrator elevation. Requires MDE attach for the engine to consume policy (registry writes succeed on any box; engine activation requires Microsoft Defender for Endpoint). .PARAMETER Mode Audit (log without block), Enforce (block + log), or Off (remove policy). .PARAMETER GroupsXmlPath Optional absolute path to a PolicyGroups.xml. Defaults to the shipped starter XML. .PARAMETER RulesXmlPath Optional absolute path to a PolicyRules.<Mode>.xml. Defaults to the shipped starter XML matching the selected -Mode. .PARAMETER SkipMpCmdRunValidation Skip the engine-side XML preflight via MpCmdRun.exe -DeviceControl -TestPolicyXml. Off by default - validation recommended. .PARAMETER SkipGpUpdate Skip the trailing gpupdate /force. gpupdate is the canonical trigger that makes Defender consume the policy. Skipping leaves the registry written but the engine may not pick up the policy until the next OS-driven refresh. .EXAMPLE Set-DefenderDcPolicy -Mode Audit -WhatIf Preview the planned registry writes without applying. .EXAMPLE Set-DefenderDcPolicy -Mode Enforce Apply policy in Enforce mode. .EXAMPLE Set-DefenderDcPolicy -Mode Enforce -GroupsXmlPath 'C:\MyPolicy\Groups.xml' -RulesXmlPath 'C:\MyPolicy\Rules.xml' Deploy a custom policy XML pair. .EXAMPLE Set-DefenderDcPolicy -Mode Off Remove the policy entirely. .LINK https://lukeevanstech.github.io/defender-device-control-unmanaged/ #> [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory)] [ValidateSet('Audit','Enforce','Off')] [string] $Mode, [string] $GroupsXmlPath, [string] $RulesXmlPath, [switch] $SkipMpCmdRunValidation, [switch] $SkipGpUpdate ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' if (-not (Test-IsElevated)) { throw "Set-DefenderDcPolicy: must be run elevated." } try { $defender = Get-DcComputerStatus } catch { throw "Set-DefenderDcPolicy: Get-MpComputerStatus failed: $($_.Exception.Message)" } if (-not $defender.AMServiceEnabled) { throw "Set-DefenderDcPolicy: Defender service not enabled." } Write-Verbose "Defender AM engine $($defender.AMEngineVersion), TamperProtection=$($defender.IsTamperProtected)" if ($Mode -eq 'Off') { Write-Verbose "Removing Device Control policy." Remove-DcPolicy if (-not $SkipGpUpdate -and $PSCmdlet.ShouldProcess('Group Policy', 'gpupdate /force')) { & gpupdate.exe /force 2>&1 | ForEach-Object { Write-Verbose " $_" } } if ($PSCmdlet.ShouldProcess('Defender engine', 'Update-MpSignature')) { Update-MpSignature -UpdateSource MMPC -ErrorAction SilentlyContinue } return } $defaultPolicyDir = Join-Path $PSScriptRoot '..\policy' if (-not $GroupsXmlPath) { $GroupsXmlPath = [System.IO.Path]::GetFullPath((Join-Path $defaultPolicyDir 'PolicyGroups.xml')) } if (-not $RulesXmlPath) { $RulesXmlPath = [System.IO.Path]::GetFullPath((Join-Path $defaultPolicyDir "PolicyRules.$Mode.xml")) } if (-not [System.IO.Path]::IsPathRooted($GroupsXmlPath)) { $GroupsXmlPath = [System.IO.Path]::GetFullPath($GroupsXmlPath) } if (-not [System.IO.Path]::IsPathRooted($RulesXmlPath)) { $RulesXmlPath = [System.IO.Path]::GetFullPath($RulesXmlPath) } foreach ($p in $GroupsXmlPath, $RulesXmlPath) { if (-not (Test-Path -LiteralPath $p -PathType Leaf)) { throw "Set-DefenderDcPolicy: required policy file not found: $p" } } Write-Verbose "Groups: $GroupsXmlPath" Write-Verbose "Rules: $RulesXmlPath" Read-DcPolicyXml -Path $GroupsXmlPath | Out-Null Read-DcPolicyXml -Path $RulesXmlPath | Out-Null if (-not $SkipMpCmdRunValidation) { Test-DcXmlWithMpCmdRun -XmlPath $GroupsXmlPath -Kind Groups Test-DcXmlWithMpCmdRun -XmlPath $RulesXmlPath -Kind Rules } $manifest = Get-DcRegistryManifest -GroupsXmlPath $GroupsXmlPath -RulesXmlPath $RulesXmlPath Write-Verbose "Removing any prior Device Control policy state before re-applying." Remove-DcPolicy Invoke-DcRegistryWrites -Manifest $manifest if (-not $SkipGpUpdate -and $PSCmdlet.ShouldProcess('Group Policy', 'gpupdate /force')) { & gpupdate.exe /force 2>&1 | ForEach-Object { Write-Verbose " $_" } } if ($PSCmdlet.ShouldProcess('Defender engine', 'Update-MpSignature')) { Update-MpSignature -UpdateSource MMPC -ErrorAction SilentlyContinue } } |