NinjaOne Technician Permissions Reporter

A read-only audit of the NinjaOne permissions that live behind the web UI.

It reads NinjaOne technician role permissions - the access definitions the public API does not expose - and turns them into one self-contained HTML report: a summary, an interactive role×permission matrix you can filter and compare, a diff against your previous run showing exactly what access changed and who changed it, plus CSV exports of the raw data. Run it whenever you want a check.

Summary tab of the audit report showing run statistics and a change banner counting permissions granted, revoked, and otherwise changed since the previous run.

The gap

Role permissions are unavailable through the API.

NinjaOne’s public REST API v2 does not expose role permission definitions, and the beta getTechnician operation returns only role names - never the per-role permission matrix. That matrix lives behind Administration → Accounts → Technician roles, a single-page app backed by NinjaOne’s internal JSON API. Without this tool there is no supported way to export technician access, diff it over time, or hand an auditor a record of who can do what. The tool reads that data through the same authenticated calls the web UI uses and turns it into a report.

What it does

Four jobs, one self-contained file.

Every capability below is something you can see in the report screenshots - there is no dashboard to host, no database, and no agent to deploy. Each run writes one HTML file you can open, archive, or email.

Permissions matrix tab: an interactive role by permission grid with a text filter, a multi-role compare selector, a differences-only toggle, a compared-roles-only toggle, and CSV export of the filtered slice.
Inventory

Interactive permission matrix

What it does: renders every technician role as a column and every permission as a row, with each cell showing Allowed or No Access.

What it gives you: a free-text filter (by category, permission, role, or access level), multi-role compare that outlines the selected columns, a “compared roles only” view, a “differences only” view, and a one-click CSV export of exactly what you have filtered to.

Role Changes tab: per-role access transitions with direction-colored arrows, each role labeled with who updated it and when.
Drift

Change tracking between runs

What it does: compares the run you just took against your previous run and lists every permission whose access changed, grouped by role.

What it gives you: each change shown as old → new with a direction-colored arrow - green for access granted, red for revoked, amber for a lateral change - so you can see at a glance whether a role gained or lost privilege.

Role-change attribution: each changed role shows who last updated it and when, drawn from the NinjaOne activity log and matched by role ID so it survives renames.
Attribution

Who changed it, and when

What it does: for each role that changed, it reads the NinjaOne activity log and matches role-update events back to the role by its internal ID, so a rename does not break the link.

What it gives you: a “Updated by <person> on <date/time UTC>” line beside the change. This is role-level - the person to ask - not a claim about which exact permission they toggled.

History index with a Compare two runs panel: From and To dropdowns of every saved run, rendering an inline color-coded diff above a table of all runs.
History

Compare any two runs

What it does: keeps every run in its own dated folder and builds an index page listing them newest-first.

What it gives you: a “compare two runs” panel - pick any two saved runs and the diff renders inline, grouped by role and color-coded. It works entirely offline, so you are not limited to comparing against just the previous run.

Inside the report

Exactly what each run produces.

One run writes a timestamped folder under reports/ containing a single HTML report and a set of CSV exports. The report has three tabs.

Summary tab

The landing view, and the first thing to read.

  • Run statistics: the number of roles read, the number of permissions, and the number of orphaned scope grants found.
  • Change banner: three counts against your previous run - granted (No Access to Allowed), revoked (Allowed to No Access), and other (a non-access change). On the first run it reads “Baseline established”; when nothing moved, “No changes since the last run.”
  • Roles affected: the list of roles that changed, so you know where to look.
  • Save as PDF: one button lays out the Summary and Role Changes as a dark-theme PDF - a portable record for a ticket or audit file.

Role Changes tab

The detailed diff behind the banner.

  • Every changed permission listed under its role as old access → new access, with a color-coded direction arrow.
  • A per-role “Updated by … on …” attribution line from the activity log.
  • How a change is counted: a permission absent from an export is treated as No Access, so results are pure access transitions - no noisy “added/removed” rows.

Permissions matrix tab

