Skip to main content

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.

QuerySave as
GET https://graph.microsoft.com/v1.0/usersusers.json
GET https://graph.microsoft.com/v1.0/servicePrincipalsservicePrincipals.json
GET https://graph.microsoft.com/v1.0/oauth2PermissionGrantsoauth2PermissionGrants.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:

QuerySave 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​

  1. Open ./out/discover-export.ndjson and read it — see the export format page for the exact field-by-field shape.
  2. Leave ./out/mapping.csv and ./out/salt.txt on your machine. See the privacy statement for what they're for.
  3. Upload discover-export.ndjson in the Discover app.
Screenshot pending

The upload flow is live in Discover; a screenshot of this step is still being prepared and will be added here.

Next​