Files
SITS_RMM_SCRIPTS/1-estudio-Install-TacticalRMM-and-RustDesk.ps1
T
uhlwoogiandClaude Sonnet 4.6 20fd7f7f6f Try Start-BitsTransfer for TacticalRMM download
WebClient and curl both fail with TLS errors (likely SSL inspection proxy
or missing root certs). BITS uses WinHTTP with the system cert store
which may handle this differently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 16:40:05 +00:00

200 lines
8.6 KiB
PowerShell

# author: https://github.com/bradhawkins85
# modified: added RustDesk (ScoutIT-RemoteSupport) silent install with service verification and ID output
$ErrorActionPreference = 'SilentlyContinue'
$innosetup = 'tacticalagent-v2.10.0-windows-amd64.exe'
$api = '"https://apirmm.scoutitsystems.com"'
$clientid = '1'
$siteid = '1'
$agenttype = '"workstation"'
$power = 0
$rdp = 0
$ping = 0
$auth = '"f77cac29dc421abacbfc1882c2700b655f7a4bcf6500aaec040208dc2a5bdbff"'
$downloadlink = 'https://agents.tacticalrmm.com/api/v2/agents/?version=2.10.0&arch=amd64&token=f3c1cbee-7d48-4f2a-8f0a-7cc33bb42802&plat=windows&api=apirmm.scoutitsystems.com'
$apilink = $downloadlink.split('/')
# ── RustDesk / ScoutIT Remote Support config ──────────────────────────────────
$rustdeskMsi = 'estudio-scoutit-remotesupport.msi'
$rustdeskDl = 'https://scoutit.help/estudio-rust'
$rustdeskInstallDir = 'C:\Program Files\ScoutIT-RemoteSupport'
$rustdeskService = 'ScoutIT-RemoteSupport'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# ── Helper: Install RustDesk ──────────────────────────────────────────────────
function Install-RustDesk {
$msiPath = "$env:SystemRoot\Temp\$rustdeskMsi"
# Download
Write-Host "Downloading ScoutIT Remote Support client..."
Try {
(New-Object System.Net.WebClient).DownloadFile($rustdeskDl, $msiPath)
}
Catch {
Write-Warning "RustDesk download failed: $($_.Exception.Message)"
return
}
# Verify the downloaded file is a valid MSI (OLE compound doc magic bytes: D0 CF 11 E0)
$fileSize = (Get-Item $msiPath).Length
Write-Host "Downloaded file size: $fileSize bytes"
$magic = [System.IO.File]::ReadAllBytes($msiPath) | Select-Object -First 4
$magicHex = ($magic | ForEach-Object { $_.ToString('X2') }) -join ' '
Write-Host "File header bytes: $magicHex"
if ($magicHex -ne 'D0 CF 11 E0') {
Write-Warning "Downloaded file is not a valid MSI (expected D0 CF 11 E0, got $magicHex). Check the download URL."
Remove-Item $msiPath -Force -ErrorAction SilentlyContinue
return
}
Write-Host "File verified as valid MSI."
# Silent MSI install
$msiLog = "$env:SystemRoot\Temp\estudio-scoutit-remotesupport-install.log"
Write-Host "Installing ScoutIT Remote Support client silently (log: $msiLog)..."
Try {
Start-Process -FilePath "msiexec.exe" `
-ArgumentList "/i `"$msiPath`" /qn /norestart /l*v `"$msiLog`" CREATEDESKTOPSHORTCUTS=`"N`" INSTALLPRINTER=`"N`"" `
-Wait
Start-Sleep -Seconds 10
}
Catch {
Write-Warning "RustDesk MSI install failed: $($_.Exception.Message)"
return
}
Finally {
if (Test-Path $msiPath) { Remove-Item -Path $msiPath -Force }
}
# Locate the installed exe
# First try: registry InstallLocation from the MSI uninstall entry
$uninstallKeys = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$rustdeskExe = $null
foreach ($key in $uninstallKeys) {
$entry = Get-ItemProperty $key -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -match 'scoutit|remotesupport|rustdesk' -or
$_.Publisher -match 'scoutit|rustdesk' } |
Select-Object -First 1
if ($entry -and $entry.InstallLocation) {
$candidate = Get-ChildItem $entry.InstallLocation -Filter '*.exe' -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($candidate) {
$rustdeskExe = $candidate.FullName
Write-Host "Found exe via registry: $rustdeskExe"
break
}
}
}
# Second try: search only within directories whose name matches expected install folder
if (-not $rustdeskExe) {
Write-Host "Searching for RustDesk exe in matching install directories..."
$found = Get-ChildItem 'C:\Program Files', 'C:\Program Files (x86)' -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match 'rustdesk|scoutit|remotesupport' } |
ForEach-Object { Get-ChildItem $_.FullName -Filter '*.exe' -ErrorAction SilentlyContinue } |
Select-Object -First 1
if ($found) {
$rustdeskExe = $found.FullName
Write-Host "Found exe at: $rustdeskExe"
}
}
# Last resort: list what the MSI actually installed so we can identify the exe
if (-not $rustdeskExe) {
Write-Warning "Could not locate RustDesk exe. Listing Program Files for diagnosis:"
Get-ChildItem 'C:\Program Files', 'C:\Program Files (x86)' -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddMinutes(-5) } |
ForEach-Object { Write-Host " $($_.FullName)" }
Write-Warning "Could not locate RustDesk exe. Skipping service and ID steps. Check MSI log: $msiLog"
return
}
$exeDir = Split-Path $rustdeskExe
# Install service if not present
$svc = Get-Service -Name $rustdeskService -ErrorAction SilentlyContinue
if ($null -eq $svc) {
Write-Host "Registering ScoutIT Remote Support service..."
Start-Process -FilePath $rustdeskExe -ArgumentList '--install-service' -Wait
Start-Sleep -Seconds 20
$svc = Get-Service -Name $rustdeskService -ErrorAction SilentlyContinue
}
# Ensure service is running
if ($null -ne $svc) {
$attempts = 0
while ($svc.Status -ne 'Running' -and $attempts -lt 5) {
Write-Host "Starting ScoutIT Remote Support service (attempt $($attempts + 1))..."
Start-Service -Name $rustdeskService -ErrorAction SilentlyContinue
Start-Sleep -Seconds 5
$svc.Refresh()
$attempts++
}
if ($svc.Status -eq 'Running') {
Write-Host "ScoutIT Remote Support service is running."
} else {
Write-Warning "ScoutIT Remote Support service did not reach Running state."
}
} else {
Write-Warning "Service '$rustdeskService' not found after install."
}
# Get and output the RustDesk ID (visible in RMM job logs)
Push-Location $exeDir
$rustdeskId = & $rustdeskExe --get-id 2>&1
Pop-Location
Write-Output "................................................."
Write-Output "ScoutIT Remote Support ID: $rustdeskId"
Write-Output "................................................."
}
# ── Tactical RMM ─────────────────────────────────────────────────────────────
$serviceName = 'tacticalrmm'
If (Get-Service $serviceName -ErrorAction SilentlyContinue) {
Write-Host "Tactical RMM Is Already Installed"
} Else {
$OutPath = $env:TMP
$output = $innosetup
$installArgs = @('-m install --api ', "$api", '--client-id', $clientid, '--site-id', $siteid, '--agent-type', "$agenttype", '--auth', "$auth")
if ($power) { $installArgs += "--power" }
if ($rdp) { $installArgs += "--rdp" }
if ($ping) { $installArgs += "--ping" }
Try {
$DefenderStatus = Get-MpComputerStatus | Select-Object AntivirusEnabled
if ($DefenderStatus -match "True") {
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*'
Add-MpPreference -ExclusionPath 'C:\ProgramData\TacticalRMM\*'
}
}
Catch { # pass
}
Try {
Write-Host "Downloading TacticalRMM agent..."
Start-BitsTransfer -Source $downloadlink -Destination "$OutPath\$output" -ErrorAction Stop
Write-Host "Download complete."
Start-Process -FilePath "$OutPath\$output" -ArgumentList '/VERYSILENT /SUPPRESSMSGBOXES' -Wait
Write-Host "Extracting..."
Start-Sleep -s 5
Start-Process -FilePath "C:\Program Files\TacticalAgent\tacticalrmm.exe" -ArgumentList $installArgs -Wait
# ── Install RustDesk after Tactical RMM ──
Install-RustDesk
exit 0
}
Catch {
Write-Warning "TacticalRMM install failed: $($_.Exception.Message)"
exit 1
}
Finally {
if (Test-Path "$OutPath\$output") { Remove-Item -Path "$OutPath\$output" -Force }
}
}