Step 2: Deployment

Configure enterprise registry settings, deploy the MSI, verify scheduled tasks, and validate end-to-end telemetry upload.

1. Registry Configuration

All settings are read from a single registry key. Deploy via GPO Preferences, Intune OMA-URI, SCCM, or any registry tool.

Prefer Group Policy? Every value on this page is also available as a proper Administrative Template, with explain text, drop-down lists and range validation. Download the ADMX / ADML templates →
Registry Path
HKLM\SOFTWARE\Policies\MDE-Toolkit

1.1 Background Collector

Value NameTypeDefaultDescription
EnableBackgroundCollectorDWORD01 = enable, 0 = off
CollectionIntervalMinutesDWORD480Minutes between collections (min 5)
CacheFolderSZ%ProgramData%\MDE-Toolkit\cacheLocal cache folder
MaxCacheFilesDWORD48Rolling file count
CacheRetentionDaysDWORD30Delete older than N days (0 = no limit)

1.2 Upload: Function App

Value NameTypeDefaultDescription
EnableUploadDWORD01 = enable upload
FunctionAppUrlSZe.g. https://func-mde-toolkit.azurewebsites.net/api/HealthReportIngestion
FunctionAppAudienceSZe.g. api://mde-toolkit-func
UploadIntervalMinutesDWORD480Upload frequency (min 5)
UploadTimeoutSecondsDWORD30HTTP POST timeout (5–300)
UploadRetryCountDWORD2Retries on failure (0–10)

1.3 Upload: Direct Blob Storage (Alternative)

Value NameTypeDefaultDescription
EnableBlobUploadDWORD01 = enable blob upload
BlobStorageAccountNameSZStorage account name
BlobContainerNameSZmde-telemetryContainer name
BlobEndpointSuffixSZe.g. blob.core.usgovcloudapi.net

1.4 Identity / Authentication

