Microsoft Entra ID via Graph Explorer
Nothing to install. You run three queries in
Graph Explorer
in your browser, save each response as JSON, then run nc-export.py against
the three files. If you would rather have the script call Graph for you,
use the PowerShell path instead — both produce the
same output file.
Read the privacy statement first if you have not already. This path performs zero network I/O in the script itself — every network call happens in your browser, against Graph Explorer, before the script ever runs.
Step 0 — get the script
Download nc-export.py from the downloads page and
verify its checksum before you run it. The command below assumes you run
it from the same directory you downloaded it into.
Before you start
- Python 3 — no packages to install; the script is standard-library only.
- Access to Graph Explorer,
signed in as an account permitted to read users, service principals, and
OAuth grants in your tenant. Graph Explorer will need to consent the same
three read-only scopes the PowerShell path uses —
User.Read.All,Application.Read.All,Directory.Read.All— since the three queries below are the same reads either path performs. Signing in with an account whose directory role can read those objects is what makes that consent possible.
Run the three queries
In Graph Explorer, run each of these GET requests, then use Save
response as JSON (or copy the response body) to write each one to a file.
Each response comes back as a Graph list document —
{"value": [...]} — which is exactly the shape the script expects.
| Query | Save as |
|---|---|
GET https://graph.microsoft.com/v1.0/users | users.json |
GET https://graph.microsoft.com/v1.0/servicePrincipals | servicePrincipals.json |
GET https://graph.microsoft.com/v1.0/oauth2PermissionGrants | oauth2PermissionGrants.json |
Optionally, a fourth query adds app-role assignment grants (direct role-based app assignments, as distinct from OAuth consent grants) to the report — entirely optional and back-compatible; skip it and you simply get a report without that source:
| Query | Save as |
|---|---|
GET https://graph.microsoft.com/v1.0/servicePrincipals/{id}/appRoleAssignedTo (per service principal) | appRoleAssignments.json |
A large tenant may need to page through @odata.nextLink in Graph
Explorer — save every page's value array into the same file (merged into
one {"value": [...]} document) if so. Concretely, page one and page two
each come back as their own {"value": [...]} document; you combine them
into a single value array before saving:
// page 1
{"value": [{"id": "user-a"}, {"id": "user-b"}]}
// page 2
{"value": [{"id": "user-c"}]}
// users.json — merged
{"value": [{"id": "user-a"}, {"id": "user-b"}, {"id": "user-c"}]}
Run the script
python3 nc-export.py entra \
--users-json users.json \
--service-principals-json servicePrincipals.json \
--grants-json oauth2PermissionGrants.json \
--out-dir ./out
Add --app-role-assignments-json appRoleAssignments.json if you saved that
fourth query. The script performs no network I/O — it only reads the
files you name on the command line and writes to --out-dir.
What you'll see
Skipped rows: 0
Nothing has been sent anywhere. Review discover-export.ndjson, then upload it in the Discover app.
Same closing message as the PowerShell path, byte-for-byte — both implementations are required to produce it identically.
If something goes wrong
The script fails loudly rather than guessing. A few messages you might see, and what they mean:
'<path>' is not a Graph list response (missing a top-level "value" array)— the file you saved isn't a Graph list document. Re-save the response from Graph Explorer rather than a partial copy-paste.invalid JSON in '<path>'— the file isn't valid JSON at all; re-export it.warning: 0 grants were found in the source data -- this may indicate a permission or consent problem, not an empty tenant— printed to the terminal, not fatal. Check that the signed-in account actually has the three scopes above before assuming the tenant has none.
After it finishes
- Open
./out/discover-export.ndjsonand read it — see the export format page for the exact field-by-field shape. - Leave
./out/mapping.csvand./out/salt.txton your machine. See the privacy statement for what they're for. - Upload
discover-export.ndjsonin the Discover app.
The upload flow is live in Discover; a screenshot of this step is still being prepared and will be added here.