• Passa al contenuto principale
  • Skip to header right navigation
  • Skip to site footer
Fabbri Enrico

Fabbri Enrico

Appunti, idee, qualsiasi cosa possa servire

  • IT
    • MacOS
    • Windows
    • Synology
    • Networking
    • Ubiquiti
    • Storage
    • Web
    • VMWare
  • Le mie passioni
    • Tesla
    • Apple
    • DJI
    • BMW GS
    • Bambu Lab
    • Xbox
  • Contatti
Portatile Dell aperto con il desktop di Windows su un piano bianco

NinjaOne: “Errore 1316 – L’account specificato esiste già” in reinstallazione dell’agente

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:

  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 (forzando i permessi se necessario)
  4. rimuove la registrazione Windows Installer (Products, Features, UserData, UpgradeCodes, voci Uninstall) e l’MSI in cache in C:\Windows\Installer
  5. 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.

Enrico Fabbri

Enrico Fabbri, classe 1989, appassionato da sempre da tutto ciò che circonda il mondo della tecnologia.
Se una cosa funziona, sento la necessità di scoprire come e perché.

Post precedente:Cuffie over-ear appese a un supporto sulla scrivania accanto a una cassa acustica bianca e al bordo di un monitorApp e giochi che si chiudono dopo KB5124010: il bug del decoder audio AC-3 su Windows 11

Interazioni del lettore

Lascia un commento Annulla risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.