The full, interactive role×permission grid (see the Inventory feature above for the filter, compare, differences-only, and CSV-export controls). Absent cells render as No Access so the differences-only view is exact, and a “Print filtered view” action saves the current filtered grid to a landscape PDF.

Files written each run

FileContents
technician-role-audit.htmlThe consolidated report (Summary / Role Changes / Permissions matrix) - the one file to open.
technician_roles.csvOne row per role permission entry: RoleName, RoleId, EntityType, EntityId, EntityName, EntityLookupStatus, PermissionKey, Category, Permission, AccessLevel.
technician_roles_diff.csvOnly the rows that changed since the previous run, each an access transition.
technician_roles_orphaned_scope_grants.csvScoped grants pointing at a deleted saved search or policy - a harmless but real cleanup item.
reports/index.htmlRegenerated every run: a history of all runs with the compare-two-runs panel.

See it work

Every part of the report, in under two minutes.

A screen recording of the actual report being driven against a fully fictional tenant - the change-tracking diff, filtering the matrix, comparing two roles side by side, and the differences-only view. Every click is a real control.

The tool writes two separate kinds of file, both real and interactive on fictional data here: a per-run report and a single history index that compares any two runs. Both open in a new tab.

How it works

A read-only, four-phase flow.

Authenticate into an in-memory session, read the role permissions over GET requests, analyze the diff against your previous run, and write one consolidated report. Run it again whenever you want a fresh check - each run becomes the baseline for the next.

Architecture diagram: Authenticate (in-memory session), Read (enumerate roles over GET), Analyze (diff vs previous run, correlate activity log), Deliver (consolidated HTML report + CSV exports), with a read-only boundary across the whole flow and a re-run loop.

Extraction uses authenticated GET requests only. The single POST (activity/search, for who-changed-it attribution) is a read-only query that mutates nothing and can be skipped with --no-activity. The session is held in memory and logged out at the end - and the authoritative confirmation that nothing changed is NinjaOne’s own activity/audit log.

Trust & safety

Built to be safe under a privileged account.

NinjaOne has no view-only role for the Technician roles tab, so the account used to run the audit is effectively privileged. The design treats that as the threat model rather than ignoring it.

Read-only extraction

Authenticated GET requests only - no role editor, no UI clicks, no PUT/PATCH/DELETE. A network guard aborts any non-GET request during recon mode.

No token on disk

You sign in once per run; the session is held in memory only and never written to disk, then logged out server-side when the run finishes.

Independently verifiable

After any run, NinjaOne’s activity/audit log should show no change events - the authoritative confirmation that the tool made no edits.

Honest about scope. “Read-only” describes the data, not the connection. The tool changes no role permissions, but it does talk to the server: signing in and signing out alter session and audit-log state by design, and the attribution step issues one read-only search POST (skippable with --no-activity). Change attribution is role-level (who edited the role), not per-permission authorship, and the audit covers technician role templates rather than per-technician overrides or End-User roles.

In practice

From sign-in to finished report.

Each audit is a single run on a Windows workstation. An operator with a NinjaOne sign-in that can open Administration → Accounts starts it; from there the tool works on its own and produces one report. Here is what a run does, end to end.

Inside a single run

Authenticate once

The tool opens a browser at the NinjaOne sign-in page. The operator completes sign-in and MFA; the tool then captures that session in memory, closes the browser, and continues on its own. No password or MFA secret is ever stored.

Read every role

Working from the in-memory session, it enumerates every technician role over read-only GET requests and pulls each role’s per-permission settings.

Analyze and deliver

It diffs the result against the most recent prior run (absent counts as No Access), correlates who changed what from the activity log, then writes one self-contained HTML report plus its CSV exports and logs the session out.

Every run after the first

Re-runs diff automatically

Each new run auto-selects the most recent prior run as its baseline and shows exactly what access changed since then, and who changed it. The very first run simply records a baseline.

Compare any two runs

The history view lists every run and renders the difference between any two of them inline, not just the latest pair.

This walkthrough documents how the tool operates; it is not currently published for self-service use. The live sample report and run history below are the best way to see what a run produces.