Thirteen Node.js tools for the administrative work around a Jira Cloud migration — permission schemes, security levels, project roles, read-only lockdown and org identity clean-up.
Jira Cloud has no bulk endpoints for most administration, and the admin UI has no multi-select. So the work either does not get done, or it gets done by hand over three days with mistakes in it.
Assigning a permission scheme to 300 projects is 300 individual calls. Adding a group to every permission in 40 schemes is over a thousand. In Data Center this was three Groovy console scripts; that API simply does not exist on Cloud.
If an issue security level hides issues from the auditing account, every count comes back low and reads as good news. "The project is empty" and "my account cannot see this project's issues" are indistinguishable from outside.
A user editing a Data Center issue after the export snapshot was taken produces a Cloud copy that is simply stale. There is no error, no report, and nobody finds out until someone notices a missing comment months later.
Thirteen tools, grouped by the job. Take the site-wide permission-scheme export first — it is the only artefact that captures the whole starting state, and the only moment to take it is before the first write.
Puts every DC project on one read-only permission scheme, saving each project's previous scheme id AND name to a portable backup. Reverts from that backup. Supports PAT, pre-encoded Basic or plain credentials.
set, revert, lockdown (create a read-only scheme and assign it in one shot), revert-from-dc-backup (restore each project's original scheme on Cloud by matching name, because the ids changed), and backfill-missing-schemes.
Snapshot every project's current permission scheme, bulk-assign a target scheme, roll back to the snapshot. The Cloud replacement for three Data Center Groovy console scripts.
Migration assistants only carry permission schemes assigned to a project; unassigned ones are silently dropped. This recreates them on Cloud as literal copies with translated grant holders. Creation only — it assigns nothing.
Widens every permission type that already has a grant. Permission types with no grants are left alone — it widens what exists rather than inventing policy.
The fix for an audit that says 40,000 issues on DC and shows 31,000 on Cloud. Temporary by design: add for the audit window, remove when it closes.
Adds a user to all roles across all or filtered projects. Paginated, rate-limit-aware, duplicate-safe. For a new administrator, a service account or a migration bot.
Via the org admin API. Suspension is the default because it is reversible, frees the licence and keeps the data. Discovery uses the search endpoint, which — unlike GET /users — also finds invited-but-unclaimed accounts.
Collects every group member and adds them as customers, with an audit phase that set-intersects access-group membership locally instead of doing a thousand per-user lookups.
Finds every Atlassian Team whose single sole member is one given person and deletes them. Each team is re-checked at delete time, so a stale plan can never delete the wrong thing.
Read-only. Cross-tabulates findability by name and by picker against every org directory attribute, so the cause falls out of the data — usually a user-search index gap, not permissions or licensing.
The answer to "I wrote a value and the user sees nothing" is almost always here: the field is not on the screen. The write succeeded; the API had no reason to warn you.
Deletes projects not updated in N months. Validates that the target URL looks like a sandbox and requires interactive confirmation. Sandbox only — deliberately awkward to point anywhere else.
The same operating model throughout, deliberately — so that knowing one tool means knowing all of them.
Every tool's default is a plan. Writing needs an explicit --apply or the absence of --dry-run, and the plan is a file you can read. The plan is also the artefact you put in front of the system owner when you ask.
Anything that changes an assignment writes the previous state to JSON first, and has a revert mode that consumes it. The backup stores both the id and the name of what it replaced, so it stays useful across an instance boundary where ids change and names survive.
Destructive operations re-verify their precondition at write time. If a team gained a member since the plan was built, it is skipped. A stale plan cannot delete the wrong thing.
A count of zero, an empty list or a 404 does not licence a write until you have proved your credentials can see the object at all — on that same object. Permission and security filters are per object, so a positive control elsewhere proves nothing.
git clone https://github.com/leanzero-srl/leanzero-jira-admin-toolkit.git
cd leanzero-jira-admin-toolkit/bulk_assign_permission_scheme
npm install
cp .env.example .env # CLOUD_BASE_URL, CLOUD_API_TOKEN = base64("email:api_token")
# The snapshot is your undo for most of this repository. Take it FIRST.
node main/bulk_assign_permission_scheme.js exportEvery question this repository raises, answered in order: what to snapshot before you change anything, how to freeze and thaw a migration window, how to widen access without inventing policy, and how to prove an admin change did what you intended. 28 sections.
The export. Snapshot every project's current permission scheme before anything in this repository touches the site — that file is your undo for most of what follows.
git clone https://github.com/leanzero-srl/leanzero-jira-admin-toolkit.git
cd leanzero-jira-admin-toolkit/bulk_assign_permission_scheme
npm install
cp .env.example .env # CLOUD_BASE_URL, CLOUD_API_TOKEN = base64("email:api_token")
node main/bulk_assign_permission_scheme.js exportEvery tool is a plain Node.js CLI over the public REST API, plus the Atlassian admin and Teams APIs for the org-level tools. Node 18 or newer, a Cloud API token, and — for suspend_accounts_by_domain, remove_solo_member_teams and diagnose_user_picker — an organisation admin API key minted at admin.atlassian.com → Settings → API keys.
No. Jira Cloud has no bulk endpoints for most administration, and the admin UI has no multi-select. Assigning a scheme to 300 projects is 300 individual PUTs.
In Jira Data Center this class of work was three Groovy console scripts using ComponentAccessor.getPermissionSchemeManager(). That API does not exist in Cloud. Cloud exposes REST endpoints only, and there is no bulk variant: every project is mutated individually via PUT /rest/api/3/project/{keyOrId}/permissionscheme.
| Job | Calls | In the UI |
|---|---|---|
| Assign one permission scheme to 300 projects | 300 PUTs | 300 page loads, no multi-select |
| Add a group to every permission in 40 schemes | 1,000+ POSTs | Open every scheme, every permission row |
| Add a group to every security level in every scheme | one per level | Same, per level |
| Add one user to every role on every project | one per role per project | Same, per role |
So the work either does not get done, or it gets done by hand over three days with mistakes in it. These tools do it in one pass, with a dry run, a snapshot, and a documented way back.
Stated up front, because discovering a limit mid-cutover is the expensive way to find it.
The toolkit is free and Apache-2.0. If you would rather have someone run the plan, put the numbers in front of your stakeholders, and execute the window with you, that is what we do.