Value NameTypeDefaultDescription
TenantIdSZ(auto)Auto-detected from PRT if blank
PreferredAuthMethodSZDefaultDefault (WAM broker, then non-interactive fallbacks) | WAM | DeviceCode | ManagedIdentity. Scheduled uploads never display a sign-in prompt.
AuthClientIdSZ(auto)Optional, but recommended for production. App (client) ID of the public client registration used for WAM/broker sign-in. When set, it is used on its own. When blank, the app ID inside FunctionAppAudience is tried first — which is already correct for the common setup where one registration both exposes the API and acts as the client — then the built-in Azure CLI client. Leaving it blank can mean your API is reached through Microsoft's own registration, so anyone with the Azure CLI can request tokens for it and Conditional Access cannot target an app you control. Whichever registration is used needs redirect URI ms-appx-web://microsoft.aad.brokerplugin/<client-id> registered under "Mobile and desktop applications", public client flows enabled, access to the exposed scope, and — if blob upload is enabled — a delegated user_impersonation permission on Azure Storage.
AzureEnvironmentSZPublicPublic | USGov | China | Germany
AuthorityHostOverrideSZ(auto)Custom Entra authority URI for sovereign / air-gapped clouds (e.g. https://login.microsoftonline.us/)

1.5 Data Scope

Control which telemetry categories are collected. All DWORD, 1 = on, 0 = off.

Value NameDefaultDescription
CollectDefenderStatus1RTP, Tamper, Signatures, engine + platform versions, scan times
CollectDefenderPolicies1ASR, NP, CFA, Device Control
CollectFirewallStatus1Domain/Private/Public profiles
CollectAppControl1WDAC status + .cip policy count
CollectDeviceGuard1VBS, HVCI, Credential Guard
CollectIntuneEnrollment1Intune / ConfigMgr info

1.6 Tagging / Organization

These values are written to the JSON snapshot and promoted to their own columns (DeviceTag, OrgUnit, Environment) in the Azure Table so Power BI can slice on them.

Value NameTypeDescription
DeviceTagSZFreeform label — e.g. Finance
OrgUnitSZe.g. US-East
EnvironmentSZProduction | Staging | Dev

1.7 UI / Feature Flags

Control the interactive WPF application (not the background collector). All DWORD, default 0.

Value NameDescription
DisableExportButtonsBlock every export path — report PDF/HTML, triage report, scan results, ASR event CSV, Advanced Hunting CSV/XML, WDAC XML, firewall migration JSON, investigation CSV. Blocked attempts show an explanatory dialog.
DisableRemoteTargetPin the toolkit to the local machine. The Target box is disabled and any non-local value is rejected, so no remote-capable service can be reached.
ForceReadOnlyModeDisable all write operations (remediation, policy push, ASR exclusions)
ShowPreviewFeaturesReveal features still marked Preview, currently Machine Investigator. Preview features are functional but may change between releases — consider a pilot group first.

Removed in 3.3.2. Seven values were previously accepted but never acted on, so setting them had no effect: CollectFirewallRules, CollectWfpFilters, CollectDeviceControl, CollectNetworkInfo, CollectSecurityScore, DisableAiFeatures and DisableAdvancedNetworking. The installer no longer writes them and the app ignores them if present — delete them so the policy key reflects only settings that are live.

To hide the AI or Networking surfaces, use VisibilityAiAnalysisPage and VisibilityNetworkingPage (section 1.9), which are enforced. DisableExportButtons and DisableRemoteTarget were in the same non-functional state and are now genuinely enforced rather than removed.

1.8 ASR Exclusion Guardrails

Restrict what administrators may create from ASR Rules → Events. All values are optional; a device with none of them set behaves as before (4h / 24h / 7d / 30d plus Permanent).

Value NameTypeDefaultDescription
AsrAllowPermanentExclusionsDWORD10 removes Permanent, forcing every exclusion to expire
AsrAllowTemporaryExclusionsDWORD10 removes all temporary durations
AsrExclusionMaxHoursDWORD0Hard cap in hours; 0 = uncapped. Applied last, so it cannot be widened by the list below
AsrExclusionDurationsHoursSZ4,24,168,720Duration menu in hours, comma-separated. Unparseable entries are ignored and reported
Example — temporary exclusions only, 4 hours maximum
reg add "HKLM\SOFTWARE\Policies\MDE-Toolkit" /v AsrAllowPermanentExclusions /t REG_DWORD /d 0 /f
reg add "HKLM\SOFTWARE\Policies\MDE-Toolkit" /v AsrExclusionMaxHours /t REG_DWORD /d 4 /f
Guardrails, not a boundary. These prevent accidental long-lived exclusions and configuration drift. They do not stop a determined local administrator, who can call Add-MpPreference directly — the toolkit's audit ledger is what makes that visible. Setting ForceReadOnlyMode to 1 blocks every exclusion write, both creation and removal.

1.9 Page Visibility

Show or hide individual pages in the interactive application, per elevation level. All DWORD.

ValueMeaning
0Hidden for everyone
1Visible only when the app is running elevated
2Visible only to standard (non-elevated) users
3Visible in both cases
Value NameDefaultPage
VisibilityAsrDashboardPage1ASR Rules overview
VisibilityAsrEventsPage1ASR Rules → Events. 3 gives standard users read-only triage; exclusion actions stay admin-gated regardless
VisibilityMdePoliciesPage1Defender policies
VisibilityFirewallPage1Firewall
VisibilityWfpFiltersPage1WFP filters
VisibilityFwMigrationPage1Firewall migration
VisibilityDCPoliciesPage1Device Control policies
VisibilityCiToolPage1CiTool integration
VisibilityAppControlAdvancedHuntingPage1App Control → Advanced Hunting
VisibilityMachineInvestigatorPage1Machine Investigator. Also requires ShowPreviewFeatures = 1
VisibilityNetworkingPage1Advanced networking
VisibilityAiAnalysisPage0AI Analysis. Hidden by default because it sends configuration data to an AI endpoint you configure
Hiding a page is a UI decision, not an access control. It removes the page from navigation; it does not prevent the underlying Windows functionality being used by other means. Pair it with ForceReadOnlyMode where the intent is to prevent change.

1.10 Logging

Local service log written by the background collector and upload loops.

Value NameTypeDefaultDescription
LogLevelSZInfoNone | Error | Warning | Info | Verbose
LogFilePathSZ(CacheFolder)\service.logCustom log path
MaxLogSizeMbDWORD10Rotate at this size (1–500 MB)

1.11 Example: GPO / Intune Deployment

PowerShell — Set registry for enterprise collection + upload
$regPath = "HKLM:\SOFTWARE\Policies\MDE-Toolkit"
New-Item -Path $regPath -Force | Out-Null

Set-ItemProperty $regPath -Name EnableBackgroundCollector -Value 1 -Type DWord
Set-ItemProperty $regPath -Name EnableUpload             -Value 1 -Type DWord
Set-ItemProperty $regPath -Name FunctionAppUrl      -Value "https://func-mde-toolkit.azurewebsites.net/api/HealthReportIngestion"
Set-ItemProperty $regPath -Name FunctionAppAudience -Value "api://mde-toolkit-func"
Set-ItemProperty $regPath -Name AzureEnvironment -Value "Public"
Set-ItemProperty $regPath -Name DeviceTag -Value "Finance"
Set-ItemProperty $regPath -Name OrgUnit   -Value "US-East"
Set-ItemProperty $regPath -Name CacheRetentionDays -Value 30 -Type DWord

1.12 New Table Columns (v3.2+)

Recent client + Function App updates added the following columns to each row upserted into Azure Table Storage. Existing Power BI reports do not need to change — new columns simply become available for slicing.

ColumnSourceNotes
DeviceTag, OrgUnit, EnvironmentRegistry (section 1.6)Promoted from JSON body to top-level columns
IpAddressFirst non-loopback IPv4NetworkInterface enumeration
LastBootTimeUtcEnvironment.TickCount64ISO 8601 UTC
LastQuickScanUtc, LastFullScanUtcMSFT_MpComputerStatus age fields, registry fallbackISO 8601 UTC
AntivirusEngineVersionAMEngineVersion, filesystem fallbackMpEngine.dll version
PlatformVersionAMProductVersion, registry & folder-name fallbacksDefender platform version
SignatureVersionCIM + AVSignatureVersion registryDefinition version
ServiceVersionAMServiceVersionDefender service version
MdeOrgIdRegistry Windows Advanced Threat Protection\Status\OrgIdEmpty if not onboarded
AppControlPolicyCountC:\Windows\System32\CodeIntegrity\CIPolicies\Active\*.cipFile count, not enforcement
AppControlStatusKMCI + policy countEnforced | Audit | Configured (N policies, not enforced) | NotConfigured
PowerShellExecutionPolicyMachine policy → per-shell → defaultRestricted / RemoteSigned / AllSigned / Bypass / etc.
SmartScreenExplorerModeRegistry Explorer\SmartScreenEnabledRaw string (RequireAdmin, Warn, Off)
Azure Table Storage is schemaless per row. Existing rows keep their original columns — only newly upserted rows carry the new fields.

1.13 Fields fixed in v3.3.3

These columns existed in the schema but were never populated by the collector: they were uploaded at their default value on every device. A dashboard reading them saw false or 0 and could not tell that apart from a real measurement.

ColumnWasNow
ExploitProtectionDepEnabled, ExploitProtectionAslrEnabled, ExploitProtectionCfgEnabled Always false — never read Decoded from MitigationOptions under Session Manager\Kernel. A read failure now sets ExploitProtectionStatus to Unknown rather than leaving false to look like a measurement.
LocalAdminCount Always 0, which is impossible — every install has the built-in Administrator Counted via NetLocalGroupGetMembers resolved by well-known SID S-1-5-32-544, so it works on non-English installs. LocalAdminStatus now reflects the count, not just LAPS.
AppControlEnforcementMode Not written at all Enforced | Audit | Deployed | NotConfigured. Deployed means .cip policies are present but Code Integrity is not running them.
LastQuickScanUtc, LastFullScanUtc Blank when the scan ran today The age test was > 0; a scan that ran today has age 0, so the freshest devices reported as never scanned. Now >= 0.
Existing rows are not backfilled. Devices must upload at least once on 3.3.3 before these columns mean anything, and rows written by older agents keep their misleading defaults. Slice on ServiceVersion to see how far the rollout has reached, and consider deleting pre-3.3.3 rows if the false zeros would skew a report.

2. MSI Deployment

The MDE Toolkit ships as a WiX v5 MSI installer, suitable for SCCM, Intune, or GPO.

What the MSI Does

  • Installs to C:\Program Files\MDE-Toolkit\ (self-contained .NET 8)
  • Creates C:\ProgramData\MDE-Toolkit\cache\
  • Writes HKLM\SOFTWARE\MDE-Toolkit with Version and InstallPath
  • Creates scheduled task MDE Toolkit Collect (SYSTEM, every 8h)
  • Creates scheduled task MDE Toolkit Upload (interactive user, every 8h)
  • Seeds every policy value under HKLM\SOFTWARE\Policies\MDE-Toolkit at its built-in default, so each setting is visible in regedit and can be edited rather than created. Existing values are never overwritten, so GPO/Intune settings and manual edits survive upgrades. Because each seeded value equals the code default, a freshly installed device behaves identically to one with no policy key; deleting a value simply restores its default.
  • Adds desktop and Start Menu shortcuts

Silent Install

Command Line
msiexec /i MDE-Toolkit-3.4.0.msi /qn
msiexec /i MDE-Toolkit-3.4.0.msi /qn /l*v install.log

# Intune detection: HKLM\SOFTWARE\MDE-Toolkit\Version exists

3. Scheduled Tasks

The MSI creates two tasks. Both use -StartWhenAvailable so missed runs catch up when the device comes back online. A third task is registered at runtime — see 3.4.

Offline resilience. If the machine is asleep or off at the scheduled time, Windows runs the task as soon as it wakes. One catch-up run fires — correct for telemetry.

3.1 Collect (SYSTEM)

PowerShell equivalent
$action    = New-ScheduledTaskAction `
  -Execute '"C:\Program Files\MDE-Toolkit\MDE Monitoring App.exe"' `
  -Argument '--collect --no-upload'
$trigger   = New-ScheduledTaskTrigger -Once -At (Get-Date) `
  -RepetitionInterval (New-TimeSpan -Hours 8)
$principal = New-ScheduledTaskPrincipal `
  -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
$settings  = New-ScheduledTaskSettingsSet `
  -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
Register-ScheduledTask -TaskName 'MDE Toolkit Collect' `
  -Action $action -Trigger $trigger -Principal $principal `
  -Settings $settings -Force

3.2 Upload (Interactive User)

PowerShell equivalent
$action    = New-ScheduledTaskAction `
  -Execute '"C:\Program Files\MDE-Toolkit\MDE Monitoring App.exe"' `
  -Argument '--upload'
$trigger   = New-ScheduledTaskTrigger -Once -At (Get-Date) `
  -RepetitionInterval (New-TimeSpan -Hours 8)
$principal = New-ScheduledTaskPrincipal `
  -GroupId 'BUILTIN\Users' -RunLevel Highest
$settings  = New-ScheduledTaskSettingsSet `
  -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
Register-ScheduledTask -TaskName 'MDE Toolkit Upload' `
  -Action $action -Trigger $trigger -Principal $principal `
  -Settings $settings -Force

3.3 Settings Summary

SettingCollectUpload
Run asSYSTEMBUILTIN\Users (interactive)
IntervalEvery 8 hoursEvery 8 hours
StartWhenAvailable
AllowStartIfOnBatteries
WakeToRun
No GUI. Both modes run headless. The WPF window is never created.
User must be logged on. Upload uses interactive logon — deferred via StartWhenAvailable if no session is active.

3.4 ASR Exclusion Sweep (registered at runtime)

\MDE Toolkit\ASR Exclusion Sweep is not created by the MSI. It is registered the first time an administrator creates a temporary ASR exclusion, and runs as SYSTEM every 15 minutes. Defender has no native expiry for exclusions, so this task is what reverts them when the toolkit itself is closed.

SettingValue
Task path\MDE Toolkit\ASR Exclusion Sweep
Run asSYSTEM (highest privileges)
IntervalEvery 15 minutes
Script%ProgramData%\MDE-Toolkit\asr-exclusion-sweep.ps1
Ledger%ProgramData%\MDE-Toolkit\asr-exclusions.json
CreatedOn first temporary exclusion (not at install)
RemovedAutomatically on uninstall
Uninstall behaviour. Removing the product reverts still-active temporary exclusions first, then deletes the task and its script. Permanent exclusions are deliberately left in place — an administrator asked for those — and the ledger JSON is preserved as an audit record. Review the Exclusions tab before uninstalling if you want a clean slate.
Directory is hardened. %ProgramData%\MDE-Toolkit is ACL'd to SYSTEM and Administrators with standard users read-only, because the sweep script executes as SYSTEM. If the directory cannot be secured, the toolkit refuses to register the task rather than create an escalation path.

4. Verification

4.1 Test Collection

PowerShell (Admin)
& "C:\Program Files\MDE-Toolkit\MDE Monitoring App.exe" --collect --no-upload
Get-Content "C:\ProgramData\MDE-Toolkit\cache\latest.json" | ConvertFrom-Json | Select hostname, healthScore, overallStatus

4.2 Test Upload

PowerShell (logged-on user)
& "C:\Program Files\MDE-Toolkit\MDE Monitoring App.exe" --upload
Get-Content "C:\ProgramData\MDE-Toolkit\cache\service.log" -Tail 10
If the upload fails with “Silent sign-in unavailable”, the log names the account Windows is signed in as and the tenant a token was requested for. When the device is not Entra joined to the tenant that owns the Function App, no registry setting can fix it — there is no primary refresh token to redeem. Sign in once as the user the upload task runs as:
PowerShell (logged-on user)
& "C:\Program Files\MDE-Toolkit\MDE Monitoring App.exe" --auth-login
This prompts once, acquires consent for every configured upload target, and caches the account in the user's profile so scheduled uploads refresh silently. It is the only mode that will ever display a sign-in window, and it is per-user: another user signing in to the same machine needs to run it too. Devices joined to the correct tenant do not need it at all.

4.3 Verify Tasks

PowerShell
Get-ScheduledTask -TaskName "MDE Toolkit*" | Format-Table TaskName, State, @{N='RunAs';E={$_.Principal.GroupId ?? $_.Principal.UserId}}

4.4 Verify Table Row

Azure CLI
az storage entity query \
  --table-name HealthReports \
  --account-name stmdetoolkit \
  --filter "Hostname eq 'YOUR-PC-NAME'"

4.5 Expected Output

CheckExpected
Exit code0
latest.jsonExists with hostname, healthScore
Service logFunction App upload succeeded (200)
Table rowHealthScore > 0
RegistryHKLM\SOFTWARE\MDE-Toolkit\Version = 3.4.0