Im publishing this and the related note
The Claude follow up made me laugh
Check in C:\Program Files (x86)\NinjaOne for an uninstaller. If it’s there, it worked when the Control Panel one didn’t.
Note: This agent hadn’t checked in for 3 days and had been removed from the website — unclear if not checking in after removal affects which uninstall.exe works.
Otherwise run:
"C:\Program Files (x86)\NinjaOne\NinjaRMMAgent.exe" -disableUninstallPrevention
Then run the Control Panel uninstall, or run:
Script that actually worked.
<#
.SYNOPSIS
Removes the NinjaOne (NinjaRMM) Agent from a Windows endpoint.
.DESCRIPTION
Official-style NinjaOne agent removal script (based on NinjaOne's published
"Remove an Endpoint Agent on Windows Using a Custom Script" guide), adapted
for direct local execution. It:
- Relaunches itself elevated if not already running as Administrator
- Disables uninstall prevention on the agent
- Runs the MSI uninstaller silently
- Stops/removes related services and processes
- Removes leftover install/data directories
- Cleans up registry remnants (including Ninja Remote, if present)
Logs everything to: C:\Windows\Temp\NinjaOneAgentRemoval.log
.NOTES
Run this in an elevated PowerShell session (or just run the .ps1 file;
it will prompt for elevation via UAC if needed).
#>
function Write-LogEntry {
param (
[Parameter(Mandatory = $true)]
[string]$Message
)
$LogPath = "$env:windir\temp\NinjaOneAgentRemoval.log"
$TimeStamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Add-Content -Path $LogPath -Value "$TimeStamp - $Message"
Write-Host "$TimeStamp - $Message"
}
function Uninstall-NinjaMSI {
$Arguments = @(
"/x$($UninstallString)"
'/quiet'
'/L*V'
"$env:windir\temp\NinjaRMMAgent_uninstall.log"
"WRAPPED_ARGUMENTS=`"--mode unattended`""
)
Start-Process "msiexec.exe" -ArgumentList $Arguments -Wait -NoNewWindow
Write-LogEntry 'Finished running uninstaller. Continuing to clean up...'
Start-Sleep 30
}
# Get current user context
$CurrentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
# Check that the user running the script is a member of the Administrator group
if (!($CurrentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator))) {
# UAC prompt will occur for the user to provide Administrator credentials and relaunch the session
Write-LogEntry 'This script must be run with administrative privileges. Relaunching with elevation...'
Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs
Exit
}
$ErrorActionPreference = "SilentlyContinue"
Write-LogEntry 'Beginning NinjaRMM Agent removal...'
Write-LogEntry "Path to log file: $env:windir\temp\NinjaOneAgentRemoval.log"
$NinjaRegPath = 'HKLM:\SOFTWARE\WOW6432Node\NinjaRMM LLC\NinjaRMMAgent'
$NinjaDataDirectory = "$($env:ProgramData)\NinjaRMMAgent"
$UninstallRegPath = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
$NinjaModulePath = "$env:ProgramFiles\WindowsPowerShell\Modules\NJCliPSh"
if (!([System.Environment]::Is64BitOperatingSystem)) {
$NinjaRegPath = 'HKLM:\SOFTWARE\NinjaRMM LLC\NinjaRMMAgent'
$UninstallRegPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
$NinjaInstallLocation = (Get-ItemPropertyValue $NinjaRegPath -Name Location -ErrorAction SilentlyContinue)
if ($NinjaInstallLocation) {
$NinjaInstallLocation = $NinjaInstallLocation.Replace('/', '\')
}
if (!$NinjaInstallLocation -or !(Test-Path "$($NinjaInstallLocation)\NinjaRMMAgent.exe")) {
$NinjaServicePath = ((Get-WmiObject Win32_Service | Where-Object { $_.Name -eq 'NinjaRMMAgent' }).PathName)
if ($NinjaServicePath) { $NinjaServicePath = $NinjaServicePath.Trim('"') }
if (!$NinjaServicePath -or !(Test-Path $NinjaServicePath)) {
Write-LogEntry 'Unable to locate Ninja installation path. Continuing with cleanup...'
}
else {
$NinjaInstallLocation = $NinjaServicePath | Split-Path
}
}
if ($NinjaInstallLocation -and (Test-Path "$($NinjaInstallLocation)\NinjaRMMAgent.exe")) {
Write-LogEntry 'Disabling uninstall prevention...'
Start-Process "$($NinjaInstallLocation)\NinjaRMMAgent.exe" -ArgumentList "-disableUninstallPrevention NOUI" -Wait -NoNewWindow
}
$UninstallString = (Get-ItemProperty $UninstallRegPath | Where-Object {
($_.DisplayName -eq 'NinjaRMMAgent') -and ($_.UninstallString -match 'msiexec')
}).UninstallString
if (!($UninstallString)) {
Write-LogEntry 'Unable to determine uninstall string. Continuing with cleanup...'
}
else {
$UninstallString = $UninstallString.Split('X')[1].Trim()
Write-LogEntry 'Running MSI uninstaller...'
Uninstall-NinjaMSI
}
$NinjaServices = @('NinjaRMMAgent', 'nmsmanager', 'lockhart')
$Processes = @('NinjaRMMAgent', 'NinjaRMMAgentPatcher', 'njbar', 'NinjaRMMProxyProcess64')
foreach ($Process in $Processes) {
$GetP = Get-Process $Process -ErrorAction SilentlyContinue
if ($GetP) {
try {
Stop-Process $GetP -Force -ErrorAction Stop
Write-LogEntry "Successfully stopped process: $($GetP.Name)"
}
catch {
Write-LogEntry "Unable to stop $($GetP.Name): $($_.Exception.Message). Continuing..."
}
}
}
foreach ($NS in $NinjaServices) {
if ($NS -eq 'lockhart' -and !(Test-Path "$NinjaInstallLocation\lockhart\bin\lockhart.exe")) {
continue
}
if (Get-Service $NS -ErrorAction SilentlyContinue) {
try {
Write-LogEntry "Stopping service $($NS)..."
Stop-Service $NS -Force -ErrorAction Stop
}
catch {
Write-LogEntry "Unable to stop $($NS) service: $($_.Exception.Message)"
Write-LogEntry 'Attempting to remove service...'
}
& sc.exe DELETE $NS | Out-Null
Start-Sleep 5
if (Get-Service $NS -ErrorAction SilentlyContinue) {
Write-LogEntry "Failed to remove $($NS) service. Continuing with remaining removal steps..."
}
else {
Write-LogEntry "Successfully removed $($NS) service."
}
}
}
if ($NinjaInstallLocation -and (Test-Path $NinjaInstallLocation)) {
Write-LogEntry "Removing Ninja installation directory: $($NinjaInstallLocation)"
try {
Remove-Item $NinjaInstallLocation -Recurse -Force -ErrorAction Stop
Write-LogEntry 'Successfully removed.'
}
catch {
Write-LogEntry "Failed to remove Ninja installation directory: $($_.Exception.Message)"
Write-LogEntry 'Continuing with removal attempt...'
}
}
if (Test-Path $NinjaDataDirectory) {
Write-LogEntry "Removing Ninja data directory: $($NinjaDataDirectory)"
try {
Remove-Item $NinjaDataDirectory -Recurse -Force -ErrorAction Stop
Write-LogEntry 'Successfully removed.'
}
catch {
Write-LogEntry "Failed to remove Ninja data directory: $($_.Exception.Message)"
Write-LogEntry 'Continuing with removal attempt...'
}
}
if (Test-Path $NinjaModulePath) {
Write-LogEntry "Removing Ninja PowerShell module directory: $($NinjaModulePath)"
try {
Remove-Item $NinjaModulePath -Recurse -Force -ErrorAction Stop
Write-LogEntry 'Successfully removed.'
}
catch {
Write-LogEntry "Failed to remove Ninja PowerShell module directory: $($_.Exception.Message)"
Write-LogEntry 'Continuing with removal attempt...'
}
}
# --- Registry cleanup ---
$MSIWrapperReg = 'HKLM:\SOFTWARE\WOW6432Node\EXEMSI.COM\MSI Wrapper\Installed'
$ProductInstallerReg = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products'
$HKCRInstallerReg = 'Registry::HKEY_CLASSES_ROOT\Installer\Products'
$RegKeysToRemove = [System.Collections.Generic.List[object]]::New()
(Get-ItemProperty $UninstallRegPath -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -eq 'NinjaRMMAgent' }).PSPath | ForEach-Object { $RegKeysToRemove.Add($_) }
(Get-ItemProperty $ProductInstallerReg -ErrorAction SilentlyContinue | Where-Object { $_.ProductName -eq 'NinjaRMMAgent' }).PSPath | ForEach-Object { $RegKeysToRemove.Add($_) }
(Get-ChildItem $MSIWrapperReg -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'NinjaRMMAgent' }).PSPath | ForEach-Object { $RegKeysToRemove.Add($_) }
Get-ChildItem $HKCRInstallerReg -ErrorAction SilentlyContinue | ForEach-Object {
if ((Get-ItemPropertyValue $_.PSPath -Name 'ProductName' -ErrorAction SilentlyContinue) -eq 'NinjaRMMAgent') {
$RegKeysToRemove.Add($_.PSPath)
}
}
$ProductInstallerKeys = Get-ChildItem $ProductInstallerReg -ErrorAction SilentlyContinue | Select-Object *
foreach ($Key in $ProductInstallerKeys) {
$KeyName = $($Key.Name).Replace('HKEY_LOCAL_MACHINE', 'HKLM:') + "\InstallProperties"
if (Get-ItemProperty $KeyName -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -eq 'NinjaRMMAgent' }) {
$RegKeysToRemove.Add($Key.PSPath)
}
}
Write-LogEntry 'Removing registry items if found...'
if (($RegKeysToRemove | Measure-Object).Count -gt 0) {
foreach ($RegKey in $RegKeysToRemove) {
if (!([string]::IsNullOrWhiteSpace($RegKey))) {
Write-LogEntry "Attempting to remove: $($RegKey)"
try {
Remove-Item $RegKey -Recurse -Force -ErrorAction Stop
Write-LogEntry 'Successfully removed.'
}
catch {
Write-LogEntry "Failed to remove registry key: $($_.Exception.Message)"
Write-LogEntry 'Continuing with removal...'
}
}
}
}
if (Test-Path $NinjaRegPath) {
try {
Write-LogEntry "Removing: $($NinjaRegPath)"
Get-Item ($NinjaRegPath | Split-Path -ErrorAction Stop) | Remove-Item -Recurse -Force -ErrorAction Stop
Write-LogEntry 'Successfully removed.'
}
catch {
Write-LogEntry "Failed to remove key: $($_.Exception.Message)"
Write-LogEntry 'Continuing with removal...'
}
}
Write-LogEntry 'Removal script completed. Please review the log if any errors were displayed.'Followup
Related: Ninja - Create List of Devices. This entry trails off mid-thought — the command after “or run:” never got written down, so the full sequence isn’t captured yet. Worth finishing the note next time this comes up, and maybe turning the disable-prevention + uninstall sequence into a one-liner in Scripts/ once the missing step is confirmed.