|
| 1 | +<# |
| 2 | +.SYNOPSIS |
| 3 | + Downloads and installs the latest or specified version of the Tactical RMM agent, with support for signed and unsigned downloads. |
| 4 | +
|
| 5 | +.DESCRIPTION |
| 6 | + This script retrieves the latest version of the Tactical RMM agent from GitHub or downloads a specified version based on the input environment variables. |
| 7 | + It supports downloading a signed version using a provided token, or an unsigned version directly from GitHub. |
| 8 | + If the specified version is set to "latest," the script fetches the most recent release information. |
| 9 | + Before downloading, it checks the locally installed version from the software list and skips the download if it matches the desired version. |
| 10 | +
|
| 11 | +.PARAMETER version |
| 12 | + Specifies the version to download. If set to "latest," the script retrieves the latest version available on GitHub. |
| 13 | + This should be specified through the environment variable `version`. |
| 14 | +
|
| 15 | +.PARAMETER signedDownloadToken |
| 16 | + The token used for authenticated signed downloads. This should be set in the environment variable `trmm_sign_download_token`. |
| 17 | + If this token is provided, the script will download the signed version. |
| 18 | +
|
| 19 | +.PARAMETER trmm_api_target |
| 20 | + The API target required for signed downloads. This should be specified in the environment variable `trmm_api_target`. |
| 21 | + This is only necessary if using a signed download. |
| 22 | +
|
| 23 | +.EXEMPLE |
| 24 | + trmm_sign_download_token={{global.trmm_sign_download_token}} |
| 25 | + version=latest |
| 26 | + version=2.7.0 |
| 27 | + trmm_api_target=api.exemple.com |
| 28 | + trmm_api_target={{global.RMM_API_URL}} |
| 29 | + |
| 30 | +.NOTES |
| 31 | + Author: SAN |
| 32 | + Date: 29.10.24 |
| 33 | + #public |
| 34 | +
|
| 35 | +.CHANGELOG |
| 36 | + 29.10.24 SAN Initial script with signed and unsigned download support. |
| 37 | + 21.12.24 SAN updated the script to not require "issigned" |
| 38 | + 22.12.24 SAN default to latest when no version is set |
| 39 | +
|
| 40 | +.TODO |
| 41 | + Add a small (15 seconds) delay to the execution of the exe to ensure trmm is capable of properly capturing the output of the script before the agent kills the service |
| 42 | + |
| 43 | +#> |
| 44 | +# Variables |
| 45 | +$version = $env:version # Specify a version manually, or leave empty to get the latest version from GitHub |
| 46 | +$signedDownloadToken = $env:trmm_sign_download_token # Token used for signed downloads only |
| 47 | +$apiTarget = $env:trmm_api_target # Environment variable for the API target URL |
| 48 | + |
| 49 | +# Define GitHub API URL for the RMMAgent repository |
| 50 | +$repoUrl = "https://api.github.com/repos/amidaware/rmmagent/releases/latest" |
| 51 | + |
| 52 | +# Function to get the currently installed version of the Tactical RMM agent from the software list |
| 53 | +function Get-InstalledVersion { |
| 54 | + $appName = "Tactical RMM Agent" # Adjust if the application's display name differs left this in case whitelabel changes the name of the app |
| 55 | + $installedSoftware = Get-CimInstance -ClassName Win32_Product | Where-Object { $_.Name -like "*$appName*" } |
| 56 | + |
| 57 | + if ($installedSoftware) { |
| 58 | + return $installedSoftware.Version |
| 59 | + } else { |
| 60 | + # Check the uninstall registry key for a more complete list |
| 61 | + $uninstallKeys = @( |
| 62 | + "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", |
| 63 | + "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" |
| 64 | + ) |
| 65 | + |
| 66 | + foreach ($key in $uninstallKeys) { |
| 67 | + $installedSoftware = Get-ItemProperty $key | Where-Object { $_.DisplayName -like "*$appName*" } |
| 68 | + if ($installedSoftware) { |
| 69 | + return $installedSoftware.DisplayVersion |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + return $null |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +try { |
| 78 | + # Set up headers for GitHub API request |
| 79 | + $headers = @{ |
| 80 | + "User-Agent" = "PowerShell Script" |
| 81 | + } |
| 82 | + |
| 83 | + # If version is not set, default to "latest" |
| 84 | + if (-not $version) { |
| 85 | + $version = "latest" |
| 86 | + } |
| 87 | + if ($version -eq "latest") { |
| 88 | + Write-Output "Fetching the latest version information of the TRMM agent from GitHub..." |
| 89 | + $response = Invoke-RestMethod -Uri $repoUrl -Headers $headers -Method Get -ErrorAction Stop |
| 90 | + $version = $response.tag_name.TrimStart('v') # Remove 'v' prefix if exists |
| 91 | + Write-Output "Latest version found: $version" |
| 92 | + } else { |
| 93 | + Write-Output "Using specified version: $version" |
| 94 | + } |
| 95 | + |
| 96 | + # Check if the installed version matches the desired version |
| 97 | + $installedVersion = Get-InstalledVersion |
| 98 | + if ($installedVersion) { |
| 99 | + Write-Output "Installed version of 'Tactical RMM Agent': $installedVersion" |
| 100 | + if ($installedVersion -eq $version) { |
| 101 | + Write-Output "The installed version matches the desired version. No upgrade required." |
| 102 | + exit 0 |
| 103 | + } else { |
| 104 | + Write-Output "The installed version ($installedVersion) does not match the desired version ($version). Proceeding with download." |
| 105 | + } |
| 106 | + } else { |
| 107 | + Write-Output "'Tactical RMM Agent' is not installed on this system. Checking installed software..." |
| 108 | + } |
| 109 | + |
| 110 | + # Define the temp directory for downloading |
| 111 | + $tempDir = [System.IO.Path]::GetTempPath() |
| 112 | + $outputFile = Join-Path -Path $tempDir -ChildPath "tacticalagent-v$version.exe" |
| 113 | + |
| 114 | + # Determine the download URL based on the presence of $signedDownloadToken |
| 115 | + if ($signedDownloadToken) { |
| 116 | + if (-not $apiTarget) { |
| 117 | + Write-Output "Error: Missing API target for signed downloads. Exiting..." |
| 118 | + exit 1 |
| 119 | + } |
| 120 | + # Download the signed agent using the token |
| 121 | + $downloadUrl = "https://agents.tacticalrmm.com/api/v2/agents?version=$version&arch=amd64&token=$signedDownloadToken&plat=windows&api=$apiTarget" |
| 122 | + } else { |
| 123 | + # Download the unsigned agent directly from GitHub releases |
| 124 | + $downloadUrl = "https://github.com/amidaware/rmmagent/releases/download/v$version/tacticalagent-v$version-windows-amd64.exe" |
| 125 | + } |
| 126 | + |
| 127 | + Write-Output "Downloading from: $downloadUrl" |
| 128 | + |
| 129 | + # Download the agent file |
| 130 | + try { |
| 131 | + Invoke-WebRequest -Uri $downloadUrl -OutFile $outputFile -ErrorAction Stop |
| 132 | + Write-Output "Download completed: $outputFile" |
| 133 | + } catch { |
| 134 | + Write-Output "Failed to download the agent. Error: $($_.Exception.Message)" |
| 135 | + exit 1 |
| 136 | + } |
| 137 | + |
| 138 | + # Run the downloaded file in a new context (using cmd) |
| 139 | + $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo |
| 140 | + $processStartInfo.FileName = $outputFile |
| 141 | + $processStartInfo.Arguments = "/VERYSILENT" |
| 142 | + $processStartInfo.UseShellExecute = $true # Allows the executable to run independently |
| 143 | + $processStartInfo.CreateNoWindow = $true # Prevents a new window from being created |
| 144 | + |
| 145 | + Write-Output "Starting installation..." |
| 146 | + |
| 147 | + # Start the process without attempting to cast the result |
| 148 | + try { |
| 149 | + [System.Diagnostics.Process]::Start($processStartInfo) |
| 150 | + Write-Output "Installation started. The process is running in the background." |
| 151 | + } catch { |
| 152 | + Write-Output "Failed to start the installation process. Error: $($_.Exception.Message)" |
| 153 | + exit 1 |
| 154 | + } |
| 155 | +} catch { |
| 156 | + # Handle unexpected errors with output |
| 157 | + Write-Output "An unexpected error occurred: $($_.Exception.Message)" |
| 158 | + exit 1 |
| 159 | +} |
0 commit comments