A self-hosted, real-time Microsoft 365 security monitoring dashboard that aggregates alerts from Defender XDR, Entra ID Protection, Intune, Exchange Online, Compliance, and more — all in one place.

No third-party SaaS required. Runs entirely on your own Windows host using Microsoft Graph API.

---dashboard_redacted

Features

Pages & Monitoring Coverage

Page What it monitors
Overview Secure Score trend, KPI tiles, recent alerts, risky users, compliance summary, alert policy status
Identity Risky users, risky sign-ins, risk detections, MFA coverage, PIM assignments, MDI alerts, foreign sign-ins
Devices Intune compliance, non-compliant devices, stale devices, MDE endpoint alerts
Email MDO protection alerts by category (malware, phish, spam), threat breakdown
Incidents & Alerts Unified Defender XDR incidents + alerts, severity KPI tiles, date-range filter
Compliance DLP alerts, MCAS alerts, IRM insider risk alerts, attack simulations
Alert Center Custom alert policies, policy templates, triggered alert history, acknowledge/resolve workflow
Service Health M365 service advisories and incidents, per-service health status
M365 Connectivity Sign-in health, connectivity issues
Licenses & Users License SKU breakdown, inactive users, expiring licenses
Conditional Access Policy list, state breakdown (Enabled/Report-only/Disabled), per-policy detail
Audit Log Unified audit log with category filter, actor/target detail
Sign-in Locations Geographic sign-in map, success/failure breakdown, country drill-down

Enterprise Features

  • Alert Policy Engine — define custom policies (MFA drop, risky user spike, device breach) with thresholds; auto-evaluates against live data and tracks triggered alerts in browser localStorage
  • 9 Pre-built Alert Templates — one-click templates for common security scenarios
  • Detail Modals — click any alert, user, device, or policy to see all available fields and a direct "View in M365 Portal →" deep link
  • Search, Filter, Sort, Export — every page has full-text search, dropdown filters, sortable columns, and CSV export
  • Saved Filter Presets — save and reload custom filter combinations per page (localStorage)
  • Dark Mode — full dark/light theme toggle, persisted across sessions
  • Collapsible Sidebar — icon-only collapsed mode with hover tooltips
  • Toast Notifications — on export, preset save, policy actions
  • Sticky Filter Bars — filter controls stay visible while scrolling long lists
  • Responsive Layout — collapses to single-column below 900px

Stack

Layer Technology
Backend ASP.NET Core 8 Minimal API
Frontend React 18 + TypeScript + Vite
Auth Microsoft Graph — Client Credentials (app-only)
Scheduler .NET BackgroundService — every 15 minutes
Storage SQL Server Express (alerts + collection runs)
Icons lucide-react

Prerequisites

  1. Windows host (Windows 10/11 or Windows Server 2019+)
  2. .NET 8 SDK or ASP.NET Core 8 Hosting Bundle
  3. SQL Server Express (free)
  4. Node.js 20+

Microsoft Entra App Registration

Create the app

  1. Go to Entra admin centerApp registrationsNew registration
  2. Name it (e.g. M365SecurityDashboard)
  3. Select Accounts in this organizational directory only
  4. No redirect URI needed
  5. Click Register
  6. Note the Tenant ID and Application (client) ID
  7. Go to Certificates & secretsNew client secret — note the secret value immediately

Required API permissions (Application, not Delegated)

Grant admin consent for all of these:

Permission Used for
SecurityAlert.Read.All Defender XDR alerts
SecurityIncident.Read.All Defender XDR incidents
IdentityRiskyUser.Read.All Entra ID risky users
IdentityRiskEvent.Read.All Risk detections
AuditLog.Read.All Sign-in logs, audit logs
Reports.Read.All MFA registration, auth methods
DeviceManagementManagedDevices.Read.All Intune devices
ServiceHealth.Read.All M365 service health
Policy.Read.All Conditional Access policies
Directory.Read.All Users, groups, PIM
PrivilegedAccess.Read.AzureAD PIM assignments
ThreatHunting.Read.All Advanced hunting / MDI
UserAuthenticationMethod.Read.All MFA method details

Some features (IRM, Attack Simulation, Identity Health) require additional Purview/Defender licensing in your tenant. The dashboard gracefully shows a permission error card for unavailable features.


Setup

1. Clone and configure secrets

git clone https://github.com/YOUR_ORG/m365-security-dashboard.git
cd m365-security-dashboard

cd src\M365SecurityDashboard.Api
dotnet user-secrets init
dotnet user-secrets set "Graph:TenantId"     "YOUR_TENANT_ID"
dotnet user-secrets set "Graph:ClientId"     "YOUR_CLIENT_ID"
dotnet user-secrets set "Graph:ClientSecret" "YOUR_CLIENT_SECRET"

Never put real credentials in appsettings.json — use User Secrets for development and environment variables or appsettings.Production.json (gitignored) for production.

2. Set up the database

# Option A: let the API auto-create on first run (requires db-create rights)
# Option B: pre-create manually
sqlcmd -S .\SQLEXPRESS -E -i .\database\schema.sql

3. Build the frontend

cd src\m365-security-dashboard-client
npm install
npm run build
Copy-Item -Recurse -Force .\dist\* ..\M365SecurityDashboard.Api\wwwroot\

4. Run the API

cd src\M365SecurityDashboard.Api
$env:ASPNETCORE_ENVIRONMENT = "Development"
dotnet run

Open http://localhost:5000


Development (hot-reload)

Run both simultaneously:

# Terminal 1 — backend
cd src\M365SecurityDashboard.Api
$env:ASPNETCORE_ENVIRONMENT = "Development"
dotnet watch run

# Terminal 2 — frontend
cd src\m365-security-dashboard-client
npm run dev

Frontend dev server: http://localhost:5173 (proxies API calls to backend)


Production Deployment (Windows Service)

# 1. Build frontend
cd src\m365-security-dashboard-client
npm install && npm run build
Copy-Item -Recurse -Force .\dist\* ..\M365SecurityDashboard.Api\wwwroot\

# 2. Publish API
cd ..\M365SecurityDashboard.Api
dotnet publish -c Release -o C:\Apps\M365SecurityDashboard

# 3. Create appsettings.Production.json in publish folder
# (see template below — this file is gitignored)

# 4. Install as Windows Service
sc.exe create M365SecurityDashboard `
  binPath= "C:\Apps\M365SecurityDashboard\M365SecurityDashboard.Api.exe --environment Production --urls http://localhost:8080" `
  start= auto
sc.exe start M365SecurityDashboard

appsettings.Production.json template (create this file manually, never commit it):

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.\\SQLEXPRESS;Database=M365SecurityDashboard;Trusted_Connection=True;Encrypt=False"
  },
  "Graph": {
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_CLIENT_ID",
    "ClientSecret": "YOUR_CLIENT_SECRET",
    "CollectionIntervalMinutes": 15,
    "DevicesNotCheckedInDays": 7,
    "SignInLookbackHours": 24
  }
}

API Reference

Method Endpoint Description
GET /api/dashboard/overview Aggregated overview data
GET /api/dashboard/identity Identity & MFA data
GET /api/dashboard/devices Intune device compliance
GET /api/dashboard/email MDO email alerts
GET /api/dashboard/compliance DLP/MCAS/IRM alerts
GET /api/dashboard/incidents Defender XDR incidents
GET /api/dashboard/mdi-alerts Microsoft Defender for Identity alerts
GET /api/dashboard/mcas-alerts Defender for Cloud Apps alerts
GET /api/dashboard/insider-risk Insider Risk Management alerts
GET /api/dashboard/risk-detections Entra ID risk detections
GET /api/dashboard/identity-health Identity health issues
GET /api/dashboard/attack-simulation Attack simulation results
GET /api/dashboard/service-health M365 service health
GET /api/dashboard/licenses License SKU usage
GET /api/dashboard/conditional-access CA policies
GET /api/dashboard/audit-log Unified audit log
GET /api/dashboard/sign-ins Sign-in locations
POST /api/collector/run Trigger manual data collection
GET /api/collector/runs Collection run history

Security & Maturity

Read this before relying on Vigil365. This is an open-source read-only visibility aggregator, currently beta. It surfaces signals that already exist across your Microsoft 365 admin centers in one place. It is not a replacement for native Microsoft security tooling (Defender XDR, Entra ID Protection, Purview), and it does not make security decisions or change configuration for you. Treat its output as a convenience view, verify findings in the source portal before acting, and do your own review of the code before deploying it in a sensitive environment.

What is in scope by design

  • Read-only, least privilege. Every Graph permission requested is *.Read.All. The app cannot modify users, devices, policies, or tenant settings even if the host is compromised.
  • No remediation automation. "View in M365 Portal →" links only deep-link you to the correct blade. The app never tells you what to change and never makes changes — remediation stays in Microsoft's tooling where it belongs.
  • No inbound exposure by default. The API binds to localhost. Remote access requires you to deliberately open a firewall port (and you should front it with TLS + auth if you do).
  • App-only client-credentials flow via MSAL (Azure.Identity). Standard Microsoft auth, not a homegrown scheme. All Graph traffic is HTTPS/TLS.

How credentials and secrets are handled

  • The Graph client secret is never committed to source. Use .NET User Secrets (dev) or appsettings.Production.json / environment variables (prod, both gitignored).
  • Notification secrets stored in the database (SMTP password, Teams/Slack & generic webhook URLs) are encrypted at rest with the Windows Data Protection API (DPAPI), machine scope — a leaked database row cannot be decrypted on another machine. Secrets are decrypted only in memory at send time and the SMTP password is never returned by the API.
  • Recommended: use certificate-based authentication instead of a client secret for production (planned/optional). A non-exportable certificate in the Windows cert store removes the plaintext shared secret entirely. (Not yet wired into the app — track this in Issues.)

Host hardening checklist (your responsibility)

The security of this app is only as good as the box it runs on. Before production use:

  • Run on a dedicated, patched, hardened Windows host — not a shared workstation or a machine that handles untrusted input
  • Run the service under a dedicated low-privilege service account, not an admin or your own login
  • Enable BitLocker / full-disk encryption so the database and secrets are protected at rest
  • Keep the host off the public internet; access the dashboard over the LAN/VPN only
  • If you must expose it, put it behind a reverse proxy with TLS and authentication
  • Ensure the host has endpoint protection and is monitored — a compromised host can read tokens in memory while the app runs
  • Rotate the Graph secret/certificate on a schedule and immediately if the host is ever suspected compromised
  • Restrict who can read appsettings.Production.json and the SQL database with NTFS/SQL permissions

Operational resilience

  • Rate limiting is handled automatically (429 Retry-After respected).
  • A failed individual Graph source does not stop the whole collection run; each card degrades independently.

Found a security issue? See SECURITY.md — please report privately, not in a public issue.