Reporting & Artifacts
Generate and customize HTML scan reports.
What Is the HTML Report?
The HTML report is a comprehensive, standalone document of a complete scan. Unlike emails/webhooks (which summarize changes), the report shows all detected activity with deep detail.
Contents:
- Severity breakdown with counts
- All detected changes organized by workload and severity
- Before/after diffs for each change
- Tenant metadata and scan timestamp
- Git commit SHA (if available)
- Dark mode compatible
- Responsive mobile-friendly design
Quick Setup
- Set
REPORT_ARTIFACT=truein pipeline variables - Run the pipeline
- Download
scan-report.htmlfrom pipeline artifacts - Open in browser
Enabling Reports
Azure DevOps
- Pipelines → PIM Monitor → Edit → Variables
- Click + Variable
- Name:
REPORT_ARTIFACT - Value:
true(case-sensitive) - Save
- Next scan will generate report
GitHub Actions
- Settings → Secrets and variables → Actions → Variables
- Click New repository variable
- Name:
REPORT_ARTIFACT - Value:
true - Create variable
- Next scan will generate report
Accessing Reports
Azure DevOps
- Go to Pipelines → PIM Monitor → [latest run]
- Scroll to Artifacts section
- Click Download for
scan-report.html - Save and open in browser
GitHub Actions
- Go to Actions → PIM Change Scan → [latest run]
- Scroll to Artifacts section
- Click scan-report.html
- Download and open in browser
Report Structure
Header
Brand, title, tenant name, scan timestamp:
PIM MONITOR
Scan Report — 2026-04-27 18:42:15 UTC
Tenant: Contoso Inc.
Summary Section
High-level counts of detected changes:
Total changes: 5
High ████████████████░░░░░░░░░░░░░░░░░░ 2 (40%)
Medium ░░░░░░░░░░░░░░░░░░████████░░░░░░░░░░░░░░░ 2 (40%)
Low ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█░░░░░░░░ 1 (20%)
Changes by Workload
Organized sections for each scanned entity type:
Directory Roles
- Role name
- Changes (policy, assignments, definition)
- Severity badges
- Diff details
PIM Groups
- Group name
- Changes (policy, assignments, definition)
- Severity badges
- Diff details
Authentication Contexts
- Context name
- Properties changed
- Severity
Administrative Units
- Unit name
- Properties changed
- Severity
Diff Details
For each change:
OLD → NEW
[red] - oldValue
[green] + newValue
Links to Entra portal (if role/group)
Footer
Metadata and generation details:
Scan complete: 2026-04-27T18:42:15Z
Repository: github.com/contoso/PIM-Monitor
Commit: a3cd69c
Generated by: PIM Monitor v1.0
Dual-View Navigation
The report ships with two parallel views toggled via anchor tabs at the top, no JavaScript required.
- By severity (default): changes grouped by severity tier (High → Medium → Low → Informational), then Access Model Compliance, then Access Model Coverage. Best for triage: "what needs my attention first?"
- By entity: changes grouped by role / group / context name, sorted by High-severity count then Medium then alphabetical. Best for research: "what changed in Global Administrator this scan?"
Click the tab → URL fragment becomes #view-severity or #view-entity; the CSS :target pseudo-class swaps which <section> is visible. Bookmark a URL with #view-entity to land directly in entity-view next time.
Older browsers without :has() support fall back to showing both views stacked, a non-breaking degradation.
Evidence Links
Each git change in the report carries up to two evidence links when running inside a CI environment with a known commit SHA:
- view file → the inventory JSON file at that commit (full state of the entity).
- view diff → the file's diff inside the commit page (only that file's changes). On GitHub this anchors to
#diff-{sha256(path)}; on Azure DevOps it uses?path=...&_a=compare.
Links are inferred best-effort from context and description. They appear for changes that map cleanly to a single inventory file (policies, assignments, definitions). Compliance violations and Coverage items omit links because they are not 1-to-1 file changes.
Print & PDF Export
The report includes a @media print stylesheet so Ctrl+P (or browser → Save as PDF) produces an audit-friendly snapshot:
- Light background (
#ffffff) and dark text (#18181b) regardless of browser dark-mode preference. - All
<details>elements auto-expanded so the printed PDF shows every diff in full. - Page-breaks suppressed where they would split a section header from its content.
- All
http(s)link URLs printed inline after the link text (e.g.view diff (https://github.com/...)) so a printed PDF remains a self-contained audit trail. In-page anchors (#view-entity) are suppressed to avoid clutter. - View-tabs, scan-time, and footer are hidden in print.
To save: open the report in a browser, Ctrl+P (or ⌘+P), choose "Save as PDF", set margins to "Default".
Customizing Reports
Change Report Title
Edit Export-ScanReport in src/notifications-html.ps1:
$pageTitle = "[PIM Monitor] Scan Report"
Change to:
$pageTitle = "Azure Identity Governance Scan"
Customize Report Colors
Edit the CSS variables in Build-HtmlReport (src/notifications-html.ps1):
--color-high: #dc2626; /* Red */
--color-medium: #ea580c; /* Orange */
--color-low: #eab308; /* Yellow */
--color-bg: #fafafa; /* Light background */
Change to your brand colors:
--color-high: #1e40af; /* Blue */
--color-medium: #7c3aed; /* Purple */
--color-low: #059669; /* Green */
Add Custom Branding
Edit the header section to add logo or organizational branding:
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<img src="https://example.com/logo.png" alt="Logo" style="height: 40px; margin-right: 20px;">
<h1>Your Organization PIM Report</h1>
</div>
Include Additional Metadata
Add tenant ID, region, or custom fields to the footer:
# In Export-ScanReport, before closing HTML:
$footer = @"
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e5e5e5;">
<p><strong>Tenant ID:</strong> $TenantId</p>
<p><strong>Region:</strong> North America</p>
<p><strong>Classification:</strong> Internal Use Only</p>
</div>
"@
Change Severity Label Names
Edit Build-HtmlReport to change "High", "Medium", "Low", "Informational" to custom labels:
# Change:
$severityLabel = $severity # "High", "Medium", etc.
# To:
$severityLabel = switch ($severity) {
"High" { "Critical"; break }
"Medium" { "Important" }
"Low" { "Minor" }
"Informational" { "FYI" }
}
Remove Sections
To exclude certain workloads or severity levels, edit the section generation logic.
Example: Skip Informational changes:
if ($change.severity -eq "Informational") {
continue # Skip informational changes in report
}
Report Privacy & Retention
Storage Location
Azure DevOps:
- Stored in Build Artifacts storage
- Retention policy: Default is 30 days, configurable per project
- Accessible to anyone with project access
GitHub Actions:
- Stored in Actions artifacts storage
- Retention policy: Default is 90 days, configurable per repository
- Accessible to anyone with repository access
Security Considerations
The report includes:
- Display names of roles, groups, administrative units
- Property values that changed (e.g., MFA settings)
- Commit links and timestamps
Recommendations:
- Store reports in private repositories (GitHub) or a secure project (Azure DevOps)
- If sharing reports, redact sensitive display names
- Set artifact retention to match your compliance policy
- Review access permissions regularly
Archival & Compliance
To keep historical reports:
Option 1: Configure extended retention
- Azure DevOps: Project Settings → Pipelines → Artifact retention
- GitHub Actions: Settings → Actions → Artifacts and logs → Retention policy
Option 2: Manually download and store
- Download reports to secure storage (OneDrive, SharePoint, etc.)
- Tag with date and tenant
- Implement archival workflow
Troubleshooting
Report not generated
Check:
- Is
REPORT_ARTIFACT=trueset in variables? - Were changes detected? Reports only generate on changes
- Check pipeline logs:
Export-ScanReportshould appear - Verify
BUILD_ARTIFACTSTAGINGDIRECTORYis available (Azure DevOps)
Report looks corrupted or incomplete
Check:
- Is HTML file fully downloaded? Incomplete file → browser error
- Try opening in different browser (Chrome, Firefox, Edge)
- Check pipeline logs for errors during report generation
- Ensure HTML has valid encoding (UTF-8)
Report file too large
Check:
- How many changes were detected? Many changes = larger file
- Disable report for scans with no changes (already does this)
- Implement filtering by severity level (before report generation)
Styling looks wrong in browser
Check:
- Browser supports CSS custom properties? (Modern browsers OK)
- Dark mode toggle? Some browsers have dark mode that conflicts
- Try incognito/private browsing (skips extensions that might affect styling)
- Check console for errors (F12 → Console tab)
Comparing Report vs. Email/Webhook
| Aspect | Report | Webhook | |
|---|---|---|---|
| Trigger | Always (if enabled) | Only on changes | Only on changes |
| Detail level | Very high (all data) | Summary (top issues) | Summary |
| Format | HTML (browser) | Email (multi-client) | JSON/Slack/Teams |
| Deep linking | Yes (Entra portal) | Yes | Limited |
| Accessibility | Requires download | Immediate delivery | Immediate delivery |
| Archival | Artifact storage | Email archive | Webhook logs |
| Mobile friendly | Yes | Responsive | Platform-dependent |
Best practice: Use both:
- Email/webhook for immediate alerts (urgent issues)
- Report artifact for detailed review and compliance archival
Related Pages
- Environment Variables: REPORT_ARTIFACT
- Pipeline Configuration: REPORT_ARTIFACT setup
- Notifications: email and webhook alternatives
- Email Notifications: email format vs. report