Build a comprehensive, layered detection policy stack in Microsoft Defender for Endpoint. Configure next-generation antivirus, EDR in block mode, custom indicators of compromise, RBAC for SOC tiers, automated investigation levels, web content filtering, device control, tamper protection, network protection, and custom detection rules with KQL.
Microsoft Defender for Endpoint provides a rich set of configurable detection and response policies that work together as a layered defense stack. This lab covers end-to-end deployment: next-generation antivirus via Intune, EDR in block mode, custom indicators, RBAC, and automated investigation and response (AIR). You will also configure supporting protections: web content filtering, device control for removable media, tamper protection, and network protection. Finally, you will build custom detection rules using KQL and validate the entire policy stack with simulated attacks.
Scenario: A global financial services firm operates 15,000 endpoints across North America, Europe, and Asia-Pacific.
The CISO has mandated a unified detection policy framework that balances security coverage with operational stability. SOC Tier 1 analysts need read-only access, Tier 2 analysts need response capabilities, and Tier 3 engineers need full configuration access. Regulatory requirements (PCI-DSS, SOX, GDPR) mandate USB device control, web filtering, and automated investigation documentation.
Success criteria: zero policy gaps across all device groups, RBAC aligned to SOC tiers, automated investigation enabled for high-confidence alerts, and a detection effectiveness dashboard.
Misconfigured detection policies are the leading cause of alert blind spots; organizations frequently deploy MDE without tuning defaults. Layered detection reduces dwell time: next-gen AV catches known threats, EDR catches behavioral anomalies, and custom rules target organization-specific risks. RBAC enforcement ensures least-privilege access, preventing accidental or unauthorized configuration changes in production. Automated investigation accelerates mean time to respond (MTTR) by eliminating manual triage for high-confidence alerts.
Before configuring policies, understand how MDE's detection layers work together. Each layer addresses a different phase of the threat lifecycle.
# ---------------------------------------------------------
# WHAT: Audit the current Defender AV configuration on a device
# WHY: Before deploying new detection policies, baseline what's
# already configured. Inconsistencies across devices indicate
# policy gaps or conflicting Intune/GPO configurations.
# ---------------------------------------------------------
# Review protection settings
# DisableRealtimeMonitoring: $false = real-time scanning ON (required)
# DisableBehaviorMonitoring: $false = behavior detection ON (required)
# MAPSReporting: 0=Off, 1=Basic, 2=Advanced (target: 2 for full cloud)
# SubmitSamplesConsent: 0=Prompt, 1=Safe, 2=Never, 3=All (target: 3)
# CloudBlockLevel: 0=Default, 2=High, 4=High+, 6=Zero tolerance
# CloudExtendedTimeout: Seconds to wait for cloud verdict (target: 50)
# EnableNetworkProtection: 0=Off, 1=Block, 2=Audit
# EnableControlledFolderAccess: 0=Off, 1=Block, 2=Audit
# PUAProtection: 0=Off, 1=Block, 2=Audit
Get-MpPreference | Select-Object `
DisableRealtimeMonitoring,
DisableBehaviorMonitoring,
MAPSReporting,
SubmitSamplesConsent,
CloudBlockLevel,
CloudExtendedTimeout,
EnableNetworkProtection,
EnableControlledFolderAccess,
PUAProtection
# Check AV engine and signature versions
# WHY: Outdated engines may lack detection capabilities for new threats.
# AMProductVersion = Defender platform version (e.g., 4.18.x)
# AMEngineVersion = AV engine version (updated with platform)
# AntispywareSignatureVersion = signature/definition version
# AntivirusSignatureLastUpdated = should be within last 24 hours
Get-MpComputerStatus | Select-Object `
AMProductVersion,
AMEngineVersion,
AntispywareSignatureVersion,
AntivirusSignatureLastUpdated,
RealTimeProtectionEnabled,
BehaviorMonitorEnabledDeploy a centrally managed antivirus policy through Microsoft Intune to ensure consistent protection settings across all endpoint groups.
The table below maps every Microsoft-documented antivirus and advanced scan setting to its OS default and the recommended hardened value, with the matching Set-MpPreference parameter and Defender CSP node. Source: Configure Microsoft Defender Antivirus scan options and the Defender CSP reference.
| Setting (Intune name) | OS Default | Recommended | PowerShell parameter |
|---|---|---|---|
| Real-time protection | Enabled | Enabled | -DisableRealtimeMonitoring $false |
| Behavior monitoring | Enabled | Enabled | -DisableBehaviorMonitoring $false |
| IOAV (scan downloads / attachments) | Enabled | Enabled | -DisableIOAVProtection $false |
| Script scanning | Enabled | Enabled | -DisableScriptScanning $false |
| Email scanning | Disabled | Enabled (PST/MBX/DBX/MIME) | -DisableEmailScanning $false |
| Archive scanning (.zip, .rar) | Enabled | Enabled | -DisableArchiveScanning $false |
| Scan network files | Disabled | Enabled (workstations) / Disabled (file servers) | -DisableScanningNetworkFiles $false |
| Scan mapped network drives (full scan) | Disabled | Enabled | -DisableScanningMappedNetworkDrivesForFullScan $false |
| Scan removable drives (full scan) | Disabled | Enabled | -DisableRemovableDriveScanning $false |
| Scan reparse points | Disabled | Disabled (avoid recursion) | No Set-MpPreference parameter; configure via Intune / Defender CSP only |
| Scan excluded items during quick scan | Disabled (0) | Enabled (1) for high-risk groups | CSP QuickScanIncludeExclusions |
| Max CPU load during scan | 50% | 50% | -ScanAvgCPULoadFactor 50 |
| Apply CPU throttle to scheduled scans only (vs. scheduled and custom) | Enabled (1) - throttle scheduled only | Enabled (1) | CSP ThrottleForScheduledScanOnly (1 = scheduled only, 0 = scheduled + custom) |
| Scan archive max depth | 0 (unlimited) | 0 (unlimited) | CSP ArchiveMaxDepth |
| Scan archive max size (KB) | 0 (no limit) | 0 (no limit) | CSP ArchiveMaxSize |
| Scan schedule day (0=Daily, 1=Sun ... 7=Sat, 8=Never) | Never (8) | Saturday (7) - low business impact | -ScanScheduleDay 7 |
| Scan schedule time (minutes past midnight) | 120 (02:00) | 120 (02:00) - off-hours | -ScanScheduleTime 02:00:00 |
| Randomize scheduled scan time | Enabled (1) | Enabled (1) - good for VDI | -RandomizeScheduleTaskTimes $true |
| Catch-up full scan (if missed) | Disabled | Enabled (run after laptop returns) | -DisableCatchupFullScan $false |
| Catch-up quick scan (if missed) | Disabled | Enabled | -DisableCatchupQuickScan $false |
| Check signatures before scheduled scan | Disabled | Enabled | -CheckForSignaturesBeforeRunningScan $true |
| PUA protection | 0 (Off) | 1 (Block) | -PUAProtection Enabled |
| Threat default actions per severity (1=Clean, 2=Quarantine, 3=Remove) | Defender chooses (0) | Severe/High = Quarantine (2), Medium/Low = Clean (1) | -SevereThreatDefaultAction / -HighThreatDefaultAction etc. |
| File hash computation (for IoC matching) | Disabled (0) | Enabled (1) | CSP EnableFileHashComputation |
| Intel TDT integration | 0 (Microsoft-managed) | 1 (Always on, where supported) | CSP IntelTDTEnabled |
# =====================================================================
# WHAT: Audit every Microsoft-documented scan-type and AV setting.
# WHY: The previous Step 1 block confirms top-level protection state.
# This block covers the "advanced scan types" doc end-to-end:
# email, archive, network files, mapped drives, removable drives,
# script scanning, CPU throttling, scheduling, and threat actions.
# DOCS:
# - https://learn.microsoft.com/defender-endpoint/configure-advanced-scan-types-microsoft-defender-antivirus
# - https://learn.microsoft.com/powershell/module/defender/set-mppreference
# OUTPUT: Grouped tables - every property has a documented meaning.
# =====================================================================
$mp = Get-MpPreference
# ---- File-type scanning ----
# DisableEmailScanning : $true = email files NOT scanned (default)
# $false = scan PST/MBX/DBX/MIME files
# DisableScriptScanning : $true = scripts NOT scanned (NOT recommended)
# $false = scripts ARE scanned (default)
# DisableArchiveScanning : $true = .zip/.rar NOT scanned (NOT recommended)
# $false = archives ARE scanned (default)
# ScanOnlyIfIdleEnabled : $true = scheduled scans run only when device is idle (default)
# $false = scheduled scans run regardless of idle state
$mp | Select-Object `
DisableEmailScanning,
DisableScriptScanning,
DisableArchiveScanning,
ScanOnlyIfIdleEnabled
# ---- Where to scan ----
# DisableScanningNetworkFiles : $true = network shares NOT scanned during RTP
# DisableScanningMappedNetworkDrivesForFullScan: $true = mapped drives NOT scanned during full
# DisableRemovableDriveScanning : $true = USB/removable drives NOT scanned in full
# DisableIOAVProtection : $true = downloads/attachments NOT scanned
$mp | Select-Object `
DisableScanningNetworkFiles,
DisableScanningMappedNetworkDrivesForFullScan,
DisableRemovableDriveScanning,
DisableIOAVProtection
# ---- CPU and scheduling ----
# ScanAvgCPULoadFactor : 0-100; advisory CPU ceiling during scheduled scans (default 50)
# ScanScheduleDay : 0=Daily, 1=Sun, 2=Mon, 3=Tue, 4=Wed, 5=Thu, 6=Fri, 7=Sat, 8=Never (default 8=Never)
# ScanScheduleTime : TimeSpan; when daily/weekly scheduled scan runs
# ScanScheduleQuickScanTime : minutes past midnight for the quick scan
# ScanParameters : 1=Quick scan, 2=Full scan (scheduled type)
# RandomizeScheduleTaskTimes : $true = jitter (recommended for VDI)
$mp | Select-Object `
ScanAvgCPULoadFactor,
ScanScheduleDay,
ScanScheduleTime,
ScanScheduleQuickScanTime,
ScanParameters,
RandomizeScheduleTaskTimes
# ---- Catch-up and signature checks ----
# DisableCatchupFullScan : $true = do NOT run missed scheduled full scan when device returns
# DisableCatchupQuickScan : $true = do NOT run missed scheduled quick scan
# CheckForSignaturesBeforeRunningScan : $true = pull latest signatures before scheduled scan
# SignatureUpdateInterval : hours between signature update checks (0 = use schedule)
$mp | Select-Object `
DisableCatchupFullScan,
DisableCatchupQuickScan,
CheckForSignaturesBeforeRunningScan,
SignatureUpdateInterval
# ---- Threat default actions ----
# 0 = Defender chooses, 1 = Clean, 2 = Quarantine, 3 = Remove,
# 6 = Allow, 8 = User defined, 9 = NoAction (do not use)
$mp | Select-Object `
SevereThreatDefaultAction,
HighThreatDefaultAction,
ModerateThreatDefaultAction,
LowThreatDefaultAction,
UnknownThreatDefaultAction
# ---- Update channels (gradual rollout rings) ----
# EngineUpdatesChannel / PlatformUpdatesChannel:
# 0=NotConfigured, 2=Beta, 3=Preview, 4=Staged, 5=Broad, 6=Critical (48h delay)
# SecurityIntelligenceUpdatesChannel:
# 0=NotConfigured, 4=Staged, 5=Broad
# MeteredConnectionUpdates: $true = allow updates over metered (mobile) connections
$mp | Select-Object `
EngineUpdatesChannel,
PlatformUpdatesChannel,
SecurityIntelligenceUpdatesChannel,
MeteredConnectionUpdates
# ---- Exclusions (FULL list - excessive exclusions weaken scanning) ----
# Every exclusion is a hole in coverage. You must see WHAT is excluded,
# not just how many - a single "C:\" would be far worse than 50 narrow
# build-tool exclusions. Empty values display as "(none)".
[PSCustomObject]@{
ExcludedPaths = if (@($mp.ExclusionPath).Count) { $mp.ExclusionPath -join '; ' } else { '(none)' }
ExcludedExtensions = if (@($mp.ExclusionExtension).Count) { $mp.ExclusionExtension -join '; ' } else { '(none)' }
ExcludedProcesses = if (@($mp.ExclusionProcess).Count) { $mp.ExclusionProcess -join '; ' } else { '(none)' }
ExcludedIpAddresses = if (@($mp.ExclusionIpAddress).Count) { $mp.ExclusionIpAddress -join '; ' } else { '(none)' }
} | Format-List# =====================================================================
# WHAT: Apply the "recommended" column of the table above.
# WHY: These values harden scanning beyond Windows defaults while
# respecting Microsoft's published guidance for typical
# enterprise workstations.
# CAUTION: If this device is Intune-managed, Intune-assigned values
# override these on the next policy sync. Apply via Intune
# for production - this is for lab/dev/break-glass scenarios.
# =====================================================================
# --- File-type scanning ---
Set-MpPreference -DisableEmailScanning $false # scan PST/MBX/DBX/MIME
Set-MpPreference -DisableScriptScanning $false # keep script scan on
Set-MpPreference -DisableArchiveScanning $false # keep archive scan on
# --- Where to scan ---
Set-MpPreference -DisableScanningNetworkFiles $false # scan network files during RTP
Set-MpPreference -DisableScanningMappedNetworkDrivesForFullScan $false # full scan covers mapped drives
Set-MpPreference -DisableRemovableDriveScanning $false # full scan covers USB drives
Set-MpPreference -DisableIOAVProtection $false # scan downloads/attachments
# --- CPU and scheduling ---
Set-MpPreference -ScanAvgCPULoadFactor 50 # advisory cap (default)
Set-MpPreference -ScanParameters 2 # 2 = Full scan
Set-MpPreference -ScanScheduleDay 7 # 7 = Saturday (low impact); per docs: 0=Daily, 1=Sun, 2=Mon, 3=Tue, 4=Wed, 5=Thu, 6=Fri, 7=Sat, 8=Never
Set-MpPreference -ScanScheduleTime 02:00:00 # 02:00 local
Set-MpPreference -RandomizeScheduleTaskTimes $true # jitter for VDI fleets
# --- Catch-up and signature freshness ---
Set-MpPreference -DisableCatchupFullScan $false # run missed scans on return
Set-MpPreference -DisableCatchupQuickScan $false
Set-MpPreference -CheckForSignaturesBeforeRunningScan $true # fresh signatures first
# --- PUA and threat actions ---
# Action codes: 1=Clean, 2=Quarantine, 3=Remove, 6=Allow, 8=UserDefined, 9=NoAction
Set-MpPreference -PUAProtection Enabled
Set-MpPreference -SevereThreatDefaultAction Quarantine # 2
Set-MpPreference -HighThreatDefaultAction Quarantine # 2
Set-MpPreference -ModerateThreatDefaultAction Clean # 1
Set-MpPreference -LowThreatDefaultAction Clean # 1
Set-MpPreference -UnknownThreatDefaultAction Quarantine # 2
# --- Update channels (recommended rings for a production fleet) ---
# 4 = Staged (early production), 5 = Broad (general production)
Set-MpPreference -EngineUpdatesChannel 5
Set-MpPreference -PlatformUpdatesChannel 5
Set-MpPreference -SignatureUpdatesChannel 5 # = SecurityIntelligenceUpdatesChannel CSP
Set-MpPreference -MeteredConnectionUpdates $trueDisableScanningNetworkFiles = $true to avoid scanning what other endpoints are already scanning locally.Cloud-delivered protection sends suspicious files to Microsoft's cloud for detonation and machine learning analysis. Block-at-first-sight stops unknown threats within seconds of first encounter.
# ---------------------------------------------------------
# WHAT: Verify and configure cloud-delivered protection settings
# WHY: Cloud protection sends suspicious files to Microsoft's
# detonation sandbox for ML analysis. Block-at-first-sight
# stops zero-day threats within seconds of first encounter.
# ---------------------------------------------------------
# Verify current cloud protection settings
Get-MpPreference | Select-Object `
MAPSReporting,
SubmitSamplesConsent,
CloudBlockLevel,
CloudExtendedTimeout
# EXPECTED VALUES for maximum protection:
# MAPSReporting : 2 (Advanced) - sends full file metadata to cloud
# SubmitSamplesConsent : 3 (Send all samples automatically)
# CloudBlockLevel : 6 (Zero Tolerance) - blocks ALL unknown executables
# Alternatives: 4 (High+) - aggressive but allows known-good
# 2 (High) - balanced for typical enterprise
# CloudExtendedTimeout : 50 (max - seconds to wait for cloud verdict)
# Configure directly via PowerShell if Intune policy hasn't applied yet
# NOTE: Intune-managed settings will override these on next policy sync.
Set-MpPreference -MAPSReporting Advanced
Set-MpPreference -SubmitSamplesConsent SendAllSamples
Set-MpPreference -CloudBlockLevel 6
Set-MpPreference -CloudExtendedTimeout 50EDR in block mode allows MDE to take automated blocking and remediation actions on post-breach detections, even when a third-party antivirus is installed. It acts as a safety net when the primary AV misses a threat.
// ---------------------------------------------------------
// WHAT: Find all EDR in block mode remediation actions
// WHY: EDR block mode acts as a safety net - it blocks threats
// that the primary AV missed, using behavioral detection.
// Tracking these events quantifies the value of EDR block mode.
// TABLE: DeviceEvents - ActionType "AntivirusDetection" with
// IsEdrInBlockMode flag in AdditionalFields.
// KEY FIELDS:
// ThreatName = the malware family/variant detected post-breach
// WasRemediated = whether the threat was successfully cleaned
// IsEdrBlockMode = true when EDR (not AV) triggered the block
// OUTPUT: Threat names ranked by block count - evidence that
// EDR block mode is catching threats the primary AV missed.
// ---------------------------------------------------------
DeviceEvents
| where Timestamp > ago(30d)
| where ActionType == "AntivirusDetection"
| extend ParsedFields = parse_json(AdditionalFields)
| extend ThreatName = tostring(ParsedFields.ThreatName),
WasRemediated = tobool(ParsedFields.WasRemediated),
IsEdrBlockMode = tobool(ParsedFields.IsEdrInBlockMode)
| where IsEdrBlockMode == true
| summarize BlockedCount = count(),
UniqueThreats = dcount(ThreatName),
AffectedDevices = dcount(DeviceName)
by ThreatName, WasRemediated
| sort by BlockedCount descCustom indicators extend MDE detection beyond global threat intelligence. Create organization-specific blocks or alerts for known-bad file hashes, IP addresses, URLs, and certificates.
// ---------------------------------------------------------
// WHAT: Find all alerts triggered by custom indicators (IoCs)
// WHY: Measures the effectiveness of your organization-specific
// threat intelligence - custom file hashes, IPs, URLs, and
// certificates that you imported into MDE.
// TABLES: AlertInfo (alert metadata) joined with AlertEvidence
// (files, processes, IPs associated with the alert).
// DETECTION SOURCES:
// "CustomDetection" = KQL custom detection rules
// "CustomerTI" = Custom Indicators (file hash, IP, URL, cert)
// OUTPUT: Which indicators are hitting, how often, and severity.
// Low hit counts may indicate stale IoCs that need removal.
// ---------------------------------------------------------
AlertInfo
| where Timestamp > ago(30d)
| where DetectionSource == "CustomDetection"
or DetectionSource == "CustomerTI"
| join kind=inner AlertEvidence on AlertId
| summarize HitCount = count(),
AffectedDevices = dcount(DeviceId),
IndicatorValues = make_set(FileName)
by Title, Severity, DetectionSource
| sort by HitCount descDevice groups determine how policies, RBAC permissions, and automated investigation levels are applied. Use tag-based membership for dynamic, scalable grouping.
HKLM\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection\DeviceTagging\GroupRBAC ensures that SOC analysts only access the devices and actions appropriate for their tier. This prevents configuration drift and supports audit compliance.
Automated Investigation and Response enables MDE to automatically investigate alerts and remediate threats without analyst intervention. Configure the automation level per device group based on risk tolerance.
C:\Windows, C:\Program Files) require analyst approval; actions in other locations remediate automatically.// ---------------------------------------------------------
// WHAT: Review automated investigation (AIR) outcomes by severity
// WHY: Measures how effectively AIR is handling alerts without
// human intervention. A high AutoRemediationRate indicates
// well-tuned automation; low rates suggest configuration gaps.
// TABLES: AlertInfo joined with AlertEvidence on AlertId.
// KEY FIELDS:
// AttackTechniques: MITRE ATT&CK techniques (non-empty = mapped)
// RemediationStatus: "Remediated" | "PendingApproval" | "Failed"
// LOGIC: Calculates auto-remediation rate as a percentage.
// Target: > 80% for standard workstation device groups.
// OUTPUT: One row per severity level with counts and rates.
// PendingApproval build-up = need more Tier 2 analysts or
// adjust automation level to "Full" for that device group.
// ---------------------------------------------------------
AlertInfo
| where Timestamp > ago(30d)
| where isnotempty(AttackTechniques)
| join kind=inner (
AlertEvidence
| where Timestamp > ago(30d)
| where isnotempty(RemediationStatus)
) on AlertId
| summarize TotalAlerts = dcount(AlertId),
AutoRemediated = dcountif(AlertId, RemediationStatus == "Remediated"),
PendingApproval = dcountif(AlertId, RemediationStatus == "PendingApproval"),
Failed = dcountif(AlertId, RemediationStatus == "Failed")
by Severity
| extend AutoRemediationRate = round(100.0 * AutoRemediated / TotalAlerts, 1)
| sort by SeverityWeb content filtering blocks access to websites based on category, reducing attack surface from drive-by downloads, phishing sites, and malicious content hosting.
Device control restricts USB and removable media access to prevent data exfiltration and block USB-based malware delivery. Configure granular policies that allow approved devices while blocking unauthorized ones.
# ---------------------------------------------------------
# WHAT: Review device control events for USB/removable media
# WHY: Device control policies generate events when users connect
# removable storage devices. Monitoring these events helps
# validate policy enforcement and detect shadow IT.
# EVENT IDs in the Defender Operational log:
# 1123 = Removable storage BLOCKED (block mode active)
# 1124 = Removable storage AUDITED (audit mode, logged only)
# 1125 = Removable storage policy APPLIED to device
# ---------------------------------------------------------
Get-WinEvent -LogName "Microsoft-Windows-Windows Defender/Operational" |
Where-Object { $_.Id -in @(1123, 1124, 1125) } |
Select-Object -First 20 TimeCreated, Id, Message |
Format-Table -AutoSize
# Event ID 1123: Blocked removable storage event
# Event ID 1124: Audit removable storage event
# Event ID 1125: Removable storage policy appliedTamper protection prevents malicious actors or unauthorized users from disabling Microsoft Defender Antivirus, real-time protection, cloud-delivered protection, and other security features.
# ---------------------------------------------------------
# WHAT: Verify tamper protection status on the local device
# WHY: Tamper protection prevents attackers (and local admins)
# from disabling Defender AV security features.
# When active, attempts to disable protections are denied.
# ---------------------------------------------------------
# Check tamper protection and real-time protection status
# IsTamperProtected: True = tamper protection active (security settings locked)
# RealTimeProtectionEnabled: True = file scanning is active
Get-MpComputerStatus | Select-Object IsTamperProtected, RealTimeProtectionEnabled
# Expected output when tamper protection is active:
# IsTamperProtected : True
# RealTimeProtectionEnabled : True
# TEST: Attempt to disable real-time protection (should FAIL when tamper protection is ON)
# WHY: Validates that tamper protection is actually enforcing.
# EXPECTED OUTCOME (tamper protection ON):
# PowerShell raises a non-terminating error similar to:
# Set-MpPreference : Operation failed with the following error: 0x800106ba.
# Operation: Set preference.
# Target: -DisableRealtimeMonitoring $true
# The Defender setting does NOT change. Confirm by re-running:
# (Get-MpPreference).DisableRealtimeMonitoring # should still be False
# UNEXPECTED OUTCOME (tamper protection OFF):
# The command silently succeeds and DisableRealtimeMonitoring becomes True.
# Re-enable tamper protection in the portal immediately if this happens.
Set-MpPreference -DisableRealtimeMonitoring $trueNetwork protection extends SmartScreen protection to all HTTP/HTTPS traffic on the device, blocking outbound connections to malicious or suspicious destinations regardless of the browser or application used.
# ---------------------------------------------------------
# WHAT: Configure and verify network protection
# WHY: Network protection extends SmartScreen to ALL HTTP/HTTPS
# traffic (not just Edge). Blocks outbound connections to
# malicious domains, C2 infrastructure, and phishing sites.
# Required for web content filtering on non-Edge browsers.
# MITRE: Blocks T1071 (Application Layer Protocol) C2 channels.
# ---------------------------------------------------------
# STEP 1 - Read current state.
# OUTPUT: 0 = Disabled, 1 = Enabled (Block), 2 = Audit
Get-MpPreference | Select-Object EnableNetworkProtection
# ---------------------------------------------------------
# STEP 2 - Pick ONE of the deployment options below.
# Do NOT run both - they overwrite each other.
# ---------------------------------------------------------
# OPTION A - Initial deployment / pilot (RECOMMENDED FIRST):
# Run in AUDIT mode for at least 1 week. Connections that would have
# been blocked are logged to the Defender Operational log (Event ID 1125)
# but the user is not interrupted. Review the events to identify
# legitimate apps/services that would be blocked before going to Block.
# Set-MpPreference -EnableNetworkProtection AuditMode
# OPTION B - Production enforcement (after audit review is clean):
# Run in BLOCK mode. Malicious connections are blocked and the user
# is shown a block notification. Custom URL/IP indicators also enforce
# at the network layer in this mode.
Set-MpPreference -EnableNetworkProtection Enabled
# STEP 3 - Verify the new value persisted.
Get-MpPreference | Select-Object EnableNetworkProtection
# STEP 4 - Functional test using Microsoft's SmartScreen test URL.
# The URL is safe but classified as malicious by SmartScreen.
# Block mode -> the browser shows a block page.
# Audit mode -> the page loads, but Event ID 1125 is logged.
Start-Process "https://smartscreentestratings2.net"Custom detection rules use KQL queries to automatically detect suspicious activity and generate alerts or take response actions. Build rules for threats specific to your organization.
// ---------------------------------------------------------
// WHAT: Custom detection rule - LSASS credential access
// WHY: Detects credential dumping tools (Mimikatz, ProcDump,
// pypykatz) targeting the LSASS process to extract
// passwords, NTLM hashes, and Kerberos tickets.
// MITRE: T1003.001 (OS Credential Dumping: LSASS Memory)
// DETECTION LOGIC:
// Matches known credential dumping tool names OR any process
// whose command line references "lsass" with dump-related keywords.
// Excludes SYSTEM/LOCAL SERVICE/NETWORK SERVICE accounts as
// they legitimately interact with LSASS.
// TRUE POSITIVE: Mimikatz, ProcDump with -ma lsass, pypykatz.
// FALSE POSITIVE: Some EDR agents or monitoring tools may access LSASS.
// Verify InitiatingProcessFileName before adding exclusions.
// FREQUENCY: Every 1 hour for near-real-time detection.
// RESPONSE: Auto-isolate the device and collect investigation package.
// ---------------------------------------------------------
DeviceProcessEvents
| where Timestamp > ago(1h)
| where FileName in~ ("procdump.exe", "procdump64.exe", "mimikatz.exe", "pypykatz.exe")
or (ProcessCommandLine has "lsass" and ProcessCommandLine has_any ("dump", "minidump", "sekurlsa"))
| where InitiatingProcessAccountName !in ("SYSTEM", "LOCAL SERVICE", "NETWORK SERVICE")
| project Timestamp, DeviceId, DeviceName, FileName,
ProcessCommandLine, AccountName,
InitiatingProcessFileName, ReportId// ---------------------------------------------------------
// WHAT: Custom detection rule - Suspicious PowerShell execution
// WHY: Encoded PowerShell and download cradles are the #1 delivery
// mechanism for malware payloads, including ransomware,
// Cobalt Strike beacons, and reverse shells.
// MITRE: T1059.001 (Command and Scripting Interpreter: PowerShell)
// T1105 (Ingress Tool Transfer) for download behavior.
// DETECTION LOGIC:
// Matches powershell.exe or pwsh.exe with suspicious command-line
// patterns: encoded commands (-enc), Base64 decoding, web downloads,
// or dynamic code execution (IEX/Invoke-Expression).
// EXCLUSIONS:
// SCCM (CCM) and Intune management agents are excluded because
// they legitimately use encoded PowerShell for policy delivery.
// UPDATE THESE EXCLUSIONS for your environment's management tools.
// TRUE POSITIVE: Encoded download cradle from unknown parent process.
// FALSE POSITIVE: IT automation scripts using Invoke-WebRequest.
// Check InitiatingProcessFileName for context.
// FREQUENCY: Every 1 hour.
// ---------------------------------------------------------
DeviceProcessEvents
| where Timestamp > ago(1h)
| where FileName =~ "powershell.exe" or FileName =~ "pwsh.exe"
| where ProcessCommandLine has_any (
"-EncodedCommand", "-enc ", "-e ",
"FromBase64String", "DownloadString",
"DownloadFile", "Invoke-WebRequest",
"IEX", "Invoke-Expression"
)
| where ProcessCommandLine !has "Windows\\CCM" // Exclude SCCM
| where ProcessCommandLine !has "IntuneManagement" // Exclude Intune
| project Timestamp, DeviceId, DeviceName, FileName,
ProcessCommandLine, AccountName,
InitiatingProcessFileName, ReportIdMeasure the effectiveness of your detection policies by building KQL queries that track coverage, alert volume, auto-remediation rates, and policy gaps across device groups.
// ---------------------------------------------------------
// WHAT: Detection coverage dashboard - alerts by source and severity
// WHY: Measures which detection layers are generating alerts.
// Low counts from a specific DetectionSource may indicate
// policy gaps (e.g., custom rules not triggering).
// DetectionSource values: "WindowsDefenderAv", "EDR", "CustomDetection",
// "CustomerTI", "SmartScreen", "AntimalwareScanInterface".
// CriticalPercent: High % = mature detections catching real threats.
// Low % = mostly informational noise.
// ---------------------------------------------------------
AlertInfo
| where Timestamp > ago(30d)
| summarize AlertCount = count(),
UniqueAlerts = dcount(Title),
CriticalCount = countif(Severity == "High" or Severity == "Critical"),
AutoResolved = countif(ServiceSource == "Automated investigation")
by DetectionSource
| extend CriticalPercent = round(100.0 * CriticalCount / AlertCount, 1)
| sort by AlertCount desc
// ---------------------------------------------------------
// WHAT: Detection coverage by MITRE ATT&CK tactic
// WHY: Identifies which kill chain phases your detections cover.
// Gaps in specific tactics (e.g., no Persistence detections)
// indicate you need additional custom rules for those techniques.
// LOGIC: mv-expand unpacks the AttackTechniques JSON array so each
// technique gets its own row for accurate counting.
// ---------------------------------------------------------
AlertInfo
| where Timestamp > ago(30d)
| where isnotempty(AttackTechniques)
| mv-expand tactic = parse_json(AttackTechniques)
| summarize AlertCount = count(),
UniqueDevices = dcount(AlertId)
by tostring(tactic)
| sort by AlertCount desc// ---------------------------------------------------------
// WHAT: Identify devices missing key security configurations
// WHY: Devices with protection features disabled are the most
// vulnerable nodes in your fleet. This query uses TVM
// (Threat & Vulnerability Management) assessments to find gaps.
// TABLE: DeviceTvmSecureConfigurationAssessment
// ConfigurationId reference (security posture checks):
// scid-2010 = Real-time protection enabled
// scid-2011 = PUA (Potentially Unwanted App) protection enabled
// scid-2012 = Cloud-delivered protection enabled
// scid-2013 = Tamper protection enabled
// scid-91 = Network protection enabled
// scid-92 = Controlled folder access enabled
// LOGIC: IsApplicable=1 means the check applies to this device.
// IsCompliant=0 means the device FAILS the check.
// MissingCount = how many checks this device fails.
// OUTPUT: Devices sorted by most missing configs - remediate top entries first.
// ---------------------------------------------------------
DeviceTvmSecureConfigurationAssessment
| where ConfigurationId in (
"scid-2010", // Real-time protection
"scid-2011", // PUA protection
"scid-2012", // Cloud-delivered protection
"scid-2013", // Tamper protection
"scid-91", // Network protection
"scid-92" // Controlled folder access
)
| where IsApplicable == 1
| where IsCompliant == 0
| summarize MissingConfigs = make_set(ConfigurationId),
MissingCount = count()
by DeviceId, DeviceName
| sort by MissingCount desc// ---------------------------------------------------------
// WHAT: Track custom indicator (IoC) hit rates over time
// WHY: A daily trend of IoC hits shows whether your threat
// intelligence is actively protecting against current threats.
// Flat-line = IoCs may be stale and need refreshing.
// Spike = active campaign targeting your organization.
// DetectionSource "CustomerTI" = Custom Indicators you imported.
// OUTPUT: Time chart for executive reporting.
// ---------------------------------------------------------
AlertInfo
| where Timestamp > ago(30d)
| where DetectionSource == "CustomerTI"
| summarize DailyHits = count() by bin(Timestamp, 1d)
| render timechart
// ---------------------------------------------------------
// WHAT: Top triggered custom indicators by alert title
// WHY: Identifies which specific IoCs (file hashes, IPs, URLs)
// are most actively matched. High-hit indicators confirm
// your TI is relevant. Zero-hit indicators should be reviewed
// for removal to keep the indicator list manageable.
// ---------------------------------------------------------
AlertInfo
| where Timestamp > ago(30d)
| where DetectionSource == "CustomerTI"
| summarize HitCount = count(),
AffectedDevices = dcount(AlertId)
by Title, Severity
| sort by HitCount descValidate that your detection policies catch real-world attack techniques by running safe simulations on your test devices. Each test below includes the exact script to run and the expected outcome to verify.
# ---------------------------------------------------------
# WHAT: Standard MDE detection test command (safe, no actual malware)
# WHY: Validates that the NGAV detection layer is operational.
# The command simulates a download-and-execute pattern that
# MDE's behavioral engine is designed to detect.
# DETECTION: Triggers "Microsoft Defender for Endpoint test alert"
# within 15-30 minutes in the Defender portal.
# NOTE: Connects to localhost (127.0.0.1) only - completely safe.
# ---------------------------------------------------------
powershell.exe -NoExit -ExecutionPolicy Bypass -WindowStyle Hidden `
$ErrorActionPreference='silentlycontinue'; `
(New-Object System.Net.WebClient).DownloadFile( `
'http://127.0.0.1/1.exe','C:\test-WDATP-test\invoice.exe'); `
Start-Process 'C:\test-WDATP-test\invoice.exe'
# Verify: An alert should appear in the portal within 15-30 minutes# ---------------------------------------------------------
# WHAT: Test network protection by accessing the SmartScreen test URL
# WHY: Validates that network protection blocks connections to
# known-malicious URLs across ALL browsers, not just Edge.
# The test URL is safe but classified as malicious by SmartScreen.
# EXPECTED (Block mode): Browser shows a block/warning page.
# EXPECTED (Audit mode): No block, but Event ID 1125 is logged.
# ---------------------------------------------------------
Start-Process "https://smartscreentestratings2.net"
# Verify block was recorded in the Defender event log
# Event ID 1125 = network protection triggered (block or audit)
Get-WinEvent -LogName "Microsoft-Windows-Windows Defender/Operational" |
Where-Object { $_.Id -eq 1125 } |
Select-Object -First 5 TimeCreated, Message# ---------------------------------------------------------
# WHAT: Test the custom PowerShell detection rule from Step 13
# WHY: Validates that your KQL-based custom detection rule actually
# triggers when an encoded PowerShell command is executed.
# HOW: Creates a Base64-encoded 'Write-Host "Detection test"' command
# and runs it via -EncodedCommand, which matches the detection
# rule's "-EncodedCommand" pattern.
# EXPECTED: Custom detection rule fires an alert within 1-3 hours
# (based on the rule's configured frequency).
# VERIFY: Hunting > Custom detection rules > select rule > Last run results
# ---------------------------------------------------------
$command = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes('Write-Host "Detection test"'))
powershell.exe -EncodedCommand $command
# Verify: Your custom detection rule should trigger an alert
# Check: Hunting > Custom detection rules > select rule > Last run resultsCreate a comprehensive policy documentation artifact that records every detection policy, its configuration, target scope, and validation status. This document serves as both an operational reference and an audit artifact.
EmailScanning, CloudBlockLevel) describe the feature state in plain English - this is the value to use in audit evidence._Raw columns (e.g., EmailScanning_Raw, CloudBlockLevel_Raw) hold the literal Get-MpPreference / Get-MpComputerStatus value so a SIEM or auditor can map back to PowerShell.DisableEmailScanning, so the raw value is opposite of the feature state. The report does the translation for you:
DisableEmailScanning = $false → EmailScanning = "Enabled (default)"DisableEmailScanning = $true → EmailScanning = "Disabled - NOT recommended"CloudBlockLevel = 6) are translated using Microsoft's documented integer table. The label always begins with the raw number, e.g., "6 - Zero Tolerance (block all unknown executables - MAX)".# =====================================================================
# WHAT: Export the complete Defender configuration from a device.
# WHY: Creates a comprehensive policy report for documentation,
# compliance audits, and baseline comparison across devices.
# Run on devices from each device group to verify consistency.
# OUTPUT (in the folder you choose):
# - Console: grouped tables with every integer translated to a label.
# - CSV: raw values + translated labels (for SIEM / Power BI).
# - HTML: human-readable styled report (for audit submissions).
# DOCS:
# Defender CSP integer mappings:
# https://learn.microsoft.com/windows/client-management/mdm/defender-csp
# Set-MpPreference parameter values:
# https://learn.microsoft.com/powershell/module/defender/set-mppreference
# =====================================================================
# ---------------------------------------------------------------------
# Step A. Choose the export folder. Validate it exists; if not,
# prompt to create it or to use a different path.
# ---------------------------------------------------------------------
$defaultFolder = 'C:\Temp'
$ExportFolder = Read-Host "Export folder [press Enter for $defaultFolder]"
if ([string]::IsNullOrWhiteSpace($ExportFolder)) { $ExportFolder = $defaultFolder }
if (-not (Test-Path -LiteralPath $ExportFolder)) {
Write-Warning "Folder '$ExportFolder' does not exist."
$answer = Read-Host "Create it now? (Y to create / N to enter a different path)"
if ($answer -match '^(y|yes)$') {
New-Item -ItemType Directory -Path $ExportFolder -Force | Out-Null
Write-Host "Created $ExportFolder" -ForegroundColor Green
} else {
$ExportFolder = Read-Host "Enter an existing folder path"
if (-not (Test-Path -LiteralPath $ExportFolder)) {
throw "Folder '$ExportFolder' does not exist. Aborting."
}
}
}
# ---------------------------------------------------------------------
# Step B. Collect Defender preferences and computer status.
# ---------------------------------------------------------------------
$config = Get-MpPreference
$status = Get-MpComputerStatus
# ---------------------------------------------------------------------
# Step C. Integer -> human-readable translators.
# Every numeric Defender setting has a documented meaning.
# These functions match the values published in the
# Defender CSP and Set-MpPreference documentation.
# ---------------------------------------------------------------------
function Convert-MapsReporting ($v) { switch ($v) { 0 {'0 - Disabled (no telemetry to MAPS cloud)'} 1 {'1 - Basic membership (limited telemetry)'} 2 {'2 - Advanced membership (full telemetry - RECOMMENDED)'} default {"$v - Unknown"} } }
function Convert-SampleSubmission ($v) { switch ($v) { 0 {'0 - Always prompt user (not recommended)'} 1 {'1 - Send safe samples automatically'} 2 {'2 - Never send any sample'} 3 {'3 - Send all samples automatically (RECOMMENDED for cloud protection)'} default {"$v - Unknown"} } }
function Convert-CloudBlockLevel ($v) { switch ($v) { 0 {'0 - Default (Microsoft baseline)'} 2 {'2 - High (aggressive cloud blocking)'} 4 {'4 - High+ (stronger cloud blocking, may impact perf)'} 6 {'6 - Zero Tolerance (block all unknown executables - MAX)'} default {"$v - Unknown"} } }
function Convert-AsrMode ($v) { switch ($v) { 0 {'0 - Disabled'} 1 {'1 - Block (enforce)'} 2 {'2 - Audit (log only)'} 6 {'6 - Warn (user prompt)'} default {"$v - Unknown"} } }
function Convert-Cfa ($v) { switch ($v) { 0 {'0 - Disabled'} 1 {'1 - Block (enforce)'} 2 {'2 - Audit'} 3 {'3 - Block disk-modification only'} 4 {'4 - Audit disk-modification only'} default {"$v - Unknown"} } }
function Convert-PuaProtection ($v) { switch ($v) { 0 {'0 - Disabled'} 1 {'1 - Block'} 2 {'2 - Audit'} default {"$v - Unknown"} } }
# Positive boolean: $true = the feature is ON.
# Used for properties like RealTimeProtectionEnabled, CheckForSignaturesBeforeRunningScan,
# IsTamperProtected, RandomizeScheduleTaskTimes, MeteredConnectionUpdates.
function Convert-Bool ($v) { if ($v) {'Enabled'} else {'Disabled'} }
# Inverted boolean: the property is named "DisableXxx", so $true = the feature is OFF.
# Used for properties like DisableEmailScanning, DisableArchiveScanning, etc.
# The report displays the FEATURE state (the thing you actually care about),
# and a separate "_Raw" column preserves the literal Get-MpPreference value.
function Convert-NotBool ($v) { if ($v) {'Disabled - NOT recommended'} else {'Enabled (default)'} }
# Threat default actions (per severity). 0 = Defender chooses; 1 = Clean; 2 = Quarantine;
# 3 = Remove; 6 = Allow (NOT recommended); 8 = User defined; 9 = NoAction (NOT recommended).
function Convert-ThreatAction ($v) { switch ($v) { 0 {'0 - Defender default'} 1 {'1 - Clean'} 2 {'2 - Quarantine'} 3 {'3 - Remove'} 6 {'6 - Allow (NOT recommended)'} 8 {'8 - User defined'} 9 {'9 - NoAction (NOT recommended)'} default {"$v - Unknown"} } }
# Scan parameters: 1 = Quick scan, 2 = Full scan
function Convert-ScanParameters ($v) { switch ($v) { 1 {'1 - Quick scan'} 2 {'2 - Full scan'} default {"$v - Unknown"} } }
# Schedule day: 0 Daily, 1 Sun, 2 Mon, 3 Tue, 4 Wed, 5 Thu, 6 Fri, 7 Sat, 8 Never
function Convert-ScheduleDay ($v) { switch ($v) { 0 {'0 - Daily'} 1 {'1 - Sunday'} 2 {'2 - Monday'} 3 {'3 - Tuesday'} 4 {'4 - Wednesday'} 5 {'5 - Thursday'} 6 {'6 - Friday'} 7 {'7 - Saturday'} 8 {'8 - Never'} default {"$v - Unknown"} } }
# Update channel rings (Engine / Platform):
# 0 = Not configured, 2 = Beta, 3 = Preview, 4 = Staged, 5 = Broad, 6 = Critical-Delay (48h)
function Convert-UpdateChannel ($v) { switch ($v) { 0 {'0 - Not configured (Microsoft default ring)'} 2 {'2 - Beta'} 3 {'3 - Preview'} 4 {'4 - Staged'} 5 {'5 - Broad (RECOMMENDED for production)'} 6 {'6 - Critical (48h time delay)'} default {"$v - Unknown"} } }
# Security intelligence (signature) channel: 0, 4, 5 only per Defender CSP
function Convert-SigChannel ($v) { switch ($v) { 0 {'0 - Not configured'} 4 {'4 - Staged'} 5 {'5 - Broad (RECOMMENDED)'} default {"$v - Unknown"} } }
# Behavioral network blocks ConfiguredState (BruteForce / RemoteEncryption):
# 0 = Not configured, 1 = Block, 2 = Audit, 4 = Off
function Convert-NetBlockState ($v) { switch ($v) { 0 {'0 - Not configured (engine default)'} 1 {'1 - Block'} 2 {'2 - Audit'} 4 {'4 - Off'} default {"$v - Unknown"} } }
# ---------------------------------------------------------------------
# Step D. Build a flat report record with BOTH raw values and labels.
# Storing both lets you sort/filter in CSV while keeping
# human-readable text for HTML and console output.
# ---------------------------------------------------------------------
$report = [PSCustomObject]@{
DeviceName = $env:COMPUTERNAME
ReportDate = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
# --- Core protection status (booleans) ---
# _Raw columns store the literal Get-MpComputerStatus value ($true/$false);
# the label columns translate it into the FEATURE state ('Enabled' / 'Disabled').
RealTimeProtection_Raw = $status.RealTimeProtectionEnabled
RealTimeProtection = Convert-Bool $status.RealTimeProtectionEnabled
BehaviorMonitoring_Raw = $status.BehaviorMonitorEnabled
BehaviorMonitoring = Convert-Bool $status.BehaviorMonitorEnabled
TamperProtection_Raw = $status.IsTamperProtected
TamperProtection = Convert-Bool $status.IsTamperProtected
OnAccessProtection_Raw = $status.OnAccessProtectionEnabled
OnAccessProtection = Convert-Bool $status.OnAccessProtectionEnabled
IoavProtection_Raw = $status.IoavProtectionEnabled
IoavProtection = Convert-Bool $status.IoavProtectionEnabled # Scans downloads & attachments
# --- Cloud-delivered protection ---
CloudBlockLevel_Raw = $config.CloudBlockLevel
CloudBlockLevel = Convert-CloudBlockLevel $config.CloudBlockLevel
CloudExtendedTimeout_Sec = $config.CloudExtendedTimeout # 0-50 seconds; target = 50
MAPSReporting_Raw = $config.MAPSReporting
MAPSReporting = Convert-MapsReporting $config.MAPSReporting
SampleSubmission_Raw = $config.SubmitSamplesConsent
SampleSubmission = Convert-SampleSubmission $config.SubmitSamplesConsent
# --- Network / ASR features ---
NetworkProtection_Raw = $config.EnableNetworkProtection
NetworkProtection = Convert-AsrMode $config.EnableNetworkProtection
ControlledFolderAccess_Raw = $config.EnableControlledFolderAccess
ControlledFolderAccess = Convert-Cfa $config.EnableControlledFolderAccess
PUAProtection_Raw = $config.PUAProtection
PUAProtection = Convert-PuaProtection $config.PUAProtection
# --- Behavioral network blocks (preview / production) ---
# ConfiguredState: 0=NotConfigured, 1=Block, 2=Audit, 4=Off
BruteForceProtection_Raw = $config.BruteForceProtectionConfiguredState
BruteForceProtection = Convert-NetBlockState $config.BruteForceProtectionConfiguredState
RemoteEncProtection_Raw = $config.RemoteEncryptionProtectionConfiguredState
RemoteEncProtection = Convert-NetBlockState $config.RemoteEncryptionProtectionConfiguredState
# --- Advanced scan-type settings (from configure-advanced-scan-types doc) ---
# NOTE: these source properties start with "Disable*", so the raw value is INVERTED:
# DisableEmailScanning = $false -> EmailScanning column shows 'Enabled (default)'
# DisableEmailScanning = $true -> EmailScanning column shows 'Disabled - NOT recommended'
# The _Raw column stores the literal Get-MpPreference value so an auditor can map back.
EmailScanning_Raw = $config.DisableEmailScanning
EmailScanning = Convert-NotBool $config.DisableEmailScanning # PST/MBX/DBX/MIME
ScriptScanning_Raw = $config.DisableScriptScanning
ScriptScanning = Convert-NotBool $config.DisableScriptScanning
ArchiveScanning_Raw = $config.DisableArchiveScanning
ArchiveScanning = Convert-NotBool $config.DisableArchiveScanning # .zip / .rar / nested
NetworkFileScanning_Raw = $config.DisableScanningNetworkFiles
NetworkFileScanning = Convert-NotBool $config.DisableScanningNetworkFiles # during RTP
MappedDriveScan_FullScan_Raw= $config.DisableScanningMappedNetworkDrivesForFullScan
MappedDriveScan_FullScan = Convert-NotBool $config.DisableScanningMappedNetworkDrivesForFullScan
RemovableDriveScan_Raw = $config.DisableRemovableDriveScanning
RemovableDriveScan_FullScan = Convert-NotBool $config.DisableRemovableDriveScanning
IOAV_Raw = $config.DisableIOAVProtection
DownloadAttachmentScan_IOAV = Convert-NotBool $config.DisableIOAVProtection
CatchupFullScan_Raw = $config.DisableCatchupFullScan
CatchupFullScan = Convert-NotBool $config.DisableCatchupFullScan
CatchupQuickScan_Raw = $config.DisableCatchupQuickScan
CatchupQuickScan = Convert-NotBool $config.DisableCatchupQuickScan
CheckSignaturesBeforeScan_Raw = $config.CheckForSignaturesBeforeRunningScan
CheckSignaturesBeforeScan = Convert-Bool $config.CheckForSignaturesBeforeRunningScan
# --- Scan schedule + CPU ---
ScanType_Raw = $config.ScanParameters
ScanType = Convert-ScanParameters $config.ScanParameters
ScanScheduleDay_Raw = $config.ScanScheduleDay
ScanScheduleDay = Convert-ScheduleDay $config.ScanScheduleDay
ScanScheduleTime = $config.ScanScheduleTime # TimeSpan
ScanScheduleQuickScan_Min = $config.ScanScheduleQuickScanTime # minutes past midnight
ScanAvgCPULoadFactor_Pct = $config.ScanAvgCPULoadFactor # 0-100, default 50
RandomizeScheduleTaskTimes_Raw = $config.RandomizeScheduleTaskTimes
RandomizeScheduleTaskTimes = Convert-Bool $config.RandomizeScheduleTaskTimes
SignatureUpdateInterval_Hr = $config.SignatureUpdateInterval # hours, 0 = use schedule
# --- Threat default actions (per severity) ---
SevereThreatAction = Convert-ThreatAction $config.SevereThreatDefaultAction
HighThreatAction = Convert-ThreatAction $config.HighThreatDefaultAction
ModerateThreatAction = Convert-ThreatAction $config.ModerateThreatDefaultAction
LowThreatAction = Convert-ThreatAction $config.LowThreatDefaultAction
UnknownThreatAction = Convert-ThreatAction $config.UnknownThreatDefaultAction
# --- Update rings (channels) ---
EngineUpdatesChannel_Raw = $config.EngineUpdatesChannel
EngineUpdatesChannel = Convert-UpdateChannel $config.EngineUpdatesChannel
PlatformUpdatesChannel_Raw = $config.PlatformUpdatesChannel
PlatformUpdatesChannel = Convert-UpdateChannel $config.PlatformUpdatesChannel
SignatureUpdatesChannel_Raw = $config.SignatureUpdatesChannel
SignatureUpdatesChannel = Convert-SigChannel $config.SignatureUpdatesChannel
MeteredConnectionUpdates_Raw= $config.MeteredConnectionUpdates
MeteredConnectionUpdates = Convert-Bool $config.MeteredConnectionUpdates
# --- Exclusions (full list + count - every entry weakens scanning) ---
# _Count answers "how many"; _List answers "exactly what". CSV stores
# the list as a semicolon-separated string for Excel; the HTML report
# renders each entry on its own line.
ExclusionPath_Count = @($config.ExclusionPath).Count
ExclusionPath_List = if (@($config.ExclusionPath).Count) { $config.ExclusionPath -join '; ' } else { '(none)' }
ExclusionExtension_Count = @($config.ExclusionExtension).Count
ExclusionExtension_List = if (@($config.ExclusionExtension).Count) { $config.ExclusionExtension -join '; ' } else { '(none)' }
ExclusionProcess_Count = @($config.ExclusionProcess).Count
ExclusionProcess_List = if (@($config.ExclusionProcess).Count) { $config.ExclusionProcess -join '; ' } else { '(none)' }
ExclusionIpAddress_Count = @($config.ExclusionIpAddress).Count
ExclusionIpAddress_List = if (@($config.ExclusionIpAddress).Count) { $config.ExclusionIpAddress -join '; ' } else { '(none)' }
# --- Engine and signature versions ---
AntivirusVersion = $status.AMProductVersion
EngineVersion = $status.AMEngineVersion
SignatureVersion = $status.AntispywareSignatureVersion
LastSignatureUpdate = $status.AntivirusSignatureLastUpdated
}
# ---------------------------------------------------------------------
# Step E. Display grouped tables on the console for quick review.
# ---------------------------------------------------------------------
Write-Host ""
Write-Host "=== MDE Policy Report: $($report.DeviceName) ($($report.ReportDate)) ===" -ForegroundColor Cyan
Write-Host "`n--- Core Protection ---" -ForegroundColor Yellow
$report | Select-Object RealTimeProtection,BehaviorMonitoring,TamperProtection,OnAccessProtection,IoavProtection | Format-List
Write-Host "--- Cloud-Delivered Protection ---" -ForegroundColor Yellow
$report | Select-Object CloudBlockLevel,CloudExtendedTimeout_Sec,MAPSReporting,SampleSubmission | Format-List
Write-Host "--- Network / ASR Features ---" -ForegroundColor Yellow
$report | Select-Object NetworkProtection,ControlledFolderAccess,PUAProtection,BruteForceProtection,RemoteEncProtection | Format-List
Write-Host "--- Advanced Scan Types (configure-advanced-scan-types doc) ---" -ForegroundColor Yellow
$report | Select-Object EmailScanning,ScriptScanning,ArchiveScanning,NetworkFileScanning,MappedDriveScan_FullScan,RemovableDriveScan_FullScan,DownloadAttachmentScan_IOAV,CatchupFullScan,CatchupQuickScan,CheckSignaturesBeforeScan | Format-List
Write-Host "--- Scan Schedule and CPU ---" -ForegroundColor Yellow
$report | Select-Object ScanType,ScanScheduleDay,ScanScheduleTime,ScanScheduleQuickScan_Min,ScanAvgCPULoadFactor_Pct,RandomizeScheduleTaskTimes,SignatureUpdateInterval_Hr | Format-List
Write-Host "--- Threat Default Actions (per severity) ---" -ForegroundColor Yellow
$report | Select-Object SevereThreatAction,HighThreatAction,ModerateThreatAction,LowThreatAction,UnknownThreatAction | Format-List
Write-Host "--- Update Rings (Engine / Platform / Signatures) ---" -ForegroundColor Yellow
$report | Select-Object EngineUpdatesChannel,PlatformUpdatesChannel,SignatureUpdatesChannel,MeteredConnectionUpdates | Format-List
Write-Host "--- Exclusions (full list - every entry weakens scanning) ---" -ForegroundColor Yellow
$report | Select-Object ExclusionPath_Count,ExclusionPath_List,ExclusionExtension_Count,ExclusionExtension_List,ExclusionProcess_Count,ExclusionProcess_List,ExclusionIpAddress_Count,ExclusionIpAddress_List | Format-List
Write-Host "--- Versions ---" -ForegroundColor Yellow
$report | Select-Object AntivirusVersion,EngineVersion,SignatureVersion,LastSignatureUpdate | Format-List
# ---------------------------------------------------------------------
# Step F. Export to CSV (raw + label columns) for SIEM / Power BI.
# ---------------------------------------------------------------------
$csvPath = Join-Path $ExportFolder "MDE-PolicyReport-$env:COMPUTERNAME.csv"
$report | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
# ---------------------------------------------------------------------
# Step G. Export an HTML report grouped into readable tables.
# ---------------------------------------------------------------------
$htmlPath = Join-Path $ExportFolder "MDE-PolicyReport-$env:COMPUTERNAME.html"
$style = @"
<style>
body{font-family:Segoe UI,Arial,sans-serif;margin:24px;color:#111}
h1{color:#0b6bcb} h2{color:#0b6bcb;border-bottom:1px solid #ddd;padding-bottom:4px;margin-top:24px}
table{border-collapse:collapse;width:100%;margin-bottom:16px}
th,td{border:1px solid #ccc;padding:8px 12px;text-align:left;vertical-align:top}
th{background:#f3f6fb}
tr:nth-child(even){background:#fafafa}
</style>
"@
function ToTable($title, $obj, $props) {
$rows = foreach ($p in $props) {
"<tr><th>$p</th><td>$($obj.$p)</td></tr>"
}
"<h2>$title</h2><table>$($rows -join '')</table>"
}
$html = @"
<!doctype html><html><head><meta charset='utf-8'>
<title>MDE Policy Report - $($report.DeviceName)</title>$style</head><body>
<h1>MDE Policy Report</h1>
<p><b>Device:</b> $($report.DeviceName) | <b>Generated:</b> $($report.ReportDate)</p>
$(ToTable 'Core Protection' $report @('RealTimeProtection','BehaviorMonitoring','TamperProtection','OnAccessProtection','IoavProtection'))
$(ToTable 'Cloud-Delivered Protection' $report @('CloudBlockLevel','CloudExtendedTimeout_Sec','MAPSReporting','SampleSubmission'))
$(ToTable 'Network / ASR Features' $report @('NetworkProtection','ControlledFolderAccess','PUAProtection','BruteForceProtection','RemoteEncProtection'))
$(ToTable 'Advanced Scan Types' $report @('EmailScanning','ScriptScanning','ArchiveScanning','NetworkFileScanning','MappedDriveScan_FullScan','RemovableDriveScan_FullScan','DownloadAttachmentScan_IOAV','CatchupFullScan','CatchupQuickScan','CheckSignaturesBeforeScan'))
$(ToTable 'Scan Schedule & CPU' $report @('ScanType','ScanScheduleDay','ScanScheduleTime','ScanScheduleQuickScan_Min','ScanAvgCPULoadFactor_Pct','RandomizeScheduleTaskTimes','SignatureUpdateInterval_Hr'))
$(ToTable 'Threat Default Actions' $report @('SevereThreatAction','HighThreatAction','ModerateThreatAction','LowThreatAction','UnknownThreatAction'))
$(ToTable 'Update Rings' $report @('EngineUpdatesChannel','PlatformUpdatesChannel','SignatureUpdatesChannel','MeteredConnectionUpdates'))
<h2>Exclusions (full list - every entry weakens scanning)</h2>
<table>
<tr><th>ExclusionPath ($($report.ExclusionPath_Count))</th><td>$($report.ExclusionPath_List -replace '; ', '<br>')</td></tr>
<tr><th>ExclusionExtension ($($report.ExclusionExtension_Count))</th><td>$($report.ExclusionExtension_List -replace '; ', '<br>')</td></tr>
<tr><th>ExclusionProcess ($($report.ExclusionProcess_Count))</th><td>$($report.ExclusionProcess_List -replace '; ', '<br>')</td></tr>
<tr><th>ExclusionIpAddress ($($report.ExclusionIpAddress_Count))</th><td>$($report.ExclusionIpAddress_List -replace '; ', '<br>')</td></tr>
</table>
$(ToTable 'Engine & Signatures' $report @('AntivirusVersion','EngineVersion','SignatureVersion','LastSignatureUpdate'))
</body></html>
"@
$html | Out-File -FilePath $htmlPath -Encoding UTF8
Write-Host ""
Write-Host "CSV saved to: $csvPath" -ForegroundColor Green
Write-Host "HTML saved to: $htmlPath" -ForegroundColor Green| Resource | Description |
|---|---|
| Configure endpoints onboarding to Microsoft Defender for Endpoint | Overview of onboarding methods for Windows, macOS, and Linux endpoints |
| Automated investigation and response in Microsoft Defender for Endpoint | Configure automated investigation levels and remediation actions |
| Role-based access control for Microsoft Defender for Endpoint | Set up RBAC with device group scoping for multi-tier SOC teams |
| EDR in block mode | Enable post-breach blocking even with third-party antivirus installed |
| Manage indicators | Create custom indicators for files, IPs, URLs, and certificates |
| Web content filtering | Block access to websites by category using web content filtering |
| Device control in Microsoft Defender for Endpoint | Manage removable media and USB device access policies |
| Protect security settings with tamper protection | Prevent unauthorized changes to Defender Antivirus security settings |
| Network protection | Block outbound connections to malicious domains and IP addresses |
| Custom detection rules | Build KQL-based detection rules for organization-specific threats |