NinjaOne
This is a how-to for RMM mass-deploy on NinjaOne. Use a policy scheduled script, not NinjaOne's application-install UI. The script reads the device's organization from NinjaOne and passes it to the silent installer.
Every method still needs a verified installer and the right deployment scope.
What NinjaOne already gives you
When NinjaOne runs a Windows, macOS, or Linux automation script, it injects organization identity as environment variables for that script process only. They are not persistent machine environment variables. If you run the same PowerShell outside NinjaOne automation, they are empty.
| NinjaOne variable | Use in SparkLogs |
|---|---|
NINJA_ORGANIZATION_ID | Default RMMCLIENTID (numeric org ID on this NinjaOne instance) |
NINJA_ORGANIZATION_NAME | Default CLIENTNAME (display label only, not a link key) |
NINJA_LOCATION_ID / NINJA_LOCATION_NAME | Do not use as the client key unless you want a SparkLogs org per site |
NINJA_COMPANY_NAME | Company label; prefer NINJA_ORGANIZATION_NAME for CLIENTNAME |
NinjaOne's model is organization → location → device. Organizations are a flat list of clients (and your own internal org). Locations are sites or slices inside one organization. Device groups and saved searches are targeting only; they are not a client identity.
Using NINJA_ORGANIZATION_ID maps one NinjaOne client (every location under it) to one SparkLogs client organization.
Copy an org ID by hand from Administration → Organizations, hover the row, ellipsis, Copy Org ID. That value is the same ID the env var carries for devices in that org.
Default path: env vars, no per-org setup
One script, run on endpoints in every client organization. Each run picks up that device's org ID and name.
- PowerShell, Windows, 64-bit, Run as System.
- Detect the
SparkLogsAgentservice, not the MSI product code (the GUID changes per version). Reruns should be no-ops when the service already exists. - Refuse if
NINJA_ORGANIZATION_IDis empty. - Download the current hash-pinned Windows x64 EXE (below).
Abort on SHA-256 mismatch (
Get-FileHash). If your workspace release ring is not this build, take the URL and hash from the deploy wizard instead. - Silent install with
EULA=ACCEPT, your Clients registration token,RMMCLIENTIDfrom the org ID, andCLIENTNAMEfrom the org name. - Treat exit
0,3010, and1641as success.
Wrap Start-Process -Wait -PassThru so PowerShell waits for the installer.
See Troubleshooting.
Property names are RMMCLIENTID, CLIENTNAME, and PSACLIENTID (not CLIENT_NAME).
The URL and hash are the current published Windows x64 EXE (1.9.6). Replace only the registration token from the deploy wizard. This is a starting point to adapt, not a supported partner package.
$ErrorActionPreference = 'Stop'
if (Get-Service 'SparkLogsAgent' -ErrorAction SilentlyContinue) { exit 0 }
$RmmClientId = [string]$env:NINJA_ORGANIZATION_ID
$ClientName = [string]$env:NINJA_ORGANIZATION_NAME
if ([string]::IsNullOrWhiteSpace($RmmClientId)) {
throw 'NINJA_ORGANIZATION_ID is empty. Run this script from NinjaOne automation, not Task Scheduler.'
}
$url = 'https://cdn.sparklogs.app/agent/data/1.9.6/windows_x86_64/sha256_43dffe5b77221bd6a87689b7f960938cb39cc67b8e45d4551226c9a0d2d7fe99/SparkLogsAgentSetup-x64-1.9.6.exe'
$sha = '43dffe5b77221bd6a87689b7f960938cb39cc67b8e45d4551226c9a0d2d7fe99'
$token = 'us_97...'
$exe = Join-Path $env:TEMP 'SparkLogsAgentSetup.exe'
Invoke-WebRequest $url -OutFile $exe -UseBasicParsing
if ((Get-FileHash $exe -Algorithm SHA256).Hash -ne $sha) { throw 'SHA-256 mismatch' }
$installArgs = @(
'/qn', '/norestart',
'EULA=ACCEPT',
"REGISTRATION_TOKEN=$token",
"RMMCLIENTID=$RmmClientId",
"CLIENTNAME=`"$ClientName`""
)
$p = Start-Process $exe -Wait -PassThru -ArgumentList $installArgs
if ($p.ExitCode -notin 0, 3010, 1641) {
throw "Install failed with exit code $($p.ExitCode)"
}
The MSI form uses the same properties on msiexec /i ... if you prefer MSI.
EXE is the usual RMM download; see Download and verify.
Tokens and NinjaOne targeting
Registration tokens are scoped to a SparkLogs organization subtree.
SparkLogs auto-creates a direct child of the token's organization for each new RMMCLIENTID.
NinjaOne does not nest client organizations under each other, so that child is one SparkLogs org per NinjaOne org, not a copy of a NinjaOne tree.
You still need two SparkLogs registration tokens whenever Internal IT and Clients are separate SparkLogs parents:
| Endpoints | SparkLogs token | Install properties |
|---|---|---|
| Customer devices | Minted on Clients | RMMCLIENTID + CLIENTNAME (script above) |
| Your own devices | Minted on Internal IT | USEPARENTORG=1 (no client ids) |
NinjaOne device groups often cannot exclude your internal organization. If a "all Windows" group includes Internal IT, either:
- Use two automations (client script vs internal script), or
- In the client script, no-op (exit 0) when
NINJA_ORGANIZATION_IDis your internal org.
Pilot before fleet:
- One internal box with the Internal IT token.
- One machine in one small real client org with the Clients token.
- In SparkLogs, confirm the agent landed under the parent you intended, with that client's name.
If a client box landed under Internal IT, the token was minted on the wrong node. Mint from Clients, purge-uninstall the test box, delete the misplaced SparkLogs org if you created one, then sweep.
When one NinjaOne org is not one SparkLogs client
Default env vars are enough when each NinjaOne organization is exactly one client you want in SparkLogs.
Walk your org list before the first sweep and decide:
- Several NinjaOne organizations for one legal client (holding company vs subsidiary, or a brand split into two Ninja orgs): each org ID becomes its own SparkLogs child unless you override with a shared custom-field ID (below).
- One NinjaOne organization that should be several SparkLogs orgs: org ID is too coarse. You would need a different key (for example a custom field you maintain, or
NINJA_LOCATION_IDif you truly want one SparkLogs org per location). Location-as-key is uncommon; only do it on purpose. - Device groups that mix clients: groups do not change
NINJA_ORGANIZATION_ID. The script still keys by the device's org. Mixing Internal IT and Clients in one group is a token problem, not an ID problem.
NINJA_ORGANIZATION_ID is local to this NinjaOne instance.
A tenant migration or merge can change it.
If you already have SparkLogs orgs keyed to old IDs, update those orgs' RMM client IDs before you resume installs; see Changing RMM or PSA platforms.
Advanced: PSA ID or a shared custom field
Use this when you already have a PSA company ID on the NinjaOne org, or when several NinjaOne orgs must land in one SparkLogs client.
- Create or reuse an organization custom field (Text). Enable automation/script read on that field. Type the field name yourself if the UI copies a label; names are immutable.
- Prefer that field when it is non-empty; otherwise fall back to
NINJA_ORGANIZATION_ID. - Pass a PSA value as
PSACLIENTID(and still passRMMCLIENTIDunless you are matching on PSA only).
SparkLogs links on RMMCLIENTID and/or PSACLIENTID, not on a SparkLogs org UUID by itself.
If you already created client organizations in SparkLogs, set each org's RMM and/or PSA client ID to the value the script will send so installs link instead of creating a duplicate.
You can store that same value in NinjaOne (Ninja org ID, PSA company ID, or a SparkLogs org ID you copied onto the SparkLogs org as its RMM client ID).
Example: prefer a PSA field named psaClientId (replace with your field name), fall back to the Ninja org ID.
Ninja-Property-Get reads a custom field when the NinjaOne agent runs the script.
$RmmClientId = [string]$env:NINJA_ORGANIZATION_ID
$ClientName = [string]$env:NINJA_ORGANIZATION_NAME
$PsaClientId = $null
if (Get-Command Ninja-Property-Get -ErrorAction SilentlyContinue) {
$PsaClientId = Ninja-Property-Get psaClientId 2>$null
}
if ([string]::IsNullOrWhiteSpace($RmmClientId) -and [string]::IsNullOrWhiteSpace($PsaClientId)) {
throw 'No RMM or PSA client id (NINJA_ORGANIZATION_ID empty and psaClientId unset).'
}
# If several Ninja orgs should be one SparkLogs client, put that shared id in the
# custom field and use it as RMMCLIENTID instead of $env:NINJA_ORGANIZATION_ID:
# if (-not [string]::IsNullOrWhiteSpace($PsaClientId)) { $RmmClientId = $PsaClientId }
$installArgs = @(
'/qn', '/norestart',
'EULA=ACCEPT',
"REGISTRATION_TOKEN=$token",
"CLIENTNAME=`"$ClientName`""
)
if (-not [string]::IsNullOrWhiteSpace($RmmClientId)) {
$installArgs += "RMMCLIENTID=$RmmClientId"
}
if (-not [string]::IsNullOrWhiteSpace($PsaClientId)) {
$installArgs += "PSACLIENTID=$PsaClientId"
}
If the PSA ID lives on a Documentation template rather than an organization custom field, use NinjaOne's documentation cmdlets (Ninja-Property-Docs-Get-Single) instead of Ninja-Property-Get.
The field still needs script read permission.
Env vars after rename or move
NinjaOne documents that if an injected variable does not show the new value, reboot the device and rerun.
That refresh only affects the next script run.
SparkLogs chooses the organization at first successful register.
Renaming a NinjaOne org updates CLIENTNAME on a new install, but does not move an already enrolled agent.
Moving a device to a different NinjaOne organization does not re-key SparkLogs until you uninstall with PURGE_STATE=1 and install again with the new ids.
Fleet task
Target a dynamic all-Windows device group so new machines onboard without a new task. Keep Internal IT out of the client automation (or no-op it in script). Run hourly on day one (catch offline boxes), then daily once almost every run is "already installed." That daily task is ongoing onboarding.
The default path does not require you to populate custom fields before a new NinjaOne organization appears. The advanced path does: fill the PSA or shared-ID field before you expect those installs to match an existing SparkLogs org.
Uninstall and identity
NinjaOne's native Uninstall Program usually leaves SparkLogs enrollment state on disk. The next install can reattach to the old agent/org even if you changed the token.
For a true re-key or offboarding, uninstall with PURGE_STATE=1 as documented on Manage and verify.
Deleted SparkLogs organizations can remain in an archive window and still match on register; wait or confirm before assuming a name is free.