Skip to main content

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

  1. Set REPORT_ARTIFACT=true in pipeline variables
  2. Run the pipeline
  3. Download scan-report.html from pipeline artifacts
  4. Open in browser

Enabling Reports

Azure DevOps

  1. PipelinesPIM MonitorEditVariables
  2. Click + Variable
  3. Name: REPORT_ARTIFACT
  4. Value: true (case-sensitive)
  5. Save
  6. Next scan will generate report

GitHub Actions

  1. SettingsSecrets and variablesActionsVariables
  2. Click New repository variable
  3. Name: REPORT_ARTIFACT
  4. Value: true
  5. Create variable
  6. Next scan will generate report

Accessing Reports

Azure DevOps

  1. Go to PipelinesPIM Monitor[latest run]
  2. Scroll to Artifacts section
  3. Click Download for scan-report.html
  4. Save and open in browser

GitHub Actions

  1. Go to ActionsPIM Change Scan[latest run]
  2. Scroll to Artifacts section
  3. Click scan-report.html
  4. Download and open in browser

Report Structure

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)

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.

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.

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 SettingsPipelinesArtifact retention
  • GitHub Actions: SettingsActionsArtifacts and logsRetention 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:

  1. Is REPORT_ARTIFACT=true set in variables?
  2. Were changes detected? Reports only generate on changes
  3. Check pipeline logs: Export-ScanReport should appear
  4. Verify BUILD_ARTIFACTSTAGINGDIRECTORY is available (Azure DevOps)

Report looks corrupted or incomplete

Check:

  1. Is HTML file fully downloaded? Incomplete file → browser error
  2. Try opening in different browser (Chrome, Firefox, Edge)
  3. Check pipeline logs for errors during report generation
  4. Ensure HTML has valid encoding (UTF-8)

Report file too large

Check:

  1. How many changes were detected? Many changes = larger file
  2. Disable report for scans with no changes (already does this)
  3. Implement filtering by severity level (before report generation)

Styling looks wrong in browser

Check:

  1. Browser supports CSS custom properties? (Modern browsers OK)
  2. Dark mode toggle? Some browsers have dark mode that conflicts
  3. Try incognito/private browsing (skips extensions that might affect styling)
  4. Check console for errors (F12 → Console tab)

Comparing Report vs. Email/Webhook

AspectReportEmailWebhook
TriggerAlways (if enabled)Only on changesOnly on changes
Detail levelVery high (all data)Summary (top issues)Summary
FormatHTML (browser)Email (multi-client)JSON/Slack/Teams
Deep linkingYes (Entra portal)YesLimited
AccessibilityRequires downloadImmediate deliveryImmediate delivery
ArchivalArtifact storageEmail archiveWebhook logs
Mobile friendlyYesResponsivePlatform-dependent

Best practice: Use both:

  • Email/webhook for immediate alerts (urgent issues)
  • Report artifact for detailed review and compliance archival