Write-Error is suppressed by SilentlyContinue at the top of the script, so failures were silent. Switch to Write-Warning which is always visible. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
200 lines
8.6 KiB
PowerShell
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..."
|
|
(New-Object System.Net.WebClient).DownloadFile($downloadlink, "$OutPath\$output")
|
|
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 }
|
|
}
|
|
}
|