Architecture¶
Pipeline¶
Auth → collect_scan_state → evaluate licensed checks
→ profile / custom rules → rank / rollup → HTML / JSON / MD (+ optional ZIP)
- Auth — implemented modes: device code (
device), client secret (client_secret), and Azure CLI (azure_cli). Dry-run uses curated sample data without a live credential. See CLI reference for flags and env vars. collect_scan_state— resolves owned SKUs / service plans, then runs the collectors selected for the scan (workload filters, profile backend preferences, and optional--backendorder). Evidence is keyed for evaluators; collection summaries record what succeeded or was skipped.- Evaluate licensed checks — YAML checks under
checks/<workload>/run only when required capabilities are owned. Missing entitlements yieldnot_licensedinstead of a false gap. - Profile / custom rules — optional profile overlays and custom
rules adjust findings after the base evaluation (accepted risk, pack scope,
backend preferences). Profile
RedactionSettingsare accepted on the schema and merged into the resolved profile, but are not applied to HTML/JSON/MD report bodies today. - Rank / rollup — priority packs (default identity + endpoint) shape the top-card moves and capability rollup; findings stay ranked by status and severity.
- Reports — static HTML dashboard, portable JSON, and Markdown. Optional
--report-archiveadds a ZIP beside those files.
There is no hosted SaaS control plane: the CLI runs locally (or in your CI), talks to Microsoft APIs with your credentials, and writes artifacts to disk.
Output layout¶
scan / demo / quickstart write report files flat into -o /
--output-dir (default reports):
reports/
security-license-lens-report.html
security-license-lens-report.json
security-license-lens-report.md
security-license-lens-report.zip # only with --report-archive
Only batch nests per tenant under slug and UTC timestamp, plus a batch
index:
diff compares two *.json artifacts by check_id and emits Markdown or JSON.
Collector families¶
Collectors are read-only. Families include:
| Family | Role |
|---|---|
| Microsoft Graph | SKUs, Conditional Access, roles/PIM, sign-ins, guests, apps, Intune, Secure Score, and related directory/security reads |
| Defender for Endpoint (MDE) | Machine inventory / onboard signals |
| Azure Resource Manager (ARM) | Sentinel alert rules, settings, workspace discovery |
| Public DNS | SPF / DKIM / DMARC and related mail-auth records |
| PowerShell bridge | Allowlisted read-only adapters (powershell/LicenseLens.Collectors) for surfaces without a Graph read API |
Operator-facing backend tokens and the bridge contract are documented in
Collectors. Full command and flag surface: CLI reference.
Contributor registry metadata lives in
src/licenselens/engine/_registry_source_meta.py.
Workspace discovery¶
collectors/workspace_discover.pyenumerates subscriptions (ARM), lists Log Analytics workspaces, and probes each for aMicrosoft.SecurityInsights/alertRulesresponse to decide whether it hosts Sentinel.run_scan(..., discover_workspaces=True)auto-selects a workspace only when exactly one Sentinel-capable workspace is found; otherwise it warns and refuses to guess.
Key types¶
SubscribedSku/ServicePlan— tenant entitlementsCapability— product feature unlocked by entitlements, plus plain-languageplain_name,outcome,why_it_matters,if_unusedfor customer reportsCheckDefinition— declarative check metadata, includingcustomer_title,customer_summary,customer_next_stepFinding— result with statusgap | partial | ok | not_licensed | error | skippedand mirrored customer-facing fieldsCapabilitySummary— owned capabilities ready for the “What you already pay for” sectionScanResult— full portable scan artifact, includingrecommended_next_steps
Extensibility¶
- Add capabilities in
catalog/capabilities.yaml - Add checks as YAML under
checks/<workload>/ - Implement collectors named in check
collectorfields - Register pure evaluators in
engine/evaluate.py(EVALUATORS) - Keep reporting format stable so MSPs can archive JSON over time