Skip to content

feat: remediation portal paths sweep + adopt Microsoft Learn URLs as canonical (rolling rot from MS UI reorgs) #362

Description

@Daren9m

Summary

Multiple remediation.portal.path strings shipped in data/registry.json (CheckID-authored) point at Microsoft admin-center menu locations that no longer exist in the current UI. Microsoft has reorganized the Entra admin center 3+ times in 18 months, dropping or restructuring entire hub sections (e.g., the "Protection" parent layer is gone in modern Entra; Conditional Access is now top-level).

Surfaced by Galvnyz/M365-Assess#879. Two specific cases observed:

  • ENTRA-SSPR-001 portal path: Entra admin center > Protection > Authentication methods > Registration campaign — "Protection" parent doesn't exist; current path is Entra ID > Authentication methods > Registration campaign
  • Conditional Access path: Entra ID > Protection > Conditional Access is wrong; CA is now directly accessible under Entra ID without a Protection hub

Of the ~250 checks with remediation.portal.path strings, an unknown subset are similarly stale. Each Microsoft UI reorg invalidates more.

Why this matters

portal.path strings are user-facing instructions. A consultant clicking through and not finding the named menu item:

  1. Loses confidence in the report
  2. Has to re-discover the actual path themselves
  3. May give up and skip the remediation entirely

For a tool whose value proposition is "hands-off auditor handoff", stale UI paths are a credibility bug.

CheckID owns this field (it's in the source data/scf-check-mapping.json). Downstream consumers (M365-Assess, Az-Assess, EZ-CMMC) sync the registry on a recurring basis; M365-Assess-side fixes get clobbered on next sync. The fix has to land here.

Two-track approach (matches M365-Assess#879's recommendation)

Track 1 — One-time data sweep

For every check in data/scf-check-mapping.json with a remediation.portal.path string:

  1. Open the path in the current Entra / Microsoft 365 / Defender / Intune / Purview admin centers
  2. Mark verified / stale / moved
  3. Update stale paths to current locations

Effort: ~1 day for ~250 checks × 30 seconds verification each.

Track 2 — Reduce future rot (structural change)

portal.path strings hard-code menu paths. Microsoft moves them. Recommended structural fix per M365-Assess#879:

Replace verbose menu paths with Microsoft Learn links as the primary remediation reference. Microsoft Learn URLs are stable; admin-center menu paths are not. Microsoft maintains those URLs as deep links to the current portal config experience anyway.

Schema impact on remediation.portal:

// Today (rots)
"portal": {
  "path": "Entra admin center > Protection > Authentication methods > Registration campaign",
  "steps": ["Entra admin center", "Protection", "Authentication methods", "Registration campaign"]
}

// Proposed
"portal": {
  "learnUrl": "https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-mfa-registration-campaign",
  "learnTitle": "Run a registration campaign — Microsoft Learn",
  "path": "Entra admin center > Authentication methods > Registration campaign",
  "pathAsOfVersion": "v3.4.0",
  "pathStaleness": "may-rot"
}

The path stays as a quick-reference (some auditors prefer click-paths to URL navigation), but the learnUrl becomes the canonical source. Each path entry carries a pathAsOfVersion so consumers can render "as of v3.4.0; verify against Learn URL if newer."

Track 3 — CI drift detection (optional, deferred)

The most thorough variant: a CI job that periodically scrapes the Microsoft Learn deep-link redirects and flags drift. Higher effort + brittle. Recommend deferring until Track 2 is in place.

Consumer-side change after this lands

M365-Assess (and other consumers) updates their renderer to:

// Render Learn URL primary, path as secondary "quick-reference"
{check.remediation.portal.learnUrl && (
  <a href={check.remediation.portal.learnUrl} target="_blank">
    {check.remediation.portal.learnTitle}
  </a>
)}
{check.remediation.portal.path && (
  <span className="path-ref">
    Click-through (as of {check.remediation.portal.pathAsOfVersion}): {check.remediation.portal.path}
  </span>
)}

Visually flags the path as potentially-stale and surfaces Microsoft Learn as the source of truth.

Acceptance criteria

Track 1 (sweep)

  • Every check with remediation.portal.path opened against current admin center
  • Stale paths updated to current locations (or removed if no current equivalent)
  • PR includes a list of verified-stale findings with their before/after for review

Track 2 (structural)

  • Schema updated to add learnUrl, learnTitle, pathAsOfVersion, optional pathStaleness ("verified" | "may-rot" | "deprecated")
  • All ~250 checks have learnUrl populated where a Microsoft Learn equivalent exists
  • CHANGELOG documents the schema addition + version-stamping convention
  • Schema validation gate added to Pester
  • docs/SCHEMA-MIGRATION-3.x.md (or equivalent) explains the consumer migration path

Effort estimate

  • Track 1 alone: ~1 day (sweep + verify) + per-check time × 250
  • Track 2 alone: schema + Pester + tooling change ≈ ~2 days; backfilling Learn URLs ≈ ~2 days
  • Track 1 + 2 combined as a single PR: ~5 days end-to-end (the sweep doubles as the source of Learn URLs to populate)

Recommend combined: while sweeping each check, also note the Learn URL → both updates happen in one pass.

Out of scope

  • Track 3 (CI drift detection) — defer until Track 2 lands
  • Non-portal remediation channels (powershell, graph, cli, notes) — they use stable command syntax / API references, less rot
  • Cross-language Microsoft Learn URLs (default to en-us; locale-specific URLs are Backlog: Multi-language rationale/impact (en-US default, future locales) #290 territory)
  • M365-Assess-side rendering changes — they're a downstream consumer concern but file a follow-up against M365-Assess once this schema lands

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    contentAuthoring rationale, impact, references for checksfeatureNew capability or significant additionschema

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions