Cross-tool correlation for the Entra ID security suite. Reads findings from three independent scanners and surfaces risks that no single tool can see on its own.
Output:
Each scanner in the suite sees one dimension of tenant risk:
- entra-workload-identity-scanner knows which apps hold dangerous permissions.
- entra-attack-path-visualizer knows which users can escalate to privileged roles.
- entra-zt-policy-engine knows which Conditional Access controls are missing.
Run separately, each produces its own report. None of them answers the question that actually matters: is a dangerous app controlled by a compromisable identity?
The orchestrator answers it by joining findings across tools.
All three scanners write findings in a shared schema (entra-security-report) to a common store. The orchestrator reads that store and runs two correlations:
Ownership join - for every over-privileged application, resolve its owners and check whether any owner is a user the attack-path scanner flags as having a privilege escalation path. Where they match, the dangerous app is owned by a compromisable identity: a real attack chain that neither tool reports alone.
Ownership confidence - each correlated finding is tagged with an ownership confidence score based on evidence from both Microsoft Graph and Azure ARM RBAC role assignments. A green RBAC verified tag means the registered owner is confirmed by an Azure RBAC role assignment. A yellow Graph only tag means ownership comes from Graph alone and may be stale. This surfaces which ownership claims are backed by real Azure access and which ones deserve investigation.
Over-privileged and unowned - over-privileged applications with no owner at all. No accountable party, harder to govern, and a standing escalation target. The workload scanner flags the privilege and the missing owner separately; the orchestrator surfaces the dangerous combination.
Create a single app registration in Entra ID that all three scanners share.
- Go to Microsoft Entra ID > App registrations > New registration.
- Name it whatever you want (e.g. "Entra Security Scanner").
- Under Authentication, add a platform: Mobile and desktop applications, then add the redirect URI
http://localhost. This is needed for the attack path scanner's interactive login. - Under Certificates & secrets, create a client secret. Copy the value immediately (you cannot see it again).
- Under API permissions, add the following Microsoft Graph application permissions:
Application.Read.AllDirectory.Read.AllAuditLog.Read.AllRoleManagement.Read.All
- Click Grant admin consent for your tenant.
For ownership confidence scoring (distinguishing RBAC-verified owners from Graph-only owners), assign the app the Reader role on each Azure subscription you want to scan. This is an Azure RBAC role assignment, not a Graph API permission.
- Go to Subscriptions in the Azure portal.
- Select a subscription.
- Go to Access control (IAM) > Add role assignment.
- Select the Reader role.
- Under Members, choose User, group, or service principal, click Select members, and search for your app registration name.
- Save. Repeat for each subscription.
Without this, the scanner still works but all ownership confidence will show as "graph-only".
The ZT policy engine authenticates with Azure CLI. Install the Azure CLI and run az login before starting the orchestrator.
Clone all four repos as siblings in the same directory:
git clone https://github.com/Dfrank77/entra-security-report.git
git clone https://github.com/Dfrank77/entra-workload-identity-scanner.git
git clone https://github.com/Dfrank77/entra-attack-path-visualizer.git
git clone https://github.com/Dfrank77/entra-zt-policy-engine.git
git clone https://github.com/Dfrank77/entra-orchestrator.git
Set up each scanner's virtual environment and install dependencies:
# Shared library
cd entra-security-report
# (no venv needed, installed as editable dependency in each scanner)
# Workload Identity Scanner
cd ../entra-workload-identity-scanner
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pip install -e ../entra-security-report
deactivate
# Attack Path Visualizer
cd ../entra-attack-path-visualizer
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
pip install -e ../entra-security-report
deactivate
# Zero Trust Policy Engine
cd ../entra-zt-policy-engine
python3 -m venv venv && source venv/bin/activate
pip install -r audit/requirements.txt
pip install -e ../entra-security-report
deactivate
# Orchestrator
cd ../entra-orchestrator
python3 -m venv venv && source venv/bin/activate
pip install -e ../entra-security-report
Each scanner needs a .env file with your app registration credentials.
Workload scanner (entra-workload-identity-scanner/.env):
TENANT_ID=your-tenant-id
CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secret
Attack path scanner (entra-attack-path-visualizer/.env):
CLIENT_ID=your-client-id
ZT policy engine (entra-zt-policy-engine/.env):
TENANT_ID=your-tenant-id
On Python 3.14, if the editable install is skipped (a known setuptools .pth issue), point the venv at the source directly instead:
export PYTHONPATH="/absolute/path/to/entra-security-report/src:$PYTHONPATH"
Run everything at once:
cd entra-orchestrator
source venv/bin/activate
python orchestrate.py
This runs all three scanners sequentially (each handles its own authentication), correlates the findings, and writes orchestrator_report.html.
The workload scanner authenticates with a client secret. The attack path scanner opens a browser window for interactive login. The ZT policy engine uses az login (run it before the scan if you haven't already).
Options:
python orchestrate.py --only workload attack-path # run specific scanners
python orchestrate.py --skip zt-policy # skip one
python orchestrate.py --no-correlate # scan only, skip correlation
Run correlation only (if scanners were run separately):
python correlate.py
open orchestrator_report.html
- Remediation guidance per finding.
- Additional correlation types (expiring-credential + over-privileged, PIM-eligible ownership).
- Deeper ownership evidence signals (sign-in activity, audit logs, resource metadata).
Ownership confidence scoring was inspired by feedback from Konrad Zawadka and his OwnerLensLite approach to evidence-based ownership - treating Graph ownership as one signal among many rather than the single source of truth.
Built with Claude Code (Anthropic).
Darius Frank - IAM & Cloud Security
- Portfolio: dfrank-iam.com
- GitHub: @Dfrank77
- LinkedIn: Darius Frank