NinjaOne → Breeze
NinjaOne is the cleanest migration of any RMM in this section. Its hierarchy maps onto Breeze’s one-for-one, its API is a well-behaved OAuth2 REST API, and its scripts are plain PowerShell and shell. Most of the effort goes into policy inheritance, which has no direct Breeze equivalent and generally simplifies on the way across.
Read Migrating to Breeze first.
Hierarchy Mapping
Section titled “Hierarchy Mapping”| NinjaOne | Breeze | Notes |
|---|---|---|
| Instance | Partner | |
| Organization | Organization | Direct match. |
| Location | Site | Direct match, including the “Main Office” default. |
| Device role / group | Device Group | Ninja’s device roles map well to Breeze dynamic device groups. |
| Device | Device | |
| Contact | Contact | Direct match, plus an optional site pin. Export the organization’s contacts and bulk-load them with Recipe 1c. |
This is as close to a lift-and-shift as RMM tenancy gets.
Phase 0 — Export from NinjaOne
Section titled “Phase 0 — Export from NinjaOne”Create an API client under Administration → Apps → API, with grant type Client Credentials and the monitoring and management scopes. Your region determines the host — app.ninjarmm.com (US), eu.ninjarmm.com, oc.ninjarmm.com, ca.ninjarmm.com.
-
Get a token:
Terminal window HOST=app.ninjarmm.comTOKEN=$(curl -sf -X POST "https://$HOST/ws/oauth/token" \-d grant_type=client_credentials \-d "client_id=$NINJA_CLIENT_ID" \-d "client_secret=$NINJA_CLIENT_SECRET" \-d 'scope=monitoring management' | jq -r .access_token) -
Export the org → location tree. This is your Breeze tenancy CSV, produced directly:
Terminal window curl -sf -H "Authorization: Bearer $TOKEN" "https://$HOST/v2/organizations" > orgs.jsoncurl -sf -H "Authorization: Bearer $TOKEN" "https://$HOST/v2/locations" > locs.jsonecho 'organization,site' > tree.csvjq -r --slurpfile o orgs.json '.[] as $l | ($o[0][] | select(.id == $l.organizationId)) as $org| [$org.name, $l.name] | @csv' locs.json >> tree.csvFeed it to Recipe 1.
-
Export devices — your reconciliation checklist:
Terminal window curl -sf -H "Authorization: Bearer $TOKEN" \"https://$HOST/v2/devices-detailed" \| jq -r '.[] | [.organizationId, .locationId, .systemName,.nodeClass, .offline, .lastContact] | @tsv' > ninja-devices.tsv -
Export custom fields. Ninja custom fields exist at global, organization, location and device scope. Dump the device-scoped values, which are the ones that usually matter:
Terminal window curl -sf -H "Authorization: Bearer $TOKEN" \"https://$HOST/v2/queries/custom-fields" | jq . > ninja-custom-fields.json -
Export policies for reference:
GET /v2/policies. You are not going to import these, but you need them open in a second window while you rebuild.
Phase 3 — Deploy the Breeze Agent with a Ninja Script
Section titled “Phase 3 — Deploy the Breeze Agent with a Ninja Script”-
Create the script. Administration → Library → Automation → Create → PowerShell, run as System. Use the Windows payload from Recipe 3.
Ninja passes script variables as parameters, so parameterise the enrollment key rather than hardcoding it:
Terminal window param([string]$Server = 'https://breeze.yourdomain.com',[string]$Key,[string]$Secret)You can also read a Ninja custom field with
Ninja-Property-Get, which lets you store the per-location enrollment key as an organization custom field and have one script resolve it automatically. That is the tidiest approach if you have more than a handful of customers. -
Add AV/EDR exclusions first — both directions. See Antivirus Exceptions.
-
Schedule it, don’t run it once. Create a Scheduled Task targeting one organization, running daily. The
agent.yamlcheck makes it idempotent, so it sweeps up offline laptops over the following days. A single manual run reliably misses 8–15% of a fleet. -
Roll in waves. Pilot org → 10% → the rest.
Migrating Scripts
Section titled “Migrating Scripts”Ninja’s automation library is PowerShell, batch, and shell, so bodies port directly. Rewrite the wrapper:
| NinjaOne | Breeze |
|---|---|
Script parameters / $env: variables |
Script parameters |
Ninja-Property-Get / Ninja-Property-Set |
Custom field reads/writes via the API |
Exit code 0 = success |
Same, plus exitCodeSeverityMapping for severity by exit code |
| Run As: System / Logged-on user | runAs: "system" / "user" |
| Script categories | category field |
The only real work is Ninja-Property-Get / Ninja-Property-Set, which have no drop-in replacement — replace with an authenticated call to the Breeze device custom-field-values endpoint.
Bulk-load the converted scripts with Recipe 6, setting availability: "partner".
Migrating Custom Fields
Section titled “Migrating Custom Fields”Ninja custom fields exist at four scopes — global, organization, location, and device — and Breeze’s custom fields only have two ownership axes (partner-wide or one organization), so the mapping collapses two Ninja scopes onto each:
| NinjaOne scope | Breeze owner | Notes |
|---|---|---|
| Global | All organizations (partner-wide) | Import once, not per organization. |
| Organization | One organization | |
| Location | One organization | Breeze has no site-level custom field; a location-scoped Ninja field becomes org-owned. If two locations under one organization disagree on a value, the later import wins — decide which location’s values are authoritative before you run it. |
| Device | (not a definition scope) | Device-scope is where values live, regardless of which scope owns the definition — see below. |
Use the Import from another RMM wizard (Settings → Custom Fields, or the Devices page) rather than the raw /v2/queries/custom-fields export on its own:
- From your Phase 0 export (step 4), separate global-scope fields from organization/location-scope ones — the wizard needs one pass per owner.
- Open the wizard, choose NinjaOne as the source. Import the global fields first with owner All organizations, then the organization/location fields per organization with owner This organization only.
- On the values step, map
systemName(or NinjaOne’s own deviceid, returned by/v2/devices-detailed) to an identifier column and each field’s values to the matching custom field. - Map the device
idto Source device ID, not just the hostname. It is the durable identifier Custom Fields records on this first run, so a later re-import — after more devices enroll — resolves exactly instead of guessing by hostname.
A row the wizard can’t resolve to exactly one device is reported, never guessed at. Re-run after each enrollment wave; an already-applied value is left alone by default.
Policies: The Part That Needs Design
Section titled “Policies: The Part That Needs Design”This is where NinjaOne migrations take their time. Ninja uses nested, inheriting policies — a parent policy with child overrides per organization or device role. Breeze does not model inheritance that way. Instead it is partner-wide first: a policy is owned by the partner and applies across every organization, or it is owned by one organization.
The translation:
| NinjaOne | Breeze |
|---|---|
| Base policy applied everywhere | Partner-wide configuration policy |
| Per-role child policy (Workstation / Server) | Separate partner-wide policy bound to a dynamic device group by role |
| Per-organization override | Organization-owned policy |
| Policy conditions → alerts | Monitors + alert rules |
| Patching section of a policy | Patch policies and update rings |
Phase 6 — Decommission NinjaRMMAgent
Section titled “Phase 6 — Decommission NinjaRMMAgent”Only after Recipe 4 is clean for that organization.
-
Disable alerting on the Ninja policies bound to that org; leave the agent installed.
-
Wait one full patch cycle.
-
Uninstall via Ninja. The supported path is to delete the device in the Ninja console, which triggers agent removal. To do it by script:
Terminal window $p = Get-CimInstance Win32_Product | Where-Object { $_.Name -like 'NinjaRMMAgent*' }if ($p) { msiexec /x $p.IdentifyingNumber /qn /norestart }On macOS, run Ninja’s
/Applications/NinjaRMMAgent/uninstall.sh. -
Verify from Breeze. Recipe 5 — Breeze Management Posture fingerprints NinjaOne. Drive the count to zero. This catches the machines whose Ninja agent was already dead and which the Ninja console therefore cannot report on.
-
Remove the organization in Ninja and reduce your licence count, after exporting anything you must retain.