# --- TIGERWEB RMM Agent v7.0 (Native Edition) --- # Questo script installa l'agente RMM Nativo (No RustDesk) # Utilizza TigerWebAgent.exe per streaming diretto MJPEG over HTTP. param( [string]$Tenant = "Unknown", [string]$BackendUrl = $null, [string]$LocalAgentPath = $null, [string]$LocalInventoryPath = $null, [string]$LocalMonitorPath = $null, [string]$LocalPatchPath = $null, [string]$LocalUninstallPath = $null ) # Force TLS 1.2 globally for the entire script session [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Check Admin Privileges and Self-Elevate if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Warning "Questo script richiede privilegi di Amministratore!" Write-Host "Tentativo di elevazione dei privilegi..." -ForegroundColor Yellow $argParts = @( "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "`"$PSCommandPath`"" ) if ($Tenant) { $argParts += @("-Tenant", "`"$Tenant`"") } if ($LocalAgentPath) { $argParts += @("-LocalAgentPath", "`"$LocalAgentPath`"") } if ($LocalInventoryPath) { $argParts += @("-LocalInventoryPath", "`"$LocalInventoryPath`"") } if ($LocalMonitorPath) { $argParts += @("-LocalMonitorPath", "`"$LocalMonitorPath`"") } if ($LocalPatchPath) { $argParts += @("-LocalPatchPath", "`"$LocalPatchPath`"") } Start-Process powershell.exe -ArgumentList ($argParts -join " ") -Verb RunAs exit } # --- Configurazione --- $ServerUrl = "https://rmm.tools-gdpr-tigerweb.com" if ($BackendUrl) { $u = [string]$BackendUrl $u = $u.Trim() if ($u.EndsWith("/")) { $u = $u.TrimEnd("/") } if ($u) { $ServerUrl = $u } } $InstallDir = "C:\Program Files\TigerWeb RMM" $AgentExePath = "$InstallDir\TigerWebAgent.exe" $LogFile = "$InstallDir\rmm-agent.log" function Write-Log { param([string]$Message) $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $LogEntry = "[$Timestamp] $Message" Write-Host $LogEntry Add-Content -Path $LogFile -Value $LogEntry -ErrorAction SilentlyContinue } # #region debug-point A:debug-helper function Send-DebugEvent { param( [string]$HypothesisId, [string]$Message, $Data = $null ) try { $payload = @{ sessionId = "installer-reinstall" runId = "pre-fix" hypothesisId = [string]$HypothesisId location = "install-agent.ps1" msg = "[DEBUG] " + [string]$Message data = $Data ts = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() } | ConvertTo-Json -Depth 6 -Compress Invoke-RestMethod -Uri "http://127.0.0.1:7777/event" -Method Post -ContentType "application/json" -Body $payload | Out-Null } catch {} } # #endregion function Get-HardwareID { $uuid = (Get-WmiObject Win32_ComputerSystemProduct).UUID if (-not $uuid) { $uuid = (Get-WmiObject Win32_BIOS).SerialNumber } return $uuid } function New-AgentKey { try { $bytes = New-Object byte[] 24 [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) return (($bytes | ForEach-Object { $_.ToString("x2") }) -join "") } catch { try { return ([Guid]::NewGuid().ToString("n") + [Guid]::NewGuid().ToString("n")).Substring(0,48) } catch { return "" } } } function Ensure-AgentKeyFromServer { param( [string]$DeviceId, [string]$TenantName, [string]$BaseUrl, [string]$ConfigPath ) try { if (-not $DeviceId) { return $null } if (-not $BaseUrl) { return $null } $uri = "$BaseUrl/api/agent/telemetry" $payload = @{ deviceId = $DeviceId tenant = $TenantName hostname = $env:COMPUTERNAME metrics = @{} security = @{} } | ConvertTo-Json -Depth 6 -Compress $resp = Invoke-RestMethod -Uri $uri -Method Post -ContentType "application/json" -Body $payload -ErrorAction Stop $k = "" try { $k = [string]$resp.agentKey } catch { $k = "" } if (-not $k) { return $null } if ($ConfigPath -and (Test-Path $ConfigPath)) { try { $c = Get-Content $ConfigPath -Raw | ConvertFrom-Json $c.agentKey = $k if ($TenantName) { $c.tenant = $TenantName } if ($BaseUrl) { $c.serverUrl = $BaseUrl } Set-Content -Path $ConfigPath -Value ($c | ConvertTo-Json -Depth 6) -Force } catch {} } return $k } catch { return $null } } function Start-AgentUserSession { param([string]$ExePath) try { if (-not $ExePath -or -not (Test-Path $ExePath)) { return $false } Start-Process -FilePath $ExePath -ArgumentList "-Service" -WindowStyle Hidden -ErrorAction Stop | Out-Null return $true } catch { return $false } } function Protect-AgentDirectory { param([string]$Path) try { if (-not $Path -or -not (Test-Path $Path)) { return } & icacls $Path /inheritance:r /grant:r "NT AUTHORITY\SYSTEM:(OI)(CI)(F)" "BUILTIN\Administrators:(OI)(CI)(F)" "BUILTIN\Users:(OI)(CI)(RX)" /T /C | Out-Null Write-Log "ACL directory agent rinforzate: Users=RX, Admin/SYSTEM=F" } catch { Write-Log "Warning: hardening ACL cartella fallito." } } function Protect-AgentConfigFile { param([string]$Path) try { if (-not $Path -or -not (Test-Path $Path)) { return } & icacls $Path /inheritance:r /grant:r "NT AUTHORITY\SYSTEM:(F)" "BUILTIN\Administrators:(F)" "BUILTIN\Users:(R)" | Out-Null try { attrib +H $Path | Out-Null } catch {} Write-Log "ACL config.json rinforzate: Users=R, Admin/SYSTEM=F" } catch { Write-Log "Warning: hardening ACL config.json fallito." } } function Try-DownloadFile { param( [string]$Name, [string]$OutFile ) $urls = @( "$ServerUrl/$Name", "$ServerUrl/rmm-backend/$Name", "$ServerUrl/api/$Name" ) foreach ($u in $urls) { try { Write-Log "Tentativo download: $u" Invoke-WebRequest -Uri $u -OutFile $OutFile -UseBasicParsing -ErrorAction Stop if (Test-Path $OutFile) { return $true } } catch { Write-Log "Download fallito: $($_.Exception.Message)" } } return $false } function Ensure-LatestFromServer { param( [string]$Name, [string]$DestPath ) $tmp = Join-Path $env:TEMP ("tw_" + $Name + "_" + [Guid]::NewGuid().ToString("n")) try { $ok = Try-DownloadFile -Name $Name -OutFile $tmp if (-not $ok) { return $false } if (-not (Test-Path $DestPath)) { Copy-Item -Path $tmp -Destination $DestPath -Force return $true } $h1 = "" $h2 = "" try { $h1 = (Get-FileHash -Path $DestPath -Algorithm SHA256 -ErrorAction Stop).Hash } catch {} try { $h2 = (Get-FileHash -Path $tmp -Algorithm SHA256 -ErrorAction Stop).Hash } catch {} if ($h1 -and $h2 -and ($h1 -ne $h2)) { Copy-Item -Path $tmp -Destination $DestPath -Force Write-Log "Aggiornato da server: $Name" return $true } if (-not $h1 -or -not $h2) { $s1 = 0 $s2 = 0 try { $s1 = (Get-Item $DestPath).Length } catch {} try { $s2 = (Get-Item $tmp).Length } catch {} if ($s1 -ne $s2 -and $s2 -gt 0) { Copy-Item -Path $tmp -Destination $DestPath -Force Write-Log "Aggiornato da server (size diff): $Name" return $true } } } catch {} finally { try { Remove-Item $tmp -Force -ErrorAction SilentlyContinue } catch {} } return $false } function Remove-LegacyAutoruns { $runPaths = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" ) $runNames = @("TigerWebAgent", "TigerWebRMMAgent") foreach ($runPath in $runPaths) { foreach ($runName in $runNames) { try { Remove-ItemProperty -Path $runPath -Name $runName -ErrorAction SilentlyContinue } catch {} } } } function Remove-LegacyUninstallKeys { $keys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TigerWebRMMAgent", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\TigerWebRMMAgent" ) foreach ($key in $keys) { try { if (Test-Path $key) { Remove-Item -Path $key -Recurse -Force -ErrorAction SilentlyContinue } } catch {} } } function Stop-AgentProcesses { $processes = @("TigerWebAgent", "node") foreach ($procName in $processes) { Stop-Process -Name $procName -Force -ErrorAction SilentlyContinue taskkill /F /IM "$procName.exe" /T 2>$null } } function Wait-AgentProcessesExit { param([int]$MaxAttempts = 12) for ($i = 0; $i -lt $MaxAttempts; $i++) { $count = @((Get-Process -Name "TigerWebAgent" -ErrorAction SilentlyContinue)).Count if ($count -eq 0) { return $true } Start-Sleep -Milliseconds 750 Stop-AgentProcesses } return (@((Get-Process -Name "TigerWebAgent" -ErrorAction SilentlyContinue)).Count -eq 0) } function Prepare-AgentDestination { param([string]$Path) if (-not (Test-Path $Path)) { return } try { attrib -R -S -H $Path 2>$null | Out-Null } catch {} try { takeown /F $Path /A 2>$null | Out-Null } catch {} try { icacls $Path /grant Administrators:F SYSTEM:F 2>$null | Out-Null } catch {} try { Remove-Item -Path $Path -Force -ErrorAction Stop } catch {} if (Test-Path $Path) { try { $backup = $Path + ".old." + (Get-Date -Format "yyyyMMddHHmmss") Move-Item -Path $Path -Destination $backup -Force -ErrorAction Stop Write-Log "File agente precedente rinominato in $backup" } catch { throw "Impossibile liberare ${Path}: $($_.Exception.Message)" } } } # #region debug-point A:install-start Send-DebugEvent -HypothesisId "A" -Message "install-start" -Data @{ tenant = $Tenant installDirExists = (Test-Path $InstallDir) configExists = (Test-Path "$InstallDir\config.json") uninstallExists = (Test-Path "$InstallDir\uninstall.ps1") uninstallKeyExists = (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TigerWebRMM") legacyRunKeyExists = ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "TigerWebAgent" -ErrorAction SilentlyContinue) -ne $null) currentRunKeyExists = ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "TigerWebRMMAgent" -ErrorAction SilentlyContinue) -ne $null) } # #endregion # 1. Creazione Cartella e Permessi if (-not (Test-Path $InstallDir)) { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null } # Baseline ACL: the user session only needs read/execute; writes stay with SYSTEM/Admin. Protect-AgentDirectory -Path $InstallDir Write-Log "Inizio installazione TigerWeb RMM Agent v7.0 (Native)" # 2. Stop Existing Processes (Robust) Write-Host "Arresto processi esistenti..." Stop-AgentProcesses # Wait for file release Start-Sleep -Seconds 2 Remove-LegacyAutoruns [void](Wait-AgentProcessesExit) # #region debug-point B:post-stop Send-DebugEvent -HypothesisId "B" -Message "post-stop-existing-processes" -Data @{ remainingAgentProcessCount = @((Get-Process -Name "TigerWebAgent" -ErrorAction SilentlyContinue)).Count installDirExists = (Test-Path $InstallDir) agentExeExists = (Test-Path "$InstallDir\TigerWebAgent.exe") } # #endregion Remove-LegacyUninstallKeys # Cleanup old files Prepare-AgentDestination -Path "$InstallDir\TigerWebAgent.exe" # 2. Download Agente Nativo (C# Compiled EXE) try { # Ferma il processo se รจ in esecuzione per permettere la sovrascrittura Stop-Service -Name "TigerWebRMMAgent" -ErrorAction SilentlyContinue sc.exe stop TigerWebRMMAgent | Out-Null # Double check process kill Stop-AgentProcesses [void](Wait-AgentProcessesExit) Prepare-AgentDestination -Path $AgentExePath if ($LocalAgentPath -and (Test-Path $LocalAgentPath)) { Write-Log "Installazione da file locale: $LocalAgentPath" Copy-Item -Path $LocalAgentPath -Destination $AgentExePath -Force Write-Log "Copia completata." } else { Write-Log "Download TigerWebAgent.exe..." [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Primary: Direct from domain root (served by Node.js static handler) $DownloadUrl = "https://rmm.tools-gdpr-tigerweb.com/TigerWebAgent.exe" Write-Log "Tentativo 1: $DownloadUrl" try { Invoke-WebRequest -Uri $DownloadUrl -OutFile $AgentExePath -UseBasicParsing -ErrorAction Stop } catch { Write-Log "Tentativo 1 fallito ($($_.Exception.Message)). Provo percorso alternativo..." # Secondary: Backend folder $DownloadUrl = "https://rmm.tools-gdpr-tigerweb.com/rmm-backend/TigerWebAgent.exe" try { Invoke-WebRequest -Uri $DownloadUrl -OutFile $AgentExePath -UseBasicParsing -ErrorAction Stop } catch { Write-Log "Tentativo 2 fallito. Provo via API..." # Tertiary: API $DownloadUrl = "https://rmm.tools-gdpr-tigerweb.com/api/TigerWebAgent.exe" try { Invoke-WebRequest -Uri $DownloadUrl -OutFile $AgentExePath -UseBasicParsing -ErrorAction Stop } catch { Write-Log "ERRORE CRITICO: Impossibile scaricare TigerWebAgent.exe da nessun percorso." throw $_ } } } if (-not (Test-Path $AgentExePath)) { throw "File non scaricato correttamente (Path non trovato)." } Write-Log "Download completato con successo." } } catch { # #region debug-point C:agent-download-fail Send-DebugEvent -HypothesisId "C" -Message "agent-download-failed" -Data @{ error = $_.Exception.Message installDirExists = (Test-Path $InstallDir) agentExeExists = (Test-Path $AgentExePath) } # #endregion Write-Log "ERRORE CRITICO Download Agent: $_" exit 1 } # 2.1 Download Script Inventario (inventory.ps1) $InventoryScriptPath = "$InstallDir\inventory.ps1" if ($LocalInventoryPath -and (Test-Path $LocalInventoryPath)) { Write-Log "Copia inventory.ps1 da locale..." Copy-Item -Path $LocalInventoryPath -Destination $InventoryScriptPath -Force } else { Write-Log "Download inventory.ps1..." if (-not (Try-DownloadFile -Name "inventory.ps1" -OutFile $InventoryScriptPath)) { throw "Impossibile scaricare inventory.ps1" } } try { [void](Ensure-LatestFromServer -Name "inventory.ps1" -DestPath $InventoryScriptPath) } catch {} # 2.2 Download Script Monitoraggio (monitor.ps1) $MonitorScriptPath = "$InstallDir\monitor.ps1" if ($LocalMonitorPath -and (Test-Path $LocalMonitorPath)) { Write-Log "Copia monitor.ps1 da locale..." Copy-Item -Path $LocalMonitorPath -Destination $MonitorScriptPath -Force } else { Write-Log "Download monitor.ps1..." try { if (-not (Try-DownloadFile -Name "monitor.ps1" -OutFile $MonitorScriptPath)) { throw "Impossibile scaricare monitor.ps1" } } catch { Write-Log "Warning: monitor.ps1 non trovato sul server. Saltato." } } try { [void](Ensure-LatestFromServer -Name "monitor.ps1" -DestPath $MonitorScriptPath) } catch {} # 2.3 Download Script Patch (scan-patches.ps1) $PatchScriptPath = "$InstallDir\scan-patches.ps1" if ($LocalPatchPath -and (Test-Path $LocalPatchPath)) { Write-Log "Copia scan-patches.ps1 da locale..." Copy-Item -Path $LocalPatchPath -Destination $PatchScriptPath -Force } else { Write-Log "Download scan-patches.ps1..." try { if (-not (Try-DownloadFile -Name "scan-patches.ps1" -OutFile $PatchScriptPath)) { throw "Impossibile scaricare scan-patches.ps1" } } catch { Write-Log "Warning: scan-patches.ps1 non trovato sul server. Saltato." } } try { [void](Ensure-LatestFromServer -Name "scan-patches.ps1" -DestPath $PatchScriptPath) } catch {} # 2.3b Download Uninstaller (uninstall.ps1) $UninstallScriptPath = "$InstallDir\uninstall.ps1" try { if ($LocalUninstallPath -and (Test-Path $LocalUninstallPath)) { Write-Log "Copia uninstall.ps1 da locale..." Copy-Item -Path $LocalUninstallPath -Destination $UninstallScriptPath -Force } else { Write-Log "Download uninstall.ps1..." try { if (-not (Try-DownloadFile -Name "uninstall.ps1" -OutFile $UninstallScriptPath)) { Write-Log "Warning: uninstall.ps1 non trovato sul server. Disinstallazione da App potrebbe non comparire." } } catch { Write-Log "Warning: download uninstall.ps1 fallito: $($_.Exception.Message)" } try { [void](Ensure-LatestFromServer -Name "uninstall.ps1" -DestPath $UninstallScriptPath) } catch {} } } catch { Write-Log "Warning: gestione uninstall.ps1 fallita: $($_.Exception.Message)" } # 2.4 Create Config JSON $ConfigFile = "$InstallDir\config.json" $ConfigData = @{ serverUrl = "$ServerUrl/api" tenant = $Tenant deviceId = $null # Will be auto-generated or preserved agentKey = $null } # Preserve existing DeviceId if available (File or Registry) $ExistingId = $null # Try File if (Test-Path $ConfigFile) { try { $OldConfig = Get-Content $ConfigFile -Raw | ConvertFrom-Json if ($OldConfig.deviceId) { $ExistingId = $OldConfig.deviceId Write-Log "DeviceId recuperato da config.json: $ExistingId" } if ($OldConfig.agentKey) { Write-Log "AgentKey esistente rilevata ma non riutilizzata: verra rigenerata/sincronizzata dal server." } } catch { Write-Log "Warning: config.json esistente ma illeggibile." } } # Try Registry (Backup) if (-not $ExistingId) { try { if (Test-Path "HKLM:\SOFTWARE\TigerWeb\RMM") { $RegId = Get-ItemProperty -Path "HKLM:\SOFTWARE\TigerWeb\RMM" -Name "DeviceId" -ErrorAction SilentlyContinue if ($RegId -and $RegId.DeviceId) { $ExistingId = $RegId.DeviceId Write-Log "DeviceId recuperato dal Registro: $ExistingId" } } } catch {} } if ($ExistingId) { $ConfigData.deviceId = $ExistingId } if (-not $ConfigData.deviceId) { $ConfigData.deviceId = Get-HardwareID } # Non preservare chiavi precedenti: dopo reinstall/scambi tenant il server deve poter riallineare l'agentKey. $ConfigData.agentKey = $null $ConfigJson = $ConfigData | ConvertTo-Json Set-Content -Path $ConfigFile -Value $ConfigJson -Force Write-Log "Configurazione salvata in $ConfigFile" Protect-AgentConfigFile -Path $ConfigFile foreach ($staleFile in @("$InstallDir\patch_scan_state.json", "$InstallDir\patches_debug.json")) { try { if (Test-Path $staleFile) { Remove-Item $staleFile -Force -ErrorAction SilentlyContinue Write-Log "Stato patch precedente rimosso: $staleFile" } } catch { Write-Log "Warning: impossibile rimuovere stato patch precedente $staleFile" } } try { $st = Get-Item $AgentExePath -ErrorAction Stop $h = Get-FileHash -Path $AgentExePath -Algorithm SHA256 -ErrorAction Stop Write-Log ("Agent EXE: size=" + $st.Length + " sha256=" + $h.Hash) } catch { Write-Log "Warning: impossibile calcolare hash/size agent." } # Backup ID to Registry for future try { if (-not (Test-Path "HKLM:\SOFTWARE\TigerWeb\RMM")) { New-Item -Path "HKLM:\SOFTWARE\TigerWeb" -Name "RMM" -Force | Out-Null } if ($ConfigData.deviceId) { Set-ItemProperty -Path "HKLM:\SOFTWARE\TigerWeb\RMM" -Name "DeviceId" -Value $ConfigData.deviceId -Force } } catch { Write-Log "Warning: Failed to save ID to registry." } # 3. Registrazione Agente (Esecuzione una tantum per registrare ID) Write-Log "Registrazione agente..." & $AgentExePath -Register -Tenant $Tenant | Out-Null $syncedKey = $null try { $syncedKey = Ensure-AgentKeyFromServer -DeviceId $ConfigData.deviceId -TenantName $Tenant -BaseUrl $ServerUrl -ConfigPath $ConfigFile if ($syncedKey) { Write-Log "AgentKey sincronizzata dal server." } else { Write-Log "Warning: AgentKey non disponibile subito. Patch/remoto potrebbero richiedere un giro monitor/telemetry." } } catch { Write-Log "Warning: sync AgentKey fallita." } $Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable $TaskName = "TigerWebRMMAgent" try { Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue | Out-Null } catch {} try { schtasks /Delete /TN $TaskName /F 2>$null | Out-Null } catch {} Write-Log "Task pianificato Agent rimosso (avvio in sessione utente via RUN)." try { $runKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" if (-not (Test-Path $runKey)) { New-Item -Path $runKey -Force | Out-Null } $psExe = "$env:WINDIR\System32\WindowsPowerShell\v1.0\powershell.exe" if (-not (Test-Path $psExe)) { $psExe = "powershell.exe" } $runValue = "`"$psExe`" -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -Command `"Start-Process -FilePath '`"$AgentExePath`"' -ArgumentList '-Service' -WindowStyle Hidden`"" New-ItemProperty -Path $runKey -Name "TigerWebRMMAgent" -Value $runValue -PropertyType String -Force | Out-Null Write-Log "Registrata esecuzione automatica (RUN) per avvio in sessione utente." } catch { Write-Log "Warning: impossibile registrare RUN key per avvio automatico." } # #region debug-point B:run-keys-state Send-DebugEvent -HypothesisId "B" -Message "run-keys-state" -Data @{ legacyRunKey = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "TigerWebAgent" -ErrorAction SilentlyContinue).TigerWebAgent currentRunKey = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "TigerWebRMMAgent" -ErrorAction SilentlyContinue).TigerWebRMMAgent runningAgentProcessCount = @((Get-Process -Name "TigerWebAgent" -ErrorAction SilentlyContinue)).Count } # #endregion $startedNow = $false try { $startedNow = Start-AgentUserSession -ExePath $AgentExePath if ($startedNow) { Write-Log "Avvio agent in sessione corrente." } else { Write-Log "Warning: avvio agent fallito." } } catch { Write-Log "Warning: avvio agent fallito: $($_.Exception.Message)" } # 5. Task Inventario (Ogni 4 ore) $InvTaskName = "TigerWebRMMInventory" $InvAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -File `"$InventoryScriptPath`"" $InvTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Hours 4) Register-ScheduledTask -TaskName $InvTaskName -Action $InvAction -Trigger $InvTrigger -Settings $Settings -User "SYSTEM" -Force | Out-Null # 6. Task Monitoraggio (Ogni 5 min) if (Test-Path $MonitorScriptPath) { $MonTaskName = "TigerWebRMMMonitor" $MonAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -File `"$MonitorScriptPath`"" $MonTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5) Register-ScheduledTask -TaskName $MonTaskName -Action $MonAction -Trigger $MonTrigger -Settings $Settings -User "SYSTEM" -Force | Out-Null Write-Log "Task Monitoraggio creato." } # 7. Task Patch Scan (Immediato + ripetizione ogni 6 ore, piu' trigger giornaliero di backup) if (Test-Path $PatchScriptPath) { $PatchTaskName = "TigerWebRMMPatchScan" $PatchAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -File `"$PatchScriptPath`"" $PatchTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Hours 6) Register-ScheduledTask -TaskName $PatchTaskName -Action $PatchAction -Trigger $PatchTrigger -Settings $Settings -User "SYSTEM" -Force | Out-Null Write-Log "Task Patch Scan creato (avvio immediato + ogni 6h)." # Fallback start: schtasks /run se Start-ScheduledTask non dovesse girare subito try { Start-ScheduledTask -TaskName $PatchTaskName -ErrorAction Stop Write-Log "Task Patch Scan avviato (Start-ScheduledTask)." } catch { try { schtasks /Run /TN $PatchTaskName 2>$null | Out-Null Write-Log "Task Patch Scan avviato (schtasks fallback)." } catch { Write-Log "Warning: avvio task patch fallito (entrambi i metodi)." } } } # 8. Esecuzione Immediata Task per Popolamento Dati Write-Log "Avvio immediato dei task per il primo popolamento dati..." try { Start-ScheduledTask -TaskName "TigerWebRMMInventory" -ErrorAction Stop } catch { try { schtasks /Run /TN "TigerWebRMMInventory" 2>$null | Out-Null } catch {} } try { Start-ScheduledTask -TaskName "TigerWebRMMMonitor" -ErrorAction Stop } catch { try { schtasks /Run /TN "TigerWebRMMMonitor" 2>$null | Out-Null } catch {} } # Patch scan: avvio robustificato anche se il task risultasse non ancora ready $PatchTaskName = "TigerWebRMMPatchScan" try { if (Get-ScheduledTask -TaskName $PatchTaskName -ErrorAction SilentlyContinue) { try { Start-ScheduledTask -TaskName $PatchTaskName -ErrorAction Stop } catch { try { schtasks /Run /TN $PatchTaskName 2>$null | Out-Null } catch {} } } } catch { try { schtasks /Run /TN $PatchTaskName 2>$null | Out-Null } catch {} } if (Test-Path $PatchScriptPath) { try { Write-Log "Invio patch iniziale (scan-patches.ps1)..." $args = @( "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "`"$PatchScriptPath`"", "-ServerUrl", "`"$ServerUrl/api`"", "-Tenant", "`"$Tenant`"", "-DeviceId", "`"$($ConfigData.deviceId)`"" ) -join " " $p = Start-Process -FilePath "powershell.exe" -ArgumentList $args -Wait -PassThru -WindowStyle Hidden Write-Log "Scan patch completato. ExitCode=$($p.ExitCode)" if ($p.ExitCode -ne 0) { Write-Log "Warning: invio patch fallito (ExitCode=$($p.ExitCode))." } } catch { Write-Log "Warning: scan patch iniziale fallito: $($_.Exception.Message)" } } Start-Sleep -Seconds 2 $agentProcCount = @((Get-Process -Name "TigerWebAgent" -ErrorAction SilentlyContinue)).Count if ($agentProcCount -lt 1) { $startedRetry = Start-AgentUserSession -ExePath $AgentExePath Write-Log ("Verifica avvio agent remoto: processCount=" + $agentProcCount + " retryStarted=" + $startedRetry) } else { Write-Log ("Verifica avvio agent remoto: processCount=" + $agentProcCount) } # 9. Register Uninstall entry (App & Features) try { if (-not (Test-Path $UninstallScriptPath)) { throw "uninstall.ps1 mancante" } $ver = "7.0" $unKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TigerWebRMM" $unKeyWow = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\TigerWebRMM" $ps = "$env:WINDIR\System32\WindowsPowerShell\v1.0\powershell.exe" if (-not (Test-Path $ps)) { $ps = "powershell.exe" } $unUrl = "$ServerUrl/rmm-backend/uninstall.ps1" $unUrlSafe = [string]$unUrl $unUrlSafe = $unUrlSafe.Replace("'", "''") $cmdCore = "`$u='$unUrlSafe';`$t=Join-Path `$env:TEMP ('tw_uninstall_'+[Guid]::NewGuid().ToString('n')+'.ps1');Invoke-WebRequest -UseBasicParsing -Uri `$u -OutFile `$t; & `$t" $cmdCoreQuiet = $cmdCore + " -Quiet" $unCmd = "`"$ps`" -NoProfile -ExecutionPolicy Bypass -Command `"$cmdCore`"" $qUnCmd = "`"$ps`" -NoProfile -ExecutionPolicy Bypass -Command `"$cmdCoreQuiet`"" if (-not (Test-Path $unKey)) { New-Item -Path $unKey -Force | Out-Null } Set-ItemProperty -Path $unKey -Name "DisplayName" -Value "TigerWeb RMM Agent" -Force Set-ItemProperty -Path $unKey -Name "DisplayVersion" -Value $ver -Force Set-ItemProperty -Path $unKey -Name "Publisher" -Value "TigerWeb" -Force Set-ItemProperty -Path $unKey -Name "InstallLocation" -Value $InstallDir -Force Set-ItemProperty -Path $unKey -Name "DisplayIcon" -Value $AgentExePath -Force Set-ItemProperty -Path $unKey -Name "UninstallString" -Value $unCmd -Force Set-ItemProperty -Path $unKey -Name "QuietUninstallString" -Value $qUnCmd -Force Set-ItemProperty -Path $unKey -Name "NoModify" -Value 1 -Type DWord -Force Set-ItemProperty -Path $unKey -Name "NoRepair" -Value 1 -Type DWord -Force Set-ItemProperty -Path $unKey -Name "WindowsInstaller" -Value 0 -Type DWord -Force try { Set-ItemProperty -Path $unKey -Name "InstallDate" -Value (Get-Date -Format "yyyyMMdd") -Force } catch {} try { if (-not (Test-Path $unKeyWow)) { New-Item -Path $unKeyWow -Force | Out-Null } Set-ItemProperty -Path $unKeyWow -Name "DisplayName" -Value "TigerWeb RMM Agent" -Force Set-ItemProperty -Path $unKeyWow -Name "DisplayVersion" -Value $ver -Force Set-ItemProperty -Path $unKeyWow -Name "Publisher" -Value "TigerWeb" -Force Set-ItemProperty -Path $unKeyWow -Name "InstallLocation" -Value $InstallDir -Force Set-ItemProperty -Path $unKeyWow -Name "DisplayIcon" -Value $AgentExePath -Force Set-ItemProperty -Path $unKeyWow -Name "UninstallString" -Value $unCmd -Force Set-ItemProperty -Path $unKeyWow -Name "QuietUninstallString" -Value $qUnCmd -Force Set-ItemProperty -Path $unKeyWow -Name "NoModify" -Value 1 -Type DWord -Force Set-ItemProperty -Path $unKeyWow -Name "NoRepair" -Value 1 -Type DWord -Force Set-ItemProperty -Path $unKeyWow -Name "WindowsInstaller" -Value 0 -Type DWord -Force try { Set-ItemProperty -Path $unKeyWow -Name "InstallDate" -Value (Get-Date -Format "yyyyMMdd") -Force } catch {} } catch {} } catch { Write-Log "Warning: registrazione disinstallazione fallita: $($_.Exception.Message)" } # #region debug-point D:install-end Send-DebugEvent -HypothesisId "D" -Message "install-end" -Data @{ installDirExists = (Test-Path $InstallDir) uninstallScriptExists = (Test-Path $UninstallScriptPath) uninstallKeyExists = (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TigerWebRMM") uninstallKeyWowExists = (Test-Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\TigerWebRMM") currentRunKeyExists = ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "TigerWebRMMAgent" -ErrorAction SilentlyContinue) -ne $null) legacyRunKeyExists = ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "TigerWebAgent" -ErrorAction SilentlyContinue) -ne $null) } # #endregion Protect-AgentDirectory -Path $InstallDir Protect-AgentConfigFile -Path $ConfigFile # 10. Report finale diagnostico installazione (singolo file di verifica) try { $ReportPath = Join-Path $InstallDir "install-report.txt" $Rpt = New-Object System.Collections.Generic.List[string] $Rpt.Add("=== TIGERWEB RMM AGENT INSTALL REPORT ===") $Rpt.Add("Generated: " + (Get-Date -Format "yyyy-MM-dd HH:mm:ss")) $Rpt.Add("InstallDir : $InstallDir") $Rpt.Add("Tenant : $Tenant") $Rpt.Add("ServerUrl : $ServerUrl") $Rpt.Add("") $Rpt.Add("--- [1] EXE / FILE NELLA CARTELLA INSTALL ---") try { Get-ChildItem $InstallDir -File -ErrorAction SilentlyContinue | ForEach-Object { $Rpt.Add((" - {0,-30} {1,10:N0} byte {2}" -f $_.Name, $_.Length, $_.LastWriteTime.ToString("yyyy-MM-dd HH:mm"))) } } catch {} $Rpt.Add("") $Rpt.Add("--- [2] STATO CONFIG.JSON ---") if (Test-Path $ConfigFile) { try { $CC = Get-Content $ConfigFile -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop $Rpt.Add(" Config file : OK") $Rpt.Add(" deviceId : " + [string]$CC.deviceId) $Rpt.Add(" tenant : " + [string]$CC.tenant) $Rpt.Add(" serverUrl : " + [string]$CC.serverUrl) $agentKeyStr = [string]$CC.agentKey if ([string]::IsNullOrWhiteSpace($agentKeyStr)) { $Rpt.Add(" agentKey : ASSENTE (problema!)") } else { $Rpt.Add(" agentKey : PRESENTE (OK, len=$($agentKeyStr.Length))") } } catch { $Rpt.Add(" Config file letto ma Parse FAIL: $_") } } else { $Rpt.Add(" CONFIG.JSON MANCANTE COMPLETAMENTE (GRAVE!)") } $Rpt.Add("") $Rpt.Add("--- [3] TASK PIANIFICATI (SYSTEM) ---") foreach ($tn in @("TigerWebRMMInventory", "TigerWebRMMMonitor", "TigerWebRMMPatchScan", "TigerWebRMMAgent")) { try { $t = Get-ScheduledTask -TaskName $tn -ErrorAction Stop $s = ($t.State).ToString() $Rpt.Add(" $tn => ESISTE State=$s") } catch { try { $q = schtasks /Query /TN $tn /FO LIST 2>$null if ($LASTEXITCODE -eq 0) { $Rpt.Add(" $tn => ESISTE (schtasks OK)") } else { $Rpt.Add(" $tn => MANCANTE (problema!)") } } catch { $Rpt.Add(" $tn => MANCANTE (problema!)") } } } $Rpt.Add("") $Rpt.Add("--- [4] AVVIO COMPLETO DI SCAN-PATCHES (diagnostica finale, blocca max 3 min) ---") $PatchExitCode = -9999 try { if (Test-Path $PatchScriptPath) { $Rpt.Add(" Esecuzione: powershell.exe -File `"$PatchScriptPath`"...") $diagArgs = @( "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "`"$PatchScriptPath`"" ) -join " " $proc = Start-Process -FilePath "powershell.exe" -ArgumentList $diagArgs -Wait -PassThru -WindowStyle Hidden -RedirectStandardOutput (Join-Path $InstallDir "scan-final.stdout.log") -RedirectStandardError (Join-Path $InstallDir "scan-final.stderr.log") $PatchExitCode = [int]$proc.ExitCode $okLabel = if ($PatchExitCode -eq 0) { "SUCCESSO" } else { "FALLITO" } $Rpt.Add(" ExitCode scan-patches: $PatchExitCode ($okLabel)") } else { $Rpt.Add(" scan-patches.ps1 NON TROVATO in $PatchScriptPath (GRAVE!)") } } catch { $Rpt.Add(" Esecuzione scan-patches fallita durante Start-Process: $_") } $Rpt.Add("") $Rpt.Add("--- [5] FILE STATO POST-SCAN ---") foreach ($fn in @("patches_debug.json", "patch_scan_state.json", "rmm-agent.log", "scan-final.stdout.log", "scan-final.stderr.log")) { $fp = Join-Path $InstallDir $fn if (Test-Path $fp) { try { $content = Get-Content $fp -Raw -ErrorAction Stop if ($content.Length -gt 4000) { $content = $content.Substring(0,4000) + "`r`n...[TRUNCATED - total $($content.Length) chars]" } $Rpt.Add(" ==> $fn :") $Rpt.Add($content) } catch { $Rpt.Add(" ==> $fn : (impossibile leggere: $_)") } } else { $Rpt.Add(" ==> $fn : NON PRESENTE") } $Rpt.Add("") } $Rpt.Add("=== FINE REPORT ===") Set-Content -Path $ReportPath -Value ($Rpt -join [Environment]::NewLine) -Encoding UTF8 -Force Write-Log ("Report installazione salvato: " + $ReportPath + " (PatchScan ExitCode=" + $PatchExitCode + ")") try { attrib +H $ReportPath | Out-Null } catch {} } catch { Write-Log "Warning: impossibile scrivere report installazione: $($_.Exception.Message)" } Write-Log "Installazione Completata."