Dopo aver rimosso l’agente NinjaOne da un PC, la reinstallazione può fallire con un messaggio piuttosto fuorviante:
Errore 1316. L’account specificato esiste già.
Non c’entrano né gli utenti Windows né l’account NinjaOne. È l’errore 1316 di Windows Installer: il ProductCode dell’MSI risulta ancora registrato nel sistema, anche se file, servizi e configurazione dell’agente sono già stati eliminati. Il nuovo installer trova il “vecchio” prodotto registrato con un nome di pacchetto diverso e si rifiuta di procedere.
Dove si nasconde il problema
La classica pulizia (servizi NinjaRMMAgent, cartella C:\ProgramData\NinjaRMMAgent, chiave HKLM\SOFTWARE\WOW6432Node\NinjaRMM LLC) non basta. Nel mio caso erano rimaste due chiavi di Windows Installer:
HKLM\SOFTWARE\Classes\Installer\Products\<GUID compresso>
HKLM\SOFTWARE\Classes\Installer\Features\<GUID compresso>
Il “GUID compresso” è il ProductCode MSI riscritto in un formato particolare (gruppi invertiti). Per trovarlo:
Get-ChildItem 'HKLM:\SOFTWARE\Classes\Installer\Products' |
? { (Get-ItemProperty $_.PSPath).ProductName -match 'Ninja' } |
select PSChildName
Eliminate quelle due chiavi, l’agente si è reinstallato al primo colpo:
reg delete "HKLM\SOFTWARE\Classes\Installer\Features\<GUID compresso>" /f
reg delete "HKLM\SOFTWARE\Classes\Installer\Products\<GUID compresso>" /f
Lo script: rimozione completa di NinjaOne
Per non doverlo fare a mano ogni volta ho scritto uno script che esegue la pulizia completa:
- disattiva l’Uninstall Prevention e tenta la disinstallazione MSI pulita
- ferma ed elimina servizi e processi Ninja (agent, NMS, NinjaRemote)
- rimuove cartelle e chiavi di configurazione (forzando i permessi se necessario)
- rimuove la registrazione Windows Installer (
Products,Features,UserData,UpgradeCodes, vociUninstall) e l’MSI in cache inC:\Windows\Installer - verifica che non sia rimasto nulla e salva un log in
C:\Windows\Temp.
Si esegue da PowerShell come Amministratore. Consiglio di lanciarlo prima con -WhatIf per vedere cosa verrebbe rimosso:
Set-ExecutionPolicy -Scope Process Bypass -Force
.\Remove-NinjaOneAgent.ps1 -WhatIf
.\Remove-NinjaOneAgent.ps1
Remove-NinjaOneAgent.ps1
<#
.SYNOPSIS
Rimozione completa dell'agente NinjaOne (NinjaRMM) da Windows, inclusa la
registrazione MSI orfana che causa l'errore 1316 in reinstallazione.
.DESCRIPTION
Risolve il classico "Errore 1316: L'account specificato esiste già" quando si
reinstalla l'agente NinjaOne dopo una rimozione incompleta.
Lo script:
1. disattiva l'Uninstall Prevention e tenta la disinstallazione MSI pulita
2. ferma ed elimina servizi e processi Ninja (agent, NMS, NinjaRemote)
3. rimuove cartelle e chiavi di configurazione
4. rimuove la registrazione Windows Installer (Products, Features, UserData,
UpgradeCodes, Uninstall) e l'MSI in cache in C:\Windows\Installer
Prima di reinstallare, eliminare il dispositivo anche dalla console NinjaOne.
.PARAMETER WhatIf
Mostra cosa verrebbe rimosso senza toccare nulla.
.EXAMPLE
.\Remove-NinjaOneAgent.ps1 -WhatIf
.\Remove-NinjaOneAgent.ps1
.NOTES
Autore : Enrico Fabbri
Uso : PowerShell 5.1+ come Amministratore. Script non ufficiale, usalo a tuo rischio.
#>
[CmdletBinding(SupportsShouldProcess)]
param(
# Nomi prodotto considerati "Ninja" (evita falsi positivi con altri software)
[string]$Pattern = 'NinjaRMM|NinjaOne|NinjaRemote'
)
#region Prerequisiti
$principal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Error 'Esegui lo script da PowerShell come Amministratore.'; exit 1
}
$log = Join-Path $env:windir "Temp\Remove-NinjaOneAgent_$(Get-Date -f yyyyMMdd_HHmmss).log"
Start-Transcript -Path $log | Out-Null
#endregion
#region Helper
# Converte ProductCode <-> GUID "compresso" usato da Windows Installer (funzione simmetrica)
function Convert-MsiGuid([string]$Guid) {
$h = $Guid -replace '[{}-]', ''
$rev = { param($s) -join ($s.ToCharArray()[($s.Length - 1)..0]) }
$out = (& $rev $h.Substring(0, 8)) + (& $rev $h.Substring(8, 4)) + (& $rev $h.Substring(12, 4))
for ($i = 16; $i -lt 32; $i += 2) { $out += "$($h[$i + 1])$($h[$i])" } # stringa, non somma di [char]
$out.ToUpper()
}
function Format-Guid([string]$h) {
'{{{0}-{1}-{2}-{3}-{4}}}' -f $h.Substring(0,8), $h.Substring(8,4), $h.Substring(12,4), $h.Substring(16,4), $h.Substring(20,12)
}
function Remove-Key {
[CmdletBinding(SupportsShouldProcess)] param([string]$Path)
if (Test-Path $Path) {
if ($PSCmdlet.ShouldProcess($Path, 'Rimuovi chiave')) {
Remove-Item $Path -Recurse -Force -ErrorAction Continue
Write-Host " [-] $Path" -ForegroundColor Yellow
}
}
}
function Remove-PathForce {
[CmdletBinding(SupportsShouldProcess)] param([string]$Path)
if (Test-Path $Path) {
if ($PSCmdlet.ShouldProcess($Path, 'Rimuovi cartella/file')) {
takeown /f $Path /r /d y 2>$null | Out-Null
icacls $Path /grant '*S-1-5-32-544:F' /t /c /q 2>$null | Out-Null # Administrators (SID, indipendente dalla lingua)
Remove-Item $Path -Recurse -Force -ErrorAction Continue
Write-Host " [-] $Path" -ForegroundColor Yellow
}
}
}
#endregion
$uninstallRoots = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$classesProducts = 'HKLM:\SOFTWARE\Classes\Installer\Products'
#region 1. Individua i ProductCode MSI di Ninja
Write-Host "`n[1] Ricerca registrazioni MSI..." -ForegroundColor Cyan
$packed = [System.Collections.Generic.HashSet[string]]::new()
Get-ChildItem $classesProducts -ErrorAction SilentlyContinue |
Where-Object { (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).ProductName -match $Pattern } |
ForEach-Object { [void]$packed.Add($_.PSChildName) }
Get-ChildItem $uninstallRoots -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -match '^\{[0-9A-F-]{36}\}$' -and
(Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).DisplayName -match $Pattern } |
ForEach-Object { [void]$packed.Add((Convert-MsiGuid $_.PSChildName)) }
foreach ($p in $packed) { Write-Host " ProductCode $(Format-Guid (Convert-MsiGuid $p)) (packed $p)" }
if (-not $packed.Count) { Write-Host ' Nessuna registrazione MSI trovata.' }
#endregion
#region 2. Disinstallazione pulita
Write-Host "`n[2] Tentativo di disinstallazione pulita..." -ForegroundColor Cyan
$agentExe = Get-ChildItem 'C:\Program Files (x86)', 'C:\Program Files' -Directory -ErrorAction SilentlyContinue |
ForEach-Object { Join-Path $_.FullName 'NinjaRMMAgent.exe' } | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($agentExe -and $PSCmdlet.ShouldProcess($agentExe, 'Disattiva Uninstall Prevention')) {
Start-Process $agentExe -ArgumentList '-disableUninstallPrevention', 'NOUI' -Wait -ErrorAction SilentlyContinue
}
foreach ($p in $packed) {
$code = Format-Guid (Convert-MsiGuid $p)
if ($PSCmdlet.ShouldProcess($code, 'msiexec /x')) {
$r = Start-Process msiexec.exe -ArgumentList "/x $code /qn /norestart" -Wait -PassThru
Write-Host " msiexec /x $code -> exit $($r.ExitCode)"
}
}
#endregion
#region 3. Servizi e processi
Write-Host "`n[3] Servizi e processi..." -ForegroundColor Cyan
$svc = Get-CimInstance Win32_Service | Where-Object {
$_.Name -in 'NinjaRMMAgent', 'nmsmanager', 'ncstreamer', 'lockhart' -or $_.PathName -match 'NinjaRMMAgent|NinjaRemote|ncstreamer'
}
foreach ($s in $svc) {
if ($PSCmdlet.ShouldProcess($s.Name, 'Stop + delete servizio')) {
sc.exe stop $s.Name 2>$null | Out-Null
sc.exe delete $s.Name | Out-Null
Write-Host " [-] servizio $($s.Name)" -ForegroundColor Yellow
}
}
Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.Path -match 'NinjaRMMAgent|NinjaRemote|ncstreamer' } |
ForEach-Object { if ($PSCmdlet.ShouldProcess($_.Name, 'Kill')) { Stop-Process $_ -Force -ErrorAction SilentlyContinue } }
Start-Sleep -Seconds 2
#endregion
#region 4. File e cartelle
Write-Host "`n[4] Cartelle..." -ForegroundColor Cyan
$dirs = @("$env:ProgramData\NinjaRMMAgent", "$env:ProgramFiles\NinjaRemote", "${env:ProgramFiles(x86)}\NinjaRemote")
$dirs += Get-ChildItem 'C:\Program Files (x86)', 'C:\Program Files' -Directory -ErrorAction SilentlyContinue |
Where-Object { Test-Path (Join-Path $_.FullName 'NinjaRMMAgent.exe') } | ForEach-Object FullName
$dirs | Select-Object -Unique | ForEach-Object { Remove-PathForce $_ }
#endregion
#region 5. Configurazione agente
Write-Host "`n[5] Chiavi di configurazione..." -ForegroundColor Cyan
'HKLM:\SOFTWARE\WOW6432Node\NinjaRMM LLC', 'HKLM:\SOFTWARE\NinjaRMM LLC' | ForEach-Object { Remove-Key $_ }
#endregion
#region 6. Registrazione Windows Installer (causa dell'errore 1316)
Write-Host "`n[6] Registrazione Windows Installer..." -ForegroundColor Cyan
foreach ($p in $packed) {
$code = Format-Guid (Convert-MsiGuid $p)
# MSI in cache (C:\Windows\Installer\xxxx.msi)
$ud = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\$p"
$cached = (Get-ItemProperty "$ud\InstallProperties" -ErrorAction SilentlyContinue).LocalPackage
if ($cached -and (Test-Path $cached) -and $PSCmdlet.ShouldProcess($cached, 'Rimuovi MSI in cache')) {
Remove-Item $cached -Force; Write-Host " [-] $cached" -ForegroundColor Yellow
}
Remove-Key "HKLM:\SOFTWARE\Classes\Installer\Products\$p"
Remove-Key "HKLM:\SOFTWARE\Classes\Installer\Features\$p"
Remove-Key $ud
foreach ($root in $uninstallRoots) { Remove-Key "$root\$code" }
# UpgradeCodes che referenziano il prodotto (il prodotto è un *valore* della chiave)
Get-ChildItem 'HKLM:\SOFTWARE\Classes\Installer\UpgradeCodes' -ErrorAction SilentlyContinue | ForEach-Object {
$props = (Get-ItemProperty $_.PSPath).PSObject.Properties.Name
if ($props -contains $p -and $PSCmdlet.ShouldProcess("$($_.PSChildName)\$p", 'Rimuovi riferimento UpgradeCode')) {
Remove-ItemProperty $_.PSPath -Name $p -Force
# se non restano altri prodotti, elimina la chiave
$left = (Get-ItemProperty $_.PSPath).PSObject.Properties.Name | Where-Object { $_ -match '^[0-9A-F]{32}$' }
if (-not $left) { Remove-Item $_.PSPath -Recurse -Force }
Write-Host " [-] UpgradeCode $($_.PSChildName)" -ForegroundColor Yellow
}
}
}
# Voci "Uninstall" non MSI rimaste orfane
Get-ChildItem $uninstallRoots -ErrorAction SilentlyContinue |
Where-Object { (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).DisplayName -match $Pattern } |
ForEach-Object { Remove-Key $_.PSPath }
#endregion
#region 7. Verifica finale
Write-Host "`n[7] Verifica..." -ForegroundColor Cyan
$residui = @()
$residui += 'HKLM:\SOFTWARE\WOW6432Node\NinjaRMM LLC', 'HKLM:\SOFTWARE\NinjaRMM LLC', "$env:ProgramData\NinjaRMMAgent" | Where-Object { Test-Path $_ }
$residui += Get-ChildItem $classesProducts -ErrorAction SilentlyContinue |
Where-Object { (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).ProductName -match $Pattern } | ForEach-Object Name
$residui += Get-CimInstance Win32_Service | Where-Object { $_.PathName -match 'NinjaRMMAgent|NinjaRemote|ncstreamer' } | ForEach-Object { "Servizio: $($_.Name)" }
if ($WhatIfPreference) {
Write-Host 'Modalità -WhatIf: nessuna modifica eseguita.' -ForegroundColor Green
} elseif ($residui) {
Write-Host 'Residui ancora presenti (riavvia e rilancia, eventualmente in modalità provvisoria):' -ForegroundColor Red
$residui | ForEach-Object { Write-Host " $_" -ForegroundColor Red }
} else {
Write-Host 'Pulizia completata. Elimina il device dalla console NinjaOne e reinstalla l''agente.' -ForegroundColor Green
}
Write-Host "Log: $log"
Stop-Transcript | Out-Null
#endregion
Prima di reinstallare
- eliminate il dispositivo anche dalla console NinjaOne (Delete, non Archive), altrimenti l’agente può riagganciarsi al vecchio record
- scaricate un installer nuovo dalla location corretta
- se qualcosa resta bloccato, riavviate e rilanciate lo script (eventualmente in modalità provvisoria).
Script non ufficiale, fornito “così com’è”: testatelo prima in un ambiente di prova.
Vi è capitato l’errore 1316 con NinjaOne o con altri agenti RMM? Raccontate nei commenti come l’avete risolto: può essere utile ad altri.


App e giochi che si chiudono dopo KB5124010: il bug del decoder audio AC-3 su Windows 11
Lascia un commento