Defender for Endpoint vulnerability reporting has always been a pain point, and this felt like a great opportunity to use AI-assisted coding to build a dashboard without a dependency on Power BI or other expensive tools. The result is a self-contained HTML dashboard built with PowerShell, along with local scripts, validation tooling, Azure automation assets, GitHub Actions workflows, and sample PDF report outputs.
I have provided the starting prompt and much of the creation history for anyone interested in how it came together here: copilot_all_prompts_2025-12-30T05-04-10.chatreplay.json
flowchart LR
A["Microsoft Defender for Endpoint"] --> B["Invoke-VulnerabilityExport.ps1"]
B --> C["VulnExport_current.json.gz"]
B --> D["VulnHistory_YYYY.json.gz"]
B --> E["Machines_Current.json.gz<br/>Machines_History.json.gz"]
B --> F["AdvancedHunting_Current.json.gz<br/>(optional)"]
C --> G["Generate-VulnerabilityDashboard.ps1"]
D --> G
E --> G
F --> G
G --> H["VulnerabilityDashboard.html"]
H --> I["reports/*.pdf"]
- Export data from Defender for Endpoint.
$secret = Read-Host -AsSecureString -Prompt 'Enter client secret'
.\Invoke-VulnerabilityExport.ps1 `
-TenantId 'your-tenant-id' `
-AppId 'your-app-id' `
-AppSecret $secret `
-OutputPath .\exports `
-IncludeAdvancedHunting- Generate and validate the dashboard.
.\Generate-VulnerabilityDashboard.ps1 `
-DirectoryPath .\exports `
-OutputPath .\VulnerabilityDashboard.html `
-ExportMachineData $false `
-Validate- Re-run validation later without regenerating the HTML.
.\Generate-VulnerabilityDashboard.ps1 `
-DirectoryPath .\exports `
-OutputPath .\VulnerabilityDashboard.html `
-ValidateOnlyFor managed identity auth, Azure provisioning, and GitHub workflow setup, use the linked docs below.
| Topic | What it covers |
|---|---|
| Azure setup | API permissions, authentication options, Azure Automation provisioning, and Container App publishing |
| GitHub Actions setup | OIDC service principal setup, required repository secrets, and branch protection guidance |
| Workflow notes | What each workflow does and when to use it |
| Script | Purpose |
|---|---|
Invoke-VulnerabilityExport.ps1 |
Downloads Defender exports and writes the canonical gzip data store |
Generate-VulnerabilityDashboard.ps1 |
Builds the self-contained HTML dashboard and can validate the result |
Validate-DashboardReports.ps1 |
Validates an existing dashboard HTML against committed exports |
Setup-AzureResources.ps1 |
Provisions Azure Automation, storage, scheduling, and optional Container App hosting |
Setup-GitHubActionServicePrincipal.ps1 |
Creates the Entra app and federated credential for GitHub Actions OIDC |
| File | Purpose |
|---|---|
exports/VulnExport_current.json.gz |
Current open vulnerability row versions |
exports/VulnHistory_YYYY.json.gz |
Historical row versions closed during each year |
exports/Machines_Current.json.gz |
Latest known state per device |
exports/Machines_History.json.gz |
Append-only machine state changes |
exports/AdvancedHunting_Current.json.gz |
Latest known CVE enrichment from Advanced Hunting |
flowchart TD
A["Same export and generation scripts"] --> B["Run locally"]
A --> C["Run in Azure Automation"]
A --> D["Run in GitHub Actions"]
B --> E["Open HTML directly"]
C --> F["Publish to blob storage or Container App"]
D --> G["Commit updated exports and dashboard"]
azure/Invoke-DashboardPipeline.ps1is a generated runbook artifact. Editshared-helpers.ps1andazure/runbook-source.ps1, then rebuild with.\azure\Build-Runbook.ps1.- Legacy
VulnExport_<group>_<date>.json(.gz)compatibility remains temporary through2026-07-01. - Sample PDF outputs are committed under
reports/.