Profiles and custom rules¶
Profiles are declarative YAML files that shape what a scan looks
at, which backends it prefers, and what redaction settings the schema
accepts. They live under catalog/profiles/ and validate against a JSON schema
(catalog/profiles/assessment-profile.schema.json) before any network call.
Built-in profiles¶
| Profile | Scope |
|---|---|
core |
Default priority packs (identity + endpoint), no proxy. |
identity |
Entra ID controls, privileged access, and identity protection. |
full |
Every declarative area currently modeled by LicenseLens. |
email |
Exchange Online / MDO themed pack. |
collaboration |
SharePoint, OneDrive, and Teams sharing controls. |
endpoint |
Intune compliance, MDE onboarding, and endpoint baselines. |
data-protection |
Purview DLP, labels, retention, and related readiness checks. |
secops |
Sentinel analytics and UEBA-oriented checks. |
power-platform |
Power Platform tenant isolation and environment governance. |
power-bi |
Power BI external sharing and capacity governance. |
scuba |
Broad pack set aligned with SCuBA-oriented coverage. |
Authoritative pack/check expansion: Profile reference.
Selecting a profile (--profile collision)¶
--profile means different things on scan vs doctor
On scan, demo, quickstart, and batch, --profile
is a profile id (core, identity, full, …).
On doctor, --profile is probe depth only: basic (default,
core Graph) or full (also MDE API + Sentinel). Profiles on
doctor use the separate, repeatable flag --assessment-profile.
# Profile on scan
licenselens scan --profile identity -o reports
licenselens scan --live --auth client_secret --profile full -o reports
# Doctor: probe depth vs assessment requirements
licenselens doctor --live --profile full --auth client_secret
licenselens doctor --assessment-profile identity --assessment-profile full
Organization overlay or standalone rules without replacing the built-in id:
licenselens scan --config org-profile.yaml -o reports
licenselens scan --rules rules.yaml -o reports
licenselens scan --profile identity --config org-overlay.yaml --rules rules.yaml -o reports
What a profile declares¶
packs— the themed pack list for the top card and default scope.check_ids— an explicit check list, when a profile is prescriptive.backend_preferences— apreferredbackend order (graph,arm,exchange_online,defender,secure_score,manual) plusallow_proxyandallow_manualflags.exclusions— accepted-risk waivers:check_id,owner,reason,expires_on, and an optionalkind: break_glasswithprincipal_idsfor named emergency-access accounts.custom_rules— post-evaluation assertions over findings (see below).severity_override— per-check severity overrides: a list of{check_id, severity}pairs that reclassify a check's severity before the report is written (see Severity overrides).omissions— free-text omission notes (a list of strings) documenting what a profile deliberately does not assess.annotations— owner/reason annotations: a list of{owner, reason}pairs recording who owns a profile decision and why.redaction— schema fieldsredact_tenant_ids,redact_user_principals,redact_domains, and areplacementtoken (see Redaction).
Severity overrides¶
severity_override lets a profile reclassify a check's severity per tenant or
per organization. Each entry names a check_id and the severity to apply
(critical, high, medium, low, info). Duplicate check_id entries are
rejected at validation time.
severity_override:
- check_id: id-ca-legacy-auth-block
severity: critical
- check_id: id-guest-sharing
severity: low
Omissions and annotations¶
omissions records what a profile deliberately does not assess (free-text
notes), and annotations records who owns a profile decision and why:
omissions:
- "Email pack not assessed (no Graph read API for MDO policy config)."
annotations:
- owner: security-team
reason: "Legacy auth blocking is the top priority for this tenant."
Report provenance¶
severity_override, omissions, and annotations all surface in the report's
provenance footer payload (build_provenance):
severity_overrides— the profile's per-check severity overrides ([{"check_id", "severity"}]), empty when the scan carried none.omissions— the profile's free-text omission notes, empty when none.annotations— the profile's owner/reason annotations ([{"owner", "reason"}]), empty when none.
This makes the report self-describing: a reader can see which checks were reclassified, what was deliberately omitted, and who owns the decisions.
Custom rules¶
A custom rule asserts a condition over the finding set and is evaluated after
the scan. Each rule declares a selector (a finding field path such as
finding.status or finding.severity), an operator (gte, in, …), a
value, and optionally a collection (count) and references.
custom_rules:
- id: identity-high-gaps
title: Identity high severity gaps
selector: finding.severity
operator: in
value:
- critical
- high
references:
- https://learn.microsoft.com/security/zero-trust/deploy/identity
Supply standalone rules without a full profile via --rules:
Exclusions and break-glass accounts¶
Exclusions let you document an accepted risk instead of pretending a finding is "fixed". A break-glass exclusion records the named emergency-access principals that are meant to skip all-user MFA, so the check can account for them rather than flagging them as a gap.
Redaction¶
RedactionSettings on the profile schema accepts enabled,
redact_tenant_ids, redact_user_principals, and redact_domains plus a
replacement token (default [redacted]). The schema default is
enabled: true with tenant-id and user-principal redaction on, and every
built-in profile ships with those defaults (redact_domains is off except on
the email profile).
The resolved profile's redaction block is applied at report-writing time to
every output surface — HTML, JSON, Markdown, and the report ZIP's embedded
DATA_JS/VIEWMODEL_JS — replacing the tenant id, any UPN-like string, and
the tenant's own domains (harvested from the scan result) with the
replacement token. Each class is gated by its flag under the master
enabled switch, so a profile that keeps redact_domains: false still strips
tenant ids and user principal names while leaving third-party URLs and host
names intact.
redaction:
enabled: true
redact_tenant_ids: true
redact_user_principals: true
redact_domains: true # opt-in on built-in profiles
replacement: "[redacted]"
--no-redact opts out for a single run; --redact re-enables redaction when a
profile turned it off. Redacted artifacts can still carry other sensitive
material (evidence samples, non-tenant object ids, third-party host names), so
treat JSON/ZIP artifacts as sensitive tenant data regardless.
See Permissions for what each collector needs, and Architecture for how profiles flow through the engine. See CLI reference for the full flag catalog.