Security License Lens
The security you already own (and ignore).
CLI: licenselens · Requires Python 3.12+
Security License Lens finds Microsoft security configuration debt: high-value capabilities in E5, Entra ID P2, Defender, and related SKUs that stay at default or unused. It starts from owned entitlements, maps them to expected controls, and reports gaps as you pay for X → expected Y → observed Z.
Sample report (dry-run): examples/sample-report/
Quick start
# One-command offline demo → HTML report
pipx install licenselens # or: pip install -e ".[dev]"
licenselens demo
# Interactive scan: prompts for anything missing (TTY)
licenselens scan
# Or jump straight to a live tenant walkthrough
licenselens quickstart
In a terminal, licenselens scan asks demo vs live tenant, sign-in method, and
other missing options. Flags and AZURE_* env vars always win when set.
Non-interactive environments default to dry-run (or exit with a clear error on
--live without credentials).
Default priority packs are identity + endpoint. They shape the headline rollup and top actions; enabled checks still evaluate unless --workload filters them. Email policy config is not readable via Graph (PowerShell-only); use --allow-email-proxy only if you explicitly want a labeled Secure Score degraded path.
Live / MSP
licenselens doctor --live --auth client_secret
licenselens scan --live --auth client_secret -o reports
licenselens batch tenants.yaml -o reports
Why Security License Lens?
| Tool | Optimizes for |
|---|---|
| ScubaGear | CISA baseline compliance |
| Maester | Continuous config tests (Pester) |
| Monkey365 | Broad CSPM / CIS-style assessment |
| Microsoft Secure Score | Score + recommendations (not SKU-gated) |
| License waste scripts | Seat assignment efficiency |
| Security License Lens | Owned SKUs → expected high-value controls → unused/default gaps |
Diff, discovery, and batch
# Compare two scan JSON artifacts by check_id
licenselens diff reports/before.json reports/after.json -o reports/diff.md
# Discover Sentinel-capable workspaces (prints ARM resource IDs)
licenselens discover-workspace --auth client_secret
# Multi-tenant scans from tenants.yaml (per-tenant reports + index.md)
licenselens batch tenants.yaml -o reports
Full check pack (v0.3.0)
| Check ID | Workload | Live evaluation |
|---|---|---|
id-ca-priv-gaps |
Identity | Conditional Access MFA + legacy auth |
id-idprotect-off |
Identity | Risk-based CA |
id-pim-unused |
Identity | Standing roles vs PIM eligibility |
id-dormant-privileged |
Identity | Unused privileged users |
id-security-defaults-on |
Identity | Security defaults ON despite CA licenses |
id-access-reviews-unused |
Identity | Access Reviews licensed but never configured |
mdo-p2-policies-default |
Defender | Off default packs; opt-in --allow-email-proxy only |
mde-onboard-gap |
Endpoint | MDE API vs licensed units |
mdi-sensors-missing |
Defender | Secure Score proxy |
sen-analytics-rule-coverage |
Sentinel | ARM analytics rules (workspace required) |
sen-ueba-not-enabled |
Sentinel | ARM UEBA/entity analytics settings |
pur-dlp-not-enforced |
Purview | Secure Score DLP proxy |
Unlicensed capabilities report not_licensed instead of false gaps.
Known limitations
See docs/limitations.md for the full list. Short version:
- Email pack off by default — no Graph API for MDO policy config (PowerShell-only);
--allow-email-proxyis opt-in and labeled - MDI / Purview may still use Secure Score proxies (starter packs)
- Sentinel needs a workspace ARM ID + Azure RBAC
- Sign-in / MDE inventories may truncate on huge tenants
- Findings are advisory, not a compliance certification
- No product telemetry by default
Architecture
SKUs / service plans → capability catalog → eligible checks
→ collectors (Graph / MDE / ARM) → findings → HTML / JSON / Markdown
Permissions
See docs/permissions.md and docs/app-registration.md.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success (no gap/partial findings) |
| 1 | Completed with gap or partial findings |
| 2 | Auth / configuration / API error |
Contributing
See CONTRIBUTING.md and docs/adding-a-check.md.
Security
SECURITY.md — read-only, no telemetry by default.
License
Disclaimer
Security License Lens is an independent open-source project and is not affiliated with, endorsed by, or sponsored by Microsoft Corporation. Findings are advisory. “Microsoft”, “Entra”, “Defender”, “Sentinel”, and “Purview” are trademarks of their respective owners.
Metadata
Release files for licenselens 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| licenselens-0.3.0.tar.gz | 197.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| licenselens-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 294.5 kB
Release files / licenselens-0.3.0.tar.gz
| Download URL | licenselens-0.3.0.tar.gz |
|---|---|
| Size | 197.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4f05aca129fe2f80e5b1274d4f428d2ef21092f8fef63ae444b999d0529414fe
|
|
BLAKE2b-256 checksum How to use checksums |
5c07bffacfd655cde26778909e94b6ac8a96de2bb1c745a7c46382f977499891
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / licenselens-0.3.0-py3-none-any.whl
| Download URL | licenselens-0.3.0-py3-none-any.whl |
|---|---|
| Size | 97.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ad49a8b62593e0dcc38f2ee611de1d981e3c2360f9ae2d6fd8717486c8333300
|
|
BLAKE2b-256 checksum How to use checksums |
3419afbe2fcd0741104f3d5001457da1c8a2c5ca63c26fae5d8bf7efee024d03
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|