Detect When Windows Defender Is Disabled (PowerShell) · CloudEngineerHub
Detection & Remediation8 min read4 views
Detecting When Windows Defender Is Disabled with PowerShell
Catch devices where Windows Defender Antivirus is silently off: engine state, WinDefend service health, and DisableAntiSpyware policy tampering, via Intune.
C
CloudEngineerHub Team
Published: September 16, 2026· Last Updated: September 28, 2026·8 min read
Antivirus that silently stopped protecting is worse than antivirus that was never there,
because everyone behaves as if the protection exists. On managed Windows devices, Defender
gets switched off in ways no single report shows: a broken third-party AV migration that
left nothing behind, a hardening script that set DisableAntiSpyware and moved on, a
service-tuning tool that disabled the WinDefend service because it was "unused". The
device keeps syncing policy and passing every check that does not actually look at the
engine, and the gap only surfaces when something lands on the disk.
Visual Walkthrough
C
CloudEngineerHub Team
Author
The CloudEngineerHub editorial team — Microsoft endpoint management and cloud engineers writing hands-on guides on Intune, Azure, Entra ID, Autopilot, Defender and PowerShell. Every article is reviewed against current Microsoft documentation.
Exit 1 flips the device into the remediation queue for this package.
remediate
3
Remediation executes
Fix script runs silently as SYSTEM with a 5-minute budget.
verify
4
Re-run detection
The same detection script must now exit 0 — proof, not hope.
telemetry
5
Results report
Per-device outcome lands in Reports → Remediations for trend analysis.
6
Repeat on schedule
The loop continues — drift gets caught the day it appears.
Continuous cycle
Proactive Remediations — The Detect → Fix → Verify Loop. 1. Detection script runs: Scheduled (daily/hourly): exit 0 healthy, exit 1 unhealthy — nothing more. — 2. Verdict: unhealthy: Exit 1 flips the device into the remediation queue for this package. — 3. Remediation executes: Fix script runs silently as SYSTEM with a 5-minute budget. — 4. Re-run detection: The same detection script must now exit 0 — proof, not hope. — 5. Results report: Per-device outcome lands in Reports → Remediations for trend analysis. — 6. Repeat on schedule: The loop continues — drift gets caught the day it appears.
This article provides a detection-only script that establishes the ground truth on each
device: whether the Defender engine is running, whether real-time protection and
antispyware are enabled, whether the WinDefend service is present and healthy, and
whether the DisableAntiSpyware policy value has been set under
HKLM:\SOFTWARE\Microsoft\Windows Defender. It also handles the case people get wrong:
on devices where a third-party antivirus is registered, Defender running passive is
expected and correct, and the script treats it as compliant instead of raising false
alarms.
The package changes nothing. Its job is to give you a truthful per-device answer and a
work queue.
Symptoms
Devices report non-compliant on antivirus or antimalware checks without an obvious cause.
The security console shows a subset of devices stopped reporting from Defender.
In the Windows Security app, real-time protection toggles read off, or are greyed out.
Get-MpComputerStatus shows AMRunning: False or RealTimeProtectionEnabled: False on devices that were never migrated off Defender.
A hardening or "debloat" script ran on the device at some point, and nobody knows what it changed.
Possible Causes
A broken third-party AV migration
Uninstalling a third-party suite is supposed to return Defender to active duty
automatically. Interrupted uninstalls, leftover filter drivers, or a suite that only
partially removed itself can leave Defender passive with nothing else actually
protecting the device.
Legacy hardening policy values
The DisableAntiSpyware value under HKLM:\SOFTWARE\Microsoft\Windows Defender is a
real, historically used knob for turning antispyware protection off. Scripts copied
between organizations for a decade carry it. On Windows 10 2004 and newer, Tamper
Protection guards this key and neutralizes attempts to use it, which changes what the
value can tell you, discussed honestly below.
The WinDefend service was disabled
Service-hardening scripts and tools that disable "unnecessary" services sometimes list
WinDefend. A disabled start type survives reboots and policy cycles, and the engine
cannot run.
The Defender platform itself is damaged
Missing or corrupted platform binaries mean the cmdlets may still be registered but the
engine cannot start. This shows up as services present, settings true, and AMRunning
false.
Tamper Protection is off
With Tamper Protection disabled, local administrators and malware can flip real-time
protection and related settings off without resistance. Its absence is a precondition for
most of the other causes on this list.
Detection Script
The script is read-only, completes in a few seconds, and runs in 64-bit SYSTEM context.
It queries the Defender platform, the service control manager view of WinDefend, the
SecurityCenter2 registration list, and one registry value, then aggregates any issues
into a single exit code.
PowerShell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#Requires -RunAsAdministrator
[CmdletBinding()]
param()
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
<#
.SYNOPSIS
Detection script: reports whether Microsoft Defender Antivirus is really
running, enabled by policy, and backed by a healthy WinDefend service.
Strictly read-only.
.NOTES
Intended for Intune Scripts and Remediations, 64-bit SYSTEM context.
Exit 0 = Defender active and enabled, OR passively inactive because a
third-party antivirus is registered (expected coexistence).
Exit 1 = Defender disabled, stopped, or policy-disabled with no
third-party antivirus registered - the device has no AV.
Exit 2 = Defender platform cmdlets unavailable on this device.
Exit 3 = unexpected error.
#>$logDir = 'C:\ProgramData\Company\Logs'$logFile = Join-Path $logDir'DefenderDisabledDetection.log'function Write-DetectionLog {
param([Parameter(Mandatory)][string] $Level, [Parameter(Mandatory)][string] $Message)
if (-not (Test-Path $logDir)) { New-Item -Path$logDir -ItemType Directory -Force | Out-Null }
"$([DateTime]::UtcNow.ToString('o')) [$Level] $Message" | Out-File -FilePath$logFile -Append -Encoding utf8
}
try {
Write-DetectionLog 'INFO''Defender disabled detection run started.' # ---- Gate: Defender platform cmdlets must exist ----if (-not (Get-Command -Name'Get-MpComputerStatus' -ErrorAction SilentlyContinue)) {
Write-DetectionLog 'WARN''Get-MpComputerStatus is unavailable - the Defender platform is missing or this SKU does not include it.'
exit 2
}
# ---- Engine state from the platform itself ----$mp = Get-MpComputerStatus
Write-DetectionLog 'INFO' ("Platform: AMRunning={0} AMServiceEnabled={1} AntispywareEnabled={2} AntivirusEnabled={3} RealTimeProtectionEnabled={4}" -f$mp.AMRunning, $mp.AMServiceEnabled, $mp.AntispywareEnabled, $mp.AntivirusEnabled, $mp.RealTimeProtectionEnabled)
# ---- Third-party AV registration (client SKUs) ---- # Defender goes passive (engine idle) when another registered AV product # takes over. That coexistence is healthy and must not be flagged.$thirdPartyAv = $falsetry {
$avProducts = @(Get-CimInstance -Namespace'root/SecurityCenter2' -ClassName'AntiVirusProduct' -ErrorAction Stop)
foreach ($avin$avProducts) {
if ($av.displayName -and ($av.displayName -notmatch'Windows') -and ($av.displayName -notmatch'Defender')) {
$thirdPartyAv = $true
Write-DetectionLog 'INFO'"Registered third-party antivirus: $($av.displayName)"
}
}
}
catch {
Write-DetectionLog 'INFO''SecurityCenter2 namespace unavailable - third-party AV check skipped (typical on Windows Server).'
}
# ---- WinDefend service state ----$service = Get-CimInstance -ClassName Win32_Service -Filter"Name='WinDefend'"if ($null -eq$service) {
if ($thirdPartyAv) {
Write-DetectionLog 'WARN''WinDefend service is absent but a third-party antivirus is registered - AV coverage exists; treated as compliant for this check.'
exit 0
}
Write-DetectionLog 'ERROR''WinDefend service is not present and no third-party antivirus is registered. The device has no antivirus coverage.'
exit 1
}
Write-DetectionLog 'INFO'"WinDefend service: Status=$($service.Status) StartMode=$($service.StartMode)" # ---- Policy value DisableAntiSpyware ---- # HONESTY NOTE: on Windows 10 2004+ Tamper Protection guards this key and # neutralizes attempts to set it; the value may therefore not reflect the # device's actual behavior. It is still informative on older builds and # as evidence of tampering attempts.$defenderKey = 'HKLM:\SOFTWARE\Microsoft\Windows Defender'$disableAntiSpyware = $nullif (Test-Path $defenderKey) {
$keyProps = Get-ItemProperty -Path$defenderKey -Name'DisableAntiSpyware' -ErrorAction SilentlyContinue
if ($null -ne$keyProps) { $disableAntiSpyware = $keyProps.DisableAntiSpyware }
}
Write-DetectionLog 'INFO'"DisableAntiSpyware policy value: $disableAntiSpyware (null means not set)" # ---- Passive-mode decision ----if ($thirdPartyAv) {
Write-DetectionLog 'WARN''A third-party antivirus is registered, so Defender running passive or idle is expected coexistence behavior. Treated as compliant by this script.'
exit 0
}
# ---- Issue aggregation (no third-party AV: Defender must be active) ----$issues = @()
if ($service.StartMode -eq'Disabled') { $issues += 'WinDefend service start type is Disabled' }
if ($service.Status -ne'Running') { $issues += "WinDefend service is $($service.Status), expected Running" }
if (-not $mp.AMRunning) { $issues += 'AMRunning=False - the antimalware engine is not running' }
if (-not $mp.AntispywareEnabled) { $issues += 'AntispywareEnabled=False' }
if (-not $mp.RealTimeProtectionEnabled) { $issues += 'RealTimeProtectionEnabled=False' }
if ($disableAntiSpyware -eq1) { $issues += 'DisableAntiSpyware=1 under HKLM:\SOFTWARE\Microsoft\Windows Defender (antispyware disabled via policy value)' }
if ($issues.Count -gt0) {
foreach ($issuein$issues) { Write-DetectionLog 'ERROR'"Issue: $issue" }
exit 1
}
Write-DetectionLog 'INFO''Microsoft Defender Antivirus is active and enabled.'
exit 0
}
catch {
Write-DetectionLog 'ERROR'"Unexpected failure: $($_.Exception.Message)"
exit 3
}
finally {
Write-DetectionLog 'INFO''Defender disabled detection run finished.'
}
Detection Logic
Platform availability. If Get-MpComputerStatus does not exist, the Defender
platform is missing and the script exits 2. There is nothing to evaluate.
Engine state.Get-MpComputerStatus supplies AMRunning,
AMServiceEnabled, AntispywareEnabled, AntivirusEnabled, and
RealTimeProtectionEnabled. A healthy active Defender reads true across the board;
every value is logged either way so partial states are visible in the log.
Third-party registration. The script enumerates
root/SecurityCenter2AntiVirusProduct instances (client SKUs; the namespace is
absent on Server, which the script handles). Any non-Microsoft product sets a flag.
This is the check that separates "Defender passive because something else protects
the device" from "Defender off and nothing replaced it".
Service state.Win32_Service gives Status and StartMode for WinDefend.
Disabled start mode or anything other than Running is an issue, unless the
third-party flag says passive mode is expected.
Policy value. The script reads DisableAntiSpyware from
HKLM:\SOFTWARE\Microsoft\Windows Defender. A value of 1 is recorded as an issue.
The honest caveat, also written into the log: on Windows 10 2004 and later, Tamper
Protection protects this key, so a set value is evidence of a tampering attempt while
an unset value does not guarantee anything on those builds. Treat the value as
authoritative mainly on older builds, and as forensic context on new ones.
Aggregation. Without a registered third-party AV, any failed check produces exit
1 with an ERROR line per issue. With a third-party AV present, the script logs the
passive-mode expectation and exits 0.
Remediation Script
This topic intentionally ships detection-only. Re-enabling antivirus on a device where
something else disabled it is not a decision a script should make blindly: the right fix
depends on why it was off, and the fix often belongs to policy rather than to the local
machine. Intune endpoint security antivirus policy is the authoritative channel for
Defender settings; for drift repair of specific preferences there is a companion package
in How to Remediate Windows Defender Configuration Drift with PowerShell.
Publish this package with No remediation script and use the results as your work
queue.
Deploying Through Intune
In the Microsoft Intune admin center, go to Devices → Scripts and remediations and select + Add under Windows 10/11.
On Basics, name it Detect - Windows Defender disabled and describe it plainly: "read-only; reports Defender engine, service, and policy state; changes nothing".
On Detection script, upload detect.ps1. Set Run script in 64-bit PowerShell: Yes and Run this script using signed-on credentials: No so it runs as SYSTEM with the rights the platform queries need.
On Remediation script, choose No remediation script.
Leave Enforce script signature check: No unless your organization signs internal scripts.
On Schedule, choose Daily. Antivirus state is a security posture question, and daily detection turns a silent gap into a one-day-old finding.
On Assignments, start with a pilot ring, then your full Windows client group. If parts of your estate deliberately run third-party AV, keep them in scope: the script reports passive mode as compliant and leaves a log line you can audit.
Post-specific notes:
Devices with a registered third-party antivirus will show exit 0 with a WARN line
about passive mode. That is the designed behavior; do not "fix" the script to flag them.
Windows Server devices skip the SecurityCenter2 check (the namespace does not exist
there) and are evaluated on engine and service state alone.
Pair this with endpoint security antivirus policy reporting in the admin center; the
script explains why a device is unhealthy, the policy view tells you what to push.
Visual Walkthrough
What must be true before Defender policies can do their job.
Interactive FlowMDE Onboarding — Sensor to Attack Surface Control6/6
1
Licensing verified
MDE P1/P2 entitlements mapped to users (not devices).
deploy
2
Onboarding package via Intune
Configuration profile ships the onboarding blob.
telemetry
3
Sensor reports in
Endpoint sensor connects to MDE service; device visible in portal.
ASR, device control, firewall, web protection via Intune.
6
Attack surface shrinks
Alerts, TVM score and ASR blocks show measurable lift.
MDE Onboarding — Sensor to Attack Surface Control. 1. Licensing verified: MDE P1/P2 entitlements mapped to users (not devices). — 2. Onboarding package via Intune: Configuration profile ships the onboarding blob. — 3. Sensor reports in: Endpoint sensor connects to MDE service; device visible in portal. — 4. OS/platform updated: Defender platform + intelligence updated automatically. — 5. Policies flow: ASR, device control, firewall, web protection via Intune. — 6. Attack surface shrinks: Alerts, TVM score and ASR blocks show measurable lift.
Exit Codes
Code
Meaning
0
Defender active and enabled, or passive by design because a third-party antivirus is registered
1
Defender disabled, stopped, or policy-disabled with no third-party antivirus - the device effectively has no antivirus
2
Prerequisites: Defender platform cmdlets unavailable on this device
3
Unexpected script error - inspect the log
Intune treats every non-zero detection result as remediation-needed. Since this package
has no remediation script, non-zero results appear as issues in device status, which is
exactly the work queue you want from a detection-only package.
Logging
The script writes to C:\ProgramData\Company\Logs\DefenderDisabledDetection.log with a
UTC timestamp, level (INFO, WARN, ERROR), and one fact per line: platform values,
third-party registrations found, service state, the DisableAntiSpyware value, and every
aggregated issue. A few lines per run keeps the file small; monthly rotation is plenty.
The log deliberately records the honesty caveats inline, so a reader six months later can
tell whether a DisableAntiSpyware finding was behavioral or forensic.
Troubleshooting the Script
Exit 3 with a Get-MpComputerStatus exception. The platform service may be mid-
upgrade or damaged. Check the service state by hand and the Microsoft Defender
Antivirus Operational event log for platform errors.
Everything reports compliant on a device you know is unprotected. Confirm you did
not force the package into 32-bit PowerShell; platform cmdlets behave differently
there. The 64-bit setting on the package is not optional.
SecurityCenter2 lines you did not expect. Any AV product registered on the device
appears in the log. Products named "Defender" or "Windows" are ignored; a suspicious
registration is worth checking with your security team, since registering a fake AV
product is a known tampering pattern.
DisableAntiSpyware reads 1 but Defender is running fine. On 2004+ builds that is
Tamper Protection doing its job: the tampering attempt was made and neutralized. Keep
the finding as evidence, not as an outage.
No log file at all. The script never ran. Check assignment scope, device last
check-in, and whether another policy conflicts on script context settings.
How to Validate the Fix
Run detect.ps1 manually in an elevated 64-bit PowerShell on a healthy device. Expect
exit 0 and an INFO line saying Defender is active.
On a lab VM, disable real-time protection via the Windows Security app (or disable the
WinDefend service), then re-run. Expect exit 1 with the matching ERROR lines.
Register a third-party AV scenario if you have one available in the lab and confirm
the passive-mode path exits 0 with its WARN line.
In the admin center, confirm device status reflects the detection results after a
sync, and that the pilot devices you expect to be flagged are the ones flagged.
Re-enable protection on the lab VM and re-run detection to see the state return to 0.
Production Considerations
Execution context. SYSTEM in 64-bit PowerShell. The platform and service queries
need administrator rights; user context adds nothing and breaks some reads.
Idempotency and safety. The script only reads. Running it hourly would not harm a
device, though daily is the sensible cadence for a security signal.
Side effects and reboots. None. No settings are written, no services touched, no
restarts required.
Security review notes. No credentials or secrets are involved. The script reads one
policy value and exposes nothing sensitive in its log. Have your PowerShell automation
SME and security team review and validate it on a test ring before broad production
rollout, and agree on how passive-mode devices should appear in your reporting.
Does the DisableAntiSpyware registry check still matter on current Windows 10 and 11?
As behavior, mostly no: Tamper Protection protects that key on Windows 10 2004 and later
and blocks writes to it. As evidence, yes: a set value on a modern build means something
attempted to disable antispyware, which is a tampering signal worth escalating to your
security team even though the engine shrugged it off.
Why does a device with third-party antivirus report compliant?
Because Defender being passive there is the designed coexistence behavior, and flagging
it would bury real findings in noise. The script logs the third-party registration it
found, so you keep the audit trail. If your security standard requires Defender to be the
active AV everywhere, that is an assignment-scope conversation, not a script change.
Why is there no remediation script in this package?
Because the correct fix depends on the cause: policy repair, third-party removal cleanup,
or service restoration are different actions with different blast radii, and the
authoritative channel for Defender settings is Intune policy. The companion drift
remediation post covers the settings-level repairs that are safe to automate.
Comments
0No comments yet — be the first to share your thoughts.