Microsoft Defender for Endpoint — Integration Setup
Note: This guide covers Microsoft Defender for Endpoint (EDR) only. For Microsoft Defender for Cloud (Azure Security Center / cloud security posture management), see integration-setup-azure-defender.md instead.
Prerequisites
- Microsoft 365 E5 or Defender for Endpoint Plan 2 license
- An Entra ID account that can grant admin consent (see below for which role, per deployment)
- CMDash Essentials tier or above
How CMDash connects
Defender for Endpoint uses CMDash's Microsoft app registration with application permissions and an app-only token (the client-credentials grant) — no refresh token, no signed-in user. Which directory is read depends on how the deployment runs its Microsoft integrations; see How CMDash connects to Microsoft.
- CMDash SaaS (per directory): available once CMDash's Microsoft apps are registered. Until then Connect answers that the Microsoft app registration is not set up on this deployment. Once it is, Integrations → Microsoft Defender → Connect, sign in as an active Global Administrator or Privileged Role Administrator, and accept the permissions for your organisation; the connection is bound to your directory. Everything CMDash checks and records is described once, in the Entra ID guide. Defender for Endpoint is commercial cloud only for now (not GCC High or DoD).
- Self-hosted (single directory): Steps 1–4 below.
Step 1 (self-hosted): Register an Azure AD App
- Go to Azure Portal → App registrations
- Click New registration
- Name:
CMDash Integration - Supported account types: Single tenant
- Redirect URI: leave blank — a single-directory connection has no sign-in step, so nothing is redirected.
- Click Register
If you already registered an app for another CMDash Microsoft integration (Entra ID, Intune, Purview, Sentinel, Defender for Cloud), reuse it: they all share the one registration.
Step 2 (self-hosted): Configure API Permissions
- In the app registration, go to API permissions → Add a permission
- Select APIs my organization uses → search for WindowsDefenderATP
- Choose Application permissions and add:
Machine.Read.All— Read machine informationVulnerability.Read.All— Read vulnerability dataAlert.Read.All— Read security alerts
- Click Grant admin consent (needs an administrator who can consent to application permissions)
Step 3 (self-hosted): Create a Client Secret
- Go to Certificates & secrets → New client secret
- Description:
CMDash - Expiry: 24 months (set a calendar reminder to rotate)
- Copy the Value immediately — it won't be shown again
Step 4 (self-hosted): Configure the deployment, then connect
The credentials are server settings, not something typed into the connect dialog. The app has no field for a tenant id, client id or secret.
- Set
AZURE_TENANT_ID(Azure AD → Overview → Directory (tenant) ID),AZURE_CLIENT_ID(the app registration's Application (client) ID) andAZURE_CLIENT_SECRET(the secret value from Step 3) in the API's environment, leaveAZURE_MULTI_TENANTunset, and restart the API. - In CMDash, navigate to Integrations, find Microsoft Defender and click Connect. CMDash requests a token server-to-server, checks it against the Defender API, and marks the connection active — or unhealthy, with the reason, if the check fails.
What Syncs
| Data Type | Defender API | Sync Frequency |
|---|---|---|
| Endpoints/Machines | /api/machines | Every 4 hours by default (configurable per connection) |
| Vulnerabilities | /api/vulnerabilities | Every 4 hours by default (configurable per connection) |
| Security Alerts | /api/alerts | Every 4 hours by default (configurable per connection) |
Troubleshooting
- 401 Unauthorized: Self-hosted: verify the client secret behind
AZURE_CLIENT_SECREThasn't expired; re-grant admin consent. SaaS: the CMDash app was removed from your directory — reconnect. - 403 Forbidden: Ensure the required API permissions are granted with admin consent. On SaaS, right after a fresh consent, Microsoft can take a few minutes to apply it: CMDash re-checks an Unhealthy connection every 30 minutes with a newly minted token and marks it Active once the check passes, after which scheduled syncs resume. Sync now retries sooner, each time with a newly minted token; if a sync has already failed on it, it stays Unhealthy until a sync succeeds.
- A SaaS connect was refused (administrator role required, different directory, wrong cloud, session mismatch): see the Entra ID guide's troubleshooting — it is the same flow.
- No data after sync: Check that Defender has onboarded devices; the API returns nothing if no devices are enrolled
- Connection shows UNHEALTHY: The health monitor checks every 30 minutes; if unhealthy for 6+ hours, verify credentials and re-authorize
Rate Limits & Sync Behavior
| Setting | Value |
|---|---|
| Sync frequency | Every 4 hours by default (syncInterval, in seconds, is configurable per connection; default 14400) |
| First sync | Historical data pulled on initial connection (machines, vulnerabilities, and alerts visible to the registered app's permissions) |
| Rate limit handling | Every request (including the health check) is paced through CMDash's shared token-bucket limiter, 1,500 requests/minute per tenant for this provider; the three fetches share that budget |
| Pagination | @odata.nextLink, up to 10 pages per dataset per sync |
| Vendor error (including 429) | Fails that sync attempt; the job queue retries it |
| Failed sync retry | Job queue retries 3 times with exponential backoff (starting at 30s) — see docs/developers/dead-letter.md |
Credential Rotation
- CMDash SaaS: there is nothing on your side to rotate; CMDash rotates its own app's secret.
- Self-hosted: Azure AD client secrets have a maximum lifetime of 24 months. The secret lives in the server's configuration (
AZURE_CLIENT_SECRET), not on the connection, and CMDash does not warn before it expires — keep the calendar reminder.
To rotate before expiry (self-hosted):
- Go to Azure Portal → App registrations and open the
CMDash Integrationapp. - Go to Certificates & secrets → New client secret.
- Give it a description (e.g.,
CMDash 2026) and set the expiry (max 24 months). - Click Add and immediately copy the Value — it will not be shown again.
- Update
AZURE_CLIENT_SECRETin the API's environment and restart the API. - Once a sync succeeds, return to Azure and delete the old secret.
The secret is shared by every CMDash Microsoft integration, so rotating it affects all of them at once.
Multiple Instances / Multi-Site
A CMDash workspace holds one Defender for Endpoint connection, bound to one directory. If your organisation has several Azure AD tenants (subsidiaries, a staging tenant), each needs its own CMDash workspace: on CMDash SaaS each workspace connects its own directory; a single-directory self-hosted install reads only AZURE_TENANT_ID, so a second directory needs the per-directory mode (AZURE_MULTI_TENANT=true, see the Entra ID guide) or its own deployment. Sovereign-cloud (GCC High, DoD) tenants are not supported by this integration yet.
Compliance Framework Coverage
The Microsoft Defender for Endpoint integration provides evidence for the following controls. Coverage is automatically mapped when data is synced. Because the integration uses Azure AD (Entra ID) app registration, it also contributes identity-related evidence.
| Framework | Control | Description |
|---|---|---|
| PCI DSS | 10.2 | Event monitoring — Defender alerts and machine activity logs |
| PCI DSS | 11.5 | Intrusion detection — Defender threat alerts mapped to IDS/IPS evidence |
| HITRUST | 09.ab | Monitoring system use — endpoint telemetry and alert events |
| HITRUST | 09.v | Information security incident management — alert incidents and response actions |
| CMMC L2 | SI.1.210 | System monitoring — continuous endpoint monitoring via Defender sensors |
| CMMC L2 | IA.1.076 | Identity management — Azure AD (Entra ID) app registration and service principal identity controls |
| SOC 2 | CC6.1 | Access controls — Azure AD authentication and app permission scoping via admin consent |
| SOC 2 | CC7.1 | Threat detection — Defender alerts and vulnerability findings feed the threat-detection evidence bucket |