NinjaOne
One policy scheduled script covers your whole fleet, client devices and your own. Fill in three values at the top of the script. Nothing else changes from client to client. Deploy it as a scheduled script, because the script reads each device's NinjaOne organization to choose its SparkLogs organization.
As with every RMM mass-deploy, you need a verified installer and the right deployment scope.
How the script knows which SparkLogs organization a device belongs to
When NinjaOne runs a Windows automation script, it injects the device's NinjaOne 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 | RMMCLIENTID (numeric org ID on this NinjaOne instance) |
NINJA_ORGANIZATION_NAME | CLIENTNAME (display label only, not a link key) |
NinjaOne's model is organization → location → device.
NinjaOne organizations are a flat list of clients plus your own internal org.
Using NINJA_ORGANIZATION_ID maps one NinjaOne organization (every location under it) to one SparkLogs organization (for most MSPs, a client).
Device groups and saved searches are targeting only; they never change NINJA_ORGANIZATION_ID.
Template values you fill in
If you followed the workspace setup wizard as an MSP, you have exactly two top-level SparkLogs organizations, Clients and Internal IT, each with its own registration token. The script picks the token from the device's NinjaOne organization, so it works as-is once you swap in three values:
| Value | Where to get it |
|---|---|
| Clients registration token | SparkLogs deploy wizard, token minted on Clients |
| Internal IT registration token | SparkLogs deploy wizard, token minted on Internal IT |
| NinjaOne org ID of your internal org | NinjaOne Administration → Organizations, hover your own org's row, ellipsis, Copy Org ID |
The branch is a pattern you can extend: add more org IDs and tokens if you later want a separate SparkLogs parent for a group of clients.
The script
PowerShell, Windows, 64-bit, Run as System.
It detects the SparkLogsAgent service so reruns are no-ops, refuses to run outside NinjaOne automation, verifies the download by SHA-256, and treats exit 0, 3010, and 1641 as success.
The URL and hash are the current published Windows x64 EXE (1.14.13). If your workspace release ring is not this build, take the URL and hash from the deploy wizard instead. This is a starting point to adapt, not a supported partner package.
$ErrorActionPreference = 'Stop'
# ============================================================
# FILL IN THESE THREE VALUES BEFORE YOU SAVE THE SCRIPT
# ============================================================
$ClientsToken = '<<< PASTE CLIENTS REGISTRATION TOKEN >>>'
$InternalItToken = '<<< PASTE INTERNAL IT REGISTRATION TOKEN >>>'
$InternalNinjaOrgId = '<<< PASTE NINJAONE ORG ID OF YOUR INTERNAL ORG >>>'
# ============================================================
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.'
}
if ($RmmClientId -eq $InternalNinjaOrgId) {
$token = $InternalItToken
} else {
$token = $ClientsToken
}
$url = 'https://cdn.sparklogs.app/agent/data/1.14.13/windows_x86_64/sha256_c01da4bb31617cef850abda814f49037b38a9620271dc79f323d542f4cf550be/SparkLogsAgentSetup-x64-1.14.13.exe'
$sha = 'c01da4bb31617cef850abda814f49037b38a9620271dc79f323d542f4cf550be'
$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)"
}
Property names are RMMCLIENTID, CLIENTNAME, and PSACLIENTID (not CLIENT_NAME).
Start-Process -Wait -PassThru is what makes PowerShell wait for the installer; see Troubleshooting.
The MSI form uses the same properties on msiexec /i ... if you prefer MSI; see Download and verify.
Add it in NinjaOne
- Administration → Library → Automation → Add → New Script. Language PowerShell, architecture 64-bit, operating system Windows, run as System.
- Paste the script, fill in the template values at the top, and save it.
Pilot on two machines
- Run the script on one machine in your internal NinjaOne organization.
- Run it on one machine that belongs to one small client.
- In SparkLogs, confirm each agent landed under the parent you intended, with that client's name.
If a client machine landed under Internal IT, its NinjaOne org ID matched $InternalNinjaOrgId, or the tokens are swapped.
- Fix the script values so later installs land correctly.
- Move the test machine with Change Ownership on Configure > Agents. Reinstalling it with the corrected token does not move its record. See Moving devices and organizations.
- Delete the misplaced SparkLogs organization if one was created.
Roll out to the fleet
- Add the script to a policy (or a scheduled task) that targets a dynamic all-Windows device group, so new machines onboard without a new task.
- Schedule it hourly on day one to reach machines that were offline, then daily once almost every run reports the agent is already installed. The daily run onboards new machines from then on.
You do not need to exclude your internal NinjaOne organization from the target group. The script sends internal devices to the Internal IT token on its own. Internal devices appear under Internal IT in a sub-organization named for your NinjaOne org, so it stays clear which machines are yours.
Linking to SparkLogs organizations you already created
SparkLogs auto-creates a direct child of the token's organization for each new RMMCLIENTID.
If you already created client organizations in SparkLogs, set each one's RMM client ID to the NinjaOne org ID the script will send, so installs link instead of creating a duplicate.
SparkLogs links on RMMCLIENTID and/or PSACLIENTID, not on a SparkLogs organization UUID by itself.
NINJA_ORGANIZATION_ID is local to this NinjaOne instance, and a tenant migration or merge can change it.
If you already have SparkLogs organizations keyed to old IDs, update those organizations' RMM client IDs before you resume installs; see Changing RMM or PSA platforms.
Optional: a PSA ID or a shared ID
Use this when you already keep a PSA company ID on the NinjaOne organization, or when several NinjaOne organizations must land in one SparkLogs organization.
- Create or reuse a NinjaOne organization custom field (Text) and enable automation/script read on it. Type the field name yourself if the UI copies a label; names are immutable.
- Read it with
Ninja-Property-Getand prefer it when non-empty. Pass a PSA value asPSACLIENTID, or use a shared value asRMMCLIENTIDwhen several NinjaOne organizations map to one SparkLogs organization.
$PsaClientId = $null
if (Get-Command Ninja-Property-Get -ErrorAction SilentlyContinue) {
$PsaClientId = Ninja-Property-Get psaClientId 2>$null
}
if (-not [string]::IsNullOrWhiteSpace($PsaClientId)) {
$installArgs += "PSACLIENTID=$PsaClientId"
}
Fill the field before you expect those installs to match an existing SparkLogs organization.
If the PSA ID lives on a Documentation template rather than a NinjaOne organization custom field, use Ninja-Property-Docs-Get-Single instead.
The field still needs script read permission.
One NinjaOne organization that should become several SparkLogs organizations needs a different key entirely, for example a custom field you maintain, or NINJA_LOCATION_ID if you truly want one SparkLogs organization per location.
Location-as-key is uncommon; only do it on purpose.
After a rename or a move in NinjaOne
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, and the values the script sends decide the SparkLogs organization for a device's first enrollment only.
- Renaming a NinjaOne organization updates
CLIENTNAMEon a new install. It does not rename or move an agent that is already enrolled. - Moving a device to a different NinjaOne organization does not move it in SparkLogs. Move it with Change Ownership on Configure > Agents; see Moving devices and organizations.
- Reinstalling with a different client's token does not move a device. A reinstall that keeps local enrollment data stays on its record. A wipe or purge enrolls the endpoint where the script's values point, as a new device when its record belongs to another SparkLogs organization. The token never takes over that record.
- Once you move a device with Change Ownership, it stays there while it keeps its local enrollment data. Move it in NinjaOne too, so a later reimage or nightly restore sends the new client's values.
Uninstall and identity
- NinjaOne's native Uninstall Program leaves SparkLogs enrollment state on disk. The next install continues the same enrollment.
- Uninstalling with
PURGE_STATE=1clears that local state. The next install returns the endpoint to its device record when the script's values point at the SparkLogs organization that holds it. See Uninstall and reinstall. - To change which SparkLogs organization owns a device, use Change Ownership. To stop collecting from a machine for good, delete it on Configure > Agents; that holds through wipes and reinstalls until you undelete it. See Moving devices and organizations.
- A SparkLogs organization you deleted still matches while it is in its archive window, and an install that resolves to it fails rather than creating a duplicate. Undelete it, or confirm it has cleared, before you reuse the name.