Force Update Microsoft Defender Antivirus Platform Using PowerShell [Intune Remediation Script]

Quick Answer: To force-update Microsoft Defender Antivirus platform and security intelligence to the latest version using PowerShell, run the script below as SYSTEM via Intune Remediation or a Scheduled Task. The script registers the Microsoft Update service directly (bypassing WSUS), downloads and installs pending Defender platform updates (KB4052623), updates security intelligence with a fallback chain, and verifies success with distinct exit codes. No hard-coded versions. No manual downloads.

Why Defender Falls Behind and Why Update-MpSignature Alone Is Not Enough

You open the Intune compliance dashboard and see it: “Update Microsoft Defender to version 1.1.26080.3” flagged as a critical security update. The device shows non-compliant. For most admins, the immediate instinct is to trigger an emergency update via PowerShell using Update-MpSignature or run the classic command line tool MpCmdRun.exe -SignatureUpdate.

You check Get-MpComputerStatus | Select-Object AntivirusSignatureLastUpdated, AMProductVersion, AMEngineVersion, and to your surprise: definitions updated, but the antimalware platform version remains unpatched and the device remains flagged non-compliant.

Microsoft Intune Defender Antivirus Policy - Engine, Platform, and Security Intelligence Update Channels
Configuring Engine, Platform, and Security Intelligence Update Channels in Microsoft Intune Defender Antivirus policy.

2. Windows Update for Business (WUfB) Update Rings & Intune Defender Update Controls

Because the core antimalware platform component (KB4052623) is distributed as a Windows Update package, healthy cloud-managed endpoints receive it automatically during their normal Windows Update check cycle, adhering to quality update deferral and maintenance windows configured under Intune Defender Update Controls (Endpoint Security > Antivirus policies).

However, when corporate endpoints experience WSUS registry hijacking, broken software distribution caches, or network isolation, these automated update rings stall. In such scenarios, pairing your Intune configuration policies with automated Intune Win32 App and PowerShell deployments or tracking registry health with our guide on collecting Windows registry data using Intune ensures zero blind spots across your fleet.

When Do You Actually Need a PowerShell Remediation Script?

If Microsoft designed Defender to update itself, why does this script exist? Because in large enterprise fleets, 5% to 10% of devices inevitably fall out of the standard pipeline. When that happens, compliance fails, zero-day vulnerabilities remain exposed, and standard portal buttons cannot force the update.

Here is a breakdown of when native automation works versus when this remediation script is required:

Operational Scenario Why Built-in Update Channels Fail Requires Remediation Script?
Standard Managed Device Device is online, healthy, and receives monthly Windows Updates normally. No — Native Intune / WUfB channels handle this automatically.
WSUS Approval Bottlenecks The device points to internal WSUS/SCCM where the security team or admin hasn’t approved KB4052623. Yes — Script directly contacts Microsoft Update cloud and bypasses internal WSUS locks.
Corrupted Defender State WMI or Defender service crashed; AMEngineVersion shows 0.0.0.0 and protection is “Not Running”. Yes — Script kicks the service, forces fresh platform reinstallation, and reinitializes protection.
Post-Migration from 3rd-Party AV Uninstalling a third-party AV or completing fresh device provisioning (see our fix for Windows Autopilot enrollment errors) can leave Defender in a passive, uninitialized, or broken state. Yes — Forces signature download, starts engine, and confirms active RTP status.
Stale / Offline Laptops Field laptops powered off for 30+ days miss intermediate prerequisite signature builds. Yes — Script executes fallback chain (Microsoft Update → MMPC) to catch up instantly.
Disabled Windows Update Service Third-party “debloater” scripts or rogue GPOs set wuauserv to Disabled. Yes — Script restores wuauserv to Manual, runs update, and restores original state.
Architecture Tip: Do not deploy the remediation script as a mandatory Win32 app or run it unconditionally on every healthy device. Instead, deploy it as an Intune Proactive Remediation. The included Detection Script tests whether Defender is already running and compliant. If the device is healthy, the remediation script never runs. It only intervenes on the machines that are actually broken.

What the Script Actually Does

This remediation script handles both layers of the Defender update stack in a single execution:

Step Component Updated Mechanism
Step 1 Security Intelligence + Engine Update-MpSignature with fallback chain (Microsoft Update → MMPC)
Step 2 Defender Platform (KB4052623) Windows Update COM API targeting Microsoft Update directly (bypasses WSUS)
Step 3 Verification Polls Get-MpComputerStatus until versions change, then runs a final WU search

Before and After: What a Broken Defender Looks Like

When Defender platform updates are missing or the antimalware engine has been corrupted, Get-MpComputerStatus shows a clear picture of the damage:

Before: Defender Engine Not Running

Get-MpComputerStatus showing Defender broken - AMEngineVersion 0.0.0.0, AMRunningMode Not running
Before remediation: AMEngineVersion 0.0.0.0, AMRunningMode Not running, AMServiceEnabled False.

Notice AMEngineVersion: 0.0.0.0 and AMRunningMode: Not running. The antimalware service is dead. The device has zero protection.

After: Defender Fully Updated and Running

Get-MpComputerStatus showing Defender fully updated - AMEngineVersion 1.1.26080.3, AMRunningMode Normal
After remediation: AMEngineVersion 1.1.26080.3, AMRunningMode Normal, all services enabled.

After running the remediation script: engine version is current, running mode is Normal, signatures are updated, and the antimalware service is active.

The Production Script: Complete PowerShell Code

Below is the full hardened remediation script. It was built for deployment via Intune Proactive Remediations, SCCM Task Sequences, or a Scheduled Task running as SYSTEM.

Important: This script must run as Administrator (SYSTEM context in Intune). It explicitly registers the Microsoft Update service to bypass WSUS. If your organization requires all updates to flow through WSUS, remove the AddService2 block in Step 2.

POWERSHELL • DefenderRemediation_v2.ps1

# ============================================================
# TechEUC - Defender Update Remediation  (v2 — Hardened)
#
# Purpose:
#   Automatically install available Microsoft Defender updates
#   (Security Intelligence + Platform) from Microsoft Update.
#
# This script does NOT contain a hard-coded Defender version.
#
# Changes from v1:
#   - EULA acceptance before download
#   - Explicit Microsoft Update targeting (bypasses WSUS)
#   - Admin privilege enforcement
#   - Signature fallback chain (MU -> MMPC)
#   - Single-instance mutex
#   - WU service state preservation
#   - Polling loop instead of hardcoded sleep
#   - Log rotation (max 500 KB)
#   - UTF-8 log encoding
#   - COM object cleanup
#   - Network pre-check
#   - Defender-absent early exit
#
# Log:
#   C:ProgramDataTechEUCDefenderUpdate
#       DefenderRemediation.log
# ============================================================

#Requires -Version 5.1
#Requires -RunAsAdministrator


# ============================================================
# CONFIGURATION
# ============================================================

$LogDir      = "$env:ProgramDataTechEUCDefenderUpdate"
$LogFile     = "$LogDirDefenderRemediation.log"
$LogMaxBytes = 512KB                          # rotate when log exceeds this
$MutexName   = "GlobalTechEUCDefenderRemediation"
$PollTimeout = 90                             # max seconds to wait for Defender init
$PollInterval = 10                            # seconds between polls

# Microsoft Update Service ID (constant, never changes)
$MicrosoftUpdateServiceID = "7971f918-a847-4430-9279-4a52d1efe18d"

# Exit codes
$EXIT_SUCCESS              = 0
$EXIT_DEFENDER_ABSENT      = 10
$EXIT_NO_NETWORK           = 11
$EXIT_ANOTHER_INSTANCE     = 12
$EXIT_REMEDIATION_INCOMPLETE = 1
$EXIT_POST_CHECK_FAILED    = 2


# ============================================================
# BOOTSTRAP — directory, log rotation
# ============================================================

New-Item -Path $LogDir -ItemType Directory -Force | Out-Null

# Log rotation: if the log exceeds $LogMaxBytes, keep the last half
if (Test-Path $LogFile) {

    $LogInfo = Get-Item $LogFile

    if ($LogInfo.Length -gt $LogMaxBytes) {

        $Lines    = Get-Content $LogFile -Encoding UTF8
        $HalfIdx  = [math]::Floor($Lines.Count / 2)
        $Lines[$HalfIdx..($Lines.Count - 1)] |
            Set-Content $LogFile -Encoding UTF8 -Force
    }
}


# ============================================================
# FUNCTIONS
# ============================================================

function Write-Log {

    param(
        [string]$Message,
        [string]$Level = "INFO"
    )

    $Time = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $Line = "$Time [$Level] $Message"

    Write-Host $Line
    Add-Content -Path $LogFile -Value $Line -Encoding UTF8
}


function Get-DefenderStatus {

    try {

        $Status = Get-MpComputerStatus -ErrorAction Stop

        return [PSCustomObject]@{

            EngineVersion      = $Status.AMEngineVersion
            PlatformVersion    = $Status.AMProductVersion
            ServiceVersion     = $Status.AMServiceVersion
            AntivirusSignature = $Status.AntivirusSignatureVersion
            RealTimeEnabled    = $Status.RealTimeProtectionEnabled
        }
    }
    catch {

        Write-Log "Unable to retrieve Defender status: $($_.Exception.Message)" "ERROR"
        return $null
    }
}


function Release-ComObject {

    param([object]$ComObject)

    if ($null -ne $ComObject) {

        try {
            [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($ComObject)
        }
        catch {
            # Swallow — object may already be collected
        }
    }
}


function Test-InternetConnectivity {

    # Quick DNS check against Microsoft endpoints
    $Targets = @(
        "go.microsoft.com"
        "definitionupdates.microsoft.com"
    )

    foreach ($Target in $Targets) {

        try {

            $null = [System.Net.Dns]::GetHostAddresses($Target)
            return $true
        }
        catch {
            # try next
        }
    }

    return $false
}


# ============================================================
# RUNTIME ADMIN CHECK (belt-and-suspenders with #Requires)
# ============================================================

$CurrentIdentity  = [Security.Principal.WindowsIdentity]::GetCurrent()
$CurrentPrincipal = [Security.Principal.WindowsPrincipal]$CurrentIdentity

if (-not $CurrentPrincipal.IsInRole(
    [Security.Principal.WindowsBuiltInRole]::Administrator)) {

    Write-Log "Script must run as Administrator. Exiting." "ERROR"
    exit $EXIT_REMEDIATION_INCOMPLETE
}


# ============================================================
# SINGLE-INSTANCE MUTEX
# ============================================================

$Mutex = $null

try {

    $Mutex = [System.Threading.Mutex]::new($false, $MutexName)

    if (-not $Mutex.WaitOne(0)) {

        Write-Log "Another instance of TechEUC Defender Remediation is already running. Exiting." "WARNING"
        exit $EXIT_ANOTHER_INSTANCE
    }
}
catch {

    Write-Log "Mutex check failed (non-fatal): $($_.Exception.Message)" "WARNING"
    # Continue — better to risk overlap than to skip remediation
}


# ============================================================
# COM objects — declared at script scope so STEP 4 can use them
# ============================================================

$Session        = $null
$Searcher       = $null
$ServiceManager = $null
$Downloader     = $null
$Installer      = $null
$Collection     = $null


try {


# ============================================================
# START
# ============================================================

Write-Log "============================================================"
Write-Log "TechEUC Defender Remediation v2 Started"
Write-Log "Computer : $env:COMPUTERNAME"
Write-Log "User     : $env:USERNAME"
Write-Log "============================================================"


# ============================================================
# PRE-FLIGHT: Network check
# ============================================================

if (-not (Test-InternetConnectivity)) {

    Write-Log "No network connectivity to Microsoft endpoints. Exiting." "ERROR"
    exit $EXIT_NO_NETWORK
}

Write-Log "Network connectivity confirmed."


# ============================================================
# PRE-FLIGHT: Defender present?
# ============================================================

$Before = Get-DefenderStatus

if ($null -eq $Before) {

    Write-Log "Defender is not available on this machine (disabled, removed, or replaced by third-party AV)." "ERROR"
    Write-Log "Exiting — nothing to remediate."
    exit $EXIT_DEFENDER_ABSENT
}

Write-Log "BEFORE UPDATE"
Write-Log "Engine   : $($Before.EngineVersion)"
Write-Log "Platform : $($Before.PlatformVersion)"
Write-Log "Service  : $($Before.ServiceVersion)"
Write-Log "AV Sig   : $($Before.AntivirusSignature)"
Write-Log "RTP      : $($Before.RealTimeEnabled)"


# ============================================================
# STEP 1 — Defender Security Intelligence (Signatures)
#
#   Fallback chain: MicrosoftUpdateServer -> MMPC
# ============================================================

Write-Log "Starting Defender signature update (Security Intelligence)."

$SigUpdated = $false

try {

    Update-MpSignature `
        -UpdateSource MicrosoftUpdateServer `
        -ErrorAction Stop

    Write-Log "Signature update from Microsoft Update succeeded." "SUCCESS"
    $SigUpdated = $true
}
catch {

    Write-Log "Microsoft Update source failed: $($_.Exception.Message)" "WARNING"
    Write-Log "Attempting MMPC fallback..."

    try {

        Update-MpSignature `
            -UpdateSource MMPC `
            -ErrorAction Stop

        Write-Log "Signature update from MMPC succeeded." "SUCCESS"
        $SigUpdated = $true
    }
    catch {

        Write-Log "MMPC fallback also failed: $($_.Exception.Message)" "WARNING"
        Write-Log "Signature update could not be completed from any source." "WARNING"
    }
}


# ============================================================
# STEP 2 — Defender Platform Update via Windows Update COM API
#
#   Explicitly targets Microsoft Update (not WSUS).
# ============================================================

Write-Log "Checking Microsoft Update for Defender Platform updates."


# --------------------------------------------------------
# Preserve and start WU service
# --------------------------------------------------------

$OriginalWUStartType = $null

try {

    $WUSvc = Get-Service -Name wuauserv -ErrorAction Stop
    $OriginalWUStartType = $WUSvc.StartType

    if ($WUSvc.StartType -eq 'Disabled') {

        Set-Service -Name wuauserv -StartupType Manual -ErrorAction Stop
        Write-Log "Changed wuauserv startup from Disabled to Manual (will restore)."
    }

    if ($WUSvc.Status -ne 'Running') {

        Start-Service -Name wuauserv -ErrorAction Stop
        Write-Log "Started wuauserv service."
    }
}
catch {

    Write-Log "Could not ensure wuauserv is running: $($_.Exception.Message)" "WARNING"
}


# --------------------------------------------------------
# Create COM objects and register Microsoft Update
# --------------------------------------------------------

$Session = New-Object -ComObject Microsoft.Update.Session
$Session.ClientApplicationID = "TechEUC Defender Remediation"

# Register Microsoft Update service so we bypass WSUS
$ServiceManager = New-Object -ComObject Microsoft.Update.ServiceManager
$ServiceManager.ClientApplicationID = "TechEUC Defender Remediation"

$RegisteredService = $null

try {

    # AddService2 flags: 7 = asfAllowPendingRegistration |
    #                        asfAllowOnlineRegistration  |
    #                        asfRegisterServiceWithAU
    $RegisteredService = $ServiceManager.AddService2(
        $MicrosoftUpdateServiceID, 7, ""
    )

    Write-Log "Microsoft Update service registered for this session."
}
catch {

    Write-Log "Could not register Microsoft Update service: $($_.Exception.Message)" "WARNING"
    Write-Log "Falling back to default update source (may be WSUS)."
}


$Searcher = $Session.CreateUpdateSearcher()

# Point the searcher at Microsoft Update (not WSUS)
if ($null -ne $RegisteredService) {

    $Searcher.ServerSelection = 3          # ssOthers
    $Searcher.ServiceID       = $RegisteredService.ServiceID
}


Write-Log "Searching Microsoft Update for pending Defender updates..."

try {

    $SearchResult = $Searcher.Search(
        "IsInstalled=0 and IsHidden=0 and Type='Software'"
    )
}
catch {

    Write-Log "Update search failed: $($_.Exception.Message)" "ERROR"
    $SearchResult = $null
}


if ($null -ne $SearchResult -and $SearchResult.Updates.Count -gt 0) {

    # --------------------------------------------------------
    # Filter to Defender updates only
    # --------------------------------------------------------

    $DefenderUpdates = @(
        $SearchResult.Updates | Where-Object {

            $_.Title -match "Microsoft Defender Antivirus" -or
            $_.Title -match "Windows Defender Antivirus" -or
            $_.Title -match "KB4052623"
        }
    )

    if ($DefenderUpdates.Count -eq 0) {

        Write-Log "No Defender Platform update is currently pending."
    }
    else {

        Write-Log "Found $($DefenderUpdates.Count) Defender update(s)."

        foreach ($Update in $DefenderUpdates) {

            Write-Log "------------------------------------------------------------"
            Write-Log "Processing Defender update:"
            Write-Log "  Title    : $($Update.Title)"
            Write-Log "  KB       : $($Update.KBArticleIDs -join ', ')"
            Write-Log "  UpdateID : $($Update.Identity.UpdateID)"


            # ------------------------------------------------
            # Accept EULA (required before download)
            # ------------------------------------------------

            if (-not $Update.EulaAccepted) {

                try {

                    $Update.AcceptEula()
                    Write-Log "  EULA accepted."
                }
                catch {

                    Write-Log "  EULA acceptance failed: $($_.Exception.Message)" "ERROR"
                    Write-Log "  Skipping this update."
                    continue
                }
            }


            # ------------------------------------------------
            # Create update collection
            # ------------------------------------------------

            $Collection = New-Object -ComObject Microsoft.Update.UpdateColl
            [void]$Collection.Add($Update)


            # ------------------------------------------------
            # Download
            # ------------------------------------------------

            Write-Log "  Downloading update..."

            $Downloader         = $Session.CreateUpdateDownloader()
            $Downloader.Updates = $Collection

            try {

                $DownloadResult = $Downloader.Download()
            }
            catch {

                Write-Log "  Download threw an exception: $($_.Exception.Message)" "ERROR"
                Release-ComObject $Collection
                Release-ComObject $Downloader
                continue
            }

            Write-Log "  Download ResultCode: $($DownloadResult.ResultCode)"

            # ResultCode: 2 = Succeeded, 3 = SucceededWithErrors
            if ($DownloadResult.ResultCode -notin @(2, 3)) {

                Write-Log "  Download failed (ResultCode $($DownloadResult.ResultCode)). Skipping install." "ERROR"
                Release-ComObject $Collection
                Release-ComObject $Downloader
                continue
            }


            # ------------------------------------------------
            # Install
            # ------------------------------------------------

            Write-Log "  Download completed. Installing update..."

            $Installer         = $Session.CreateUpdateInstaller()
            $Installer.Updates = $Collection

            # Suppress UI prompts (IUpdateInstaller2/3 — may not exist on all builds)
            try { $Installer.AllowSourcePrompts = $false } catch {}
            try { $Installer.ForceQuiet         = $true  } catch {}

            try {

                $InstallResult = $Installer.Install()
            }
            catch {

                Write-Log "  Install threw an exception: $($_.Exception.Message)" "ERROR"
                Release-ComObject $Collection
                Release-ComObject $Downloader
                Release-ComObject $Installer
                continue
            }

            Write-Log "  Install ResultCode : $($InstallResult.ResultCode)"
            Write-Log "  Reboot Required    : $($InstallResult.RebootRequired)"

            if ($InstallResult.ResultCode -eq 2) {

                Write-Log "  Defender update installed successfully." "SUCCESS"
            }
            elseif ($InstallResult.ResultCode -eq 3) {

                Write-Log "  Defender update installed with warnings." "WARNING"
            }
            else {

                Write-Log "  Defender update installation returned non-success (ResultCode $($InstallResult.ResultCode))." "WARNING"
            }

            Release-ComObject $Collection
            Release-ComObject $Downloader
            Release-ComObject $Installer
        }
    }
}
else {

    Write-Log "No pending Defender updates found via Microsoft Update."
}


# ============================================================
# STEP 3 — Wait for Defender to initialize (polling loop)
# ============================================================

Write-Log "Waiting for Defender components to initialize (max ${PollTimeout}s)..."

$Elapsed = 0

while ($Elapsed -lt $PollTimeout) {

    Start-Sleep -Seconds $PollInterval
    $Elapsed += $PollInterval

    $Check = Get-DefenderStatus

    if ($null -ne $Check) {

        # If the platform version changed, Defender has re-initialized
        if ($Before.PlatformVersion -ne $Check.PlatformVersion -or
            $Before.EngineVersion   -ne $Check.EngineVersion) {

            Write-Log "Defender versions changed after ${Elapsed}s — initialization complete."
            break
        }
    }
}

if ($Elapsed -ge $PollTimeout) {

    Write-Log "Timed out waiting for Defender re-initialization (${PollTimeout}s). Proceeding with verification." "WARNING"
}


# ============================================================
# AFTER STATUS
# ============================================================

$After = Get-DefenderStatus

if ($null -eq $After) {

    Write-Log "Unable to verify Defender status after remediation." "ERROR"
    exit $EXIT_POST_CHECK_FAILED
}

Write-Log "AFTER UPDATE"
Write-Log "Engine   : $($After.EngineVersion)"
Write-Log "Platform : $($After.PlatformVersion)"
Write-Log "Service  : $($After.ServiceVersion)"
Write-Log "AV Sig   : $($After.AntivirusSignature)"
Write-Log "RTP      : $($After.RealTimeEnabled)"


# ============================================================
# STEP 4 — Final check: any Defender updates still pending?
# ============================================================

Write-Log "Performing final Microsoft Update check for remaining Defender updates."

if ($null -ne $Searcher) {

    try {

        $FinalSearch = $Searcher.Search(
            "IsInstalled=0 and IsHidden=0 and Type='Software'"
        )

        $RemainingUpdates = @(
            $FinalSearch.Updates | Where-Object {

                $_.Title -match "Microsoft Defender Antivirus" -or
                $_.Title -match "Windows Defender Antivirus" -or
                $_.Title -match "KB4052623"
            }
        )

        if ($RemainingUpdates.Count -gt 0) {

            Write-Log "Defender update(s) are still pending after remediation." "WARNING"

            foreach ($Remaining in $RemainingUpdates) {

                Write-Log "  Pending: $($Remaining.Title)"
            }

            Write-Log "RESULT: REMEDIATION INCOMPLETE." "WARNING"
            exit $EXIT_REMEDIATION_INCOMPLETE
        }
        else {

            Write-Log "No remaining Defender updates pending."
        }
    }
    catch {

        Write-Log "Final update check failed: $($_.Exception.Message)" "ERROR"
        exit $EXIT_POST_CHECK_FAILED
    }
}
else {

    Write-Log "Searcher not available — skipping final Windows Update verification." "WARNING"
}


# ============================================================
# FINAL RESULT
# ============================================================

Write-Log "============================================================"
Write-Log "SUCCESS — Defender is up to date with available updates."
Write-Log "Final Engine   : $($After.EngineVersion)"
Write-Log "Final Platform : $($After.PlatformVersion)"
Write-Log "Final Service  : $($After.ServiceVersion)"
Write-Log "Final AV Sig   : $($After.AntivirusSignature)"
Write-Log "Log File       : $LogFile"
Write-Log "============================================================"

Write-Host ""
Write-Host "============================================================"
Write-Host "       ***** DEFENDER REMEDIATION SUCCESS *****"
Write-Host "============================================================"
Write-Host " Engine   : $($After.EngineVersion)"
Write-Host " Platform : $($After.PlatformVersion)"
Write-Host " Service  : $($After.ServiceVersion)"
Write-Host " Log File : $LogFile"
Write-Host "============================================================"

exit $EXIT_SUCCESS


}   # end outer try


# ============================================================
# CLEANUP — always runs
# ============================================================

finally {

    # --------------------------------------------------------
    # Release COM objects
    # --------------------------------------------------------

    foreach ($Obj in @($Installer, $Downloader, $Collection, $Searcher, $Session, $ServiceManager)) {

        Release-ComObject $Obj
    }


    # --------------------------------------------------------
    # Restore WU service startup type
    # --------------------------------------------------------

    if ($null -ne $OriginalWUStartType) {

        try {

            Set-Service -Name wuauserv -StartupType $OriginalWUStartType -ErrorAction SilentlyContinue
            Write-Log "Restored wuauserv startup type to: $OriginalWUStartType"
        }
        catch {
            # Best-effort
        }
    }


    # --------------------------------------------------------
    # Release mutex
    # --------------------------------------------------------

    if ($null -ne $Mutex) {

        try {

            $Mutex.ReleaseMutex()
            $Mutex.Dispose()
        }
        catch {
            # Best-effort
        }
    }

    Write-Log "Cleanup complete. Script exiting."
}

How to Deploy via Intune Proactive Remediations

The fastest way to push this across your fleet is through Intune Proactive Remediations (now called Remediations under Devices).

Step 1: Create a Detection Script

The detection script checks whether the Defender engine is running and whether the platform version is current. If either check fails, it returns a non-zero exit code, which triggers the remediation.

POWERSHELL • DefenderDetection.ps1

# ============================================================
# Detection Script - Check Defender Health
# ============================================================

try {
    $Status = Get-MpComputerStatus -ErrorAction Stop

    if ($Status.AMRunningMode -ne "Normal" -or
        $Status.AMEngineVersion -eq "0.0.0.0" -or
        $Status.AMServiceEnabled -eq $false) {

        Write-Host "Defender is not healthy. Remediation required."
        exit 1
    }

    Write-Host "Defender is healthy. Engine: $($Status.AMEngineVersion)"
    exit 0
}
catch {
    Write-Host "Cannot query Defender: $($_.Exception.Message)"
    exit 1
}

Step 2: Upload Both Scripts to Intune

  1. Go to DevicesRemediations+ Create script package.
  2. Name it TechEUC - Defender Update Remediation.
  3. Upload the detection script and the remediation script (the full script above).
  4. Set Run this script using the logged-on credentials to No (runs as SYSTEM).
  5. Set Run script in 64-bit PowerShell to Yes.
  6. Assign to your target device groups.
  7. Set the schedule (recommended: every 4 hours for the first week, then daily).

Exit Codes Reference

The script uses distinct exit codes so you can filter results in Intune reporting or your SIEM:

Exit Code Meaning Action Required
0 Success — Defender is up to date None
1 Remediation incomplete — updates still pending Check network, WSUS approval, or re-run
2 Post-check failed — cannot verify final state Check Defender service health
10 Defender absent or disabled on this machine Check if third-party AV is installed
11 No network connectivity to Microsoft endpoints Check firewall rules and proxy settings
12 Another script instance already running Wait for existing instance to finish

Key Hardening Features in This Script

If you have used simpler Defender update scripts before, you will notice this one handles a number of edge cases that break production deployments:

  • EULA acceptance: The Windows Update COM API requires programmatic EULA acceptance before downloading. Without it, downloads fail silently.
  • Microsoft Update bypass: On WSUS-managed devices, the default WUA searcher talks to WSUS, not Microsoft Update. This script explicitly registers the Microsoft Update service (ServiceID 7971f918-a847-4430-9279-4a52d1efe18d) so it always finds the latest Defender platform version.
  • Signature fallback chain: If MicrosoftUpdateServer fails (common behind corporate proxies), the script falls back to MMPC (Microsoft Malware Protection Center).
  • Single-instance mutex: Prevents two copies from running simultaneously when both Intune Remediations and a Scheduled Task trigger at the same time.
  • WU service state preservation: Captures the original wuauserv startup type before changing it, and restores it in the finally block.
  • COM object cleanup: All WUA COM objects are released via Marshal::ReleaseComObject() in the finally block to prevent handle leaks.
  • Polling loop instead of hardcoded sleep: Instead of Start-Sleep -Seconds 45, the script polls Get-MpComputerStatus every 10 seconds for up to 90 seconds, breaking early when versions change.

Frequently Asked Questions

Does this script require internet access?

Yes. The script contacts Microsoft Update servers directly. If your devices sit behind a proxy that blocks *.microsoft.com, both the signature update and the platform update will fail. The script does a DNS pre-check and exits with code 11 if connectivity is not available.

Will this script conflict with WSUS or SCCM update management?

The script registers the Microsoft Update service for the current session only. It does not permanently change the device’s WSUS configuration or Group Policy settings. After the script exits, the device continues using its configured update source for all other updates.

Can I use this script with Intune’s native Windows Update policies?

Yes. This script complements Intune Update Rings. The Update Ring controls the normal update cadence. This script acts as an emergency remediation for devices where the normal cadence has failed or where Defender is broken and needs immediate recovery.

Does the script trigger a reboot?

No. The script never calls Restart-Computer. It logs whether a reboot is required but leaves the decision to your existing reboot policy (Intune Update Ring deadline, GPO, or manual action).

Conclusion

When Defender falls behind on platform updates, a simple Update-MpSignature is not enough. KB4052623 ships through Windows Update, and on WSUS-managed devices it can stay unapproved indefinitely. This script bridges that gap by talking directly to Microsoft Update, handling EULA acceptance, cleaning up COM objects, and reporting distinct exit codes for every failure mode.

Deploy it as an Intune Remediation, let it run on a 4-hour schedule, and your Defender compliance numbers will stop bleeding red.


Need help building custom Intune remediation scripts for your organization?

👉 Hire me on Fiverr Pro | 📧 Contact TechEUC



TechEUC - Atoofa Shaikh
FIVERR PRO VERIFIED 12+ YRS EXPERIENCE

Atoofa Shaikh

Senior Microsoft 365, EUC & Cloud Endpoint Architect

Need custom Win32 App Packaging, PowerShell Automation, Zero-Touch Intune Autopilot, or SCCM Co-Management for your enterprise or MSP? I specialize in production-grade deployment architectures with zero downtime.