Files
2025-09-18 17:20:32 +02:00

345 lines
12 KiB
PowerShell

<#
.SYNOPSIS
Install and configure Rustdesk Client
.DESCRIPTION
Deployment script to deploy the latest Rustdesk (https://rustdesk.com/) client on a windows computer. Use described parameters to configure the client.
.PARAMETER rdServer
The IP address or FQDN of the RustDesk ID Server and Relay Server
.PARAMETER rdKey
Specifies the key of ID/Relay Server
.PARAMETER pwLength
Specifies the length of the password to connect to the RustDesk client
.PARAMETER enableAudio
EnableAudio in RustDesk client. (https://rustdesk.com/docs/en/self-host/client-configuration/advanced-settings/#enable-audio)
.PARAMETER enablePrinter
EnablePrinter in RustDesk client (https://rustdesk.com/docs/en/self-host/client-configuration/advanced-settings/#enable-remote-printer)
.EXAMPLE
PS> .\RustdeskInstall.ps1 -rdServer "somehost.example.tld" -rdKey "KeyFromServer="
Install RustDesk Client by defining a different ID/Relay server and corresponding key
.EXAMPLE
PS> .\RustdeskInstall.ps1 -rdServer "somehost.example.tld" -rdKey "KeyFromServer=" -pwLength 24
Optionally define length for client password
.EXAMPLE
PS> .\RustdeskInstall.ps1 -rdServer "somehost.example.tld" -rdKey "KeyFromServer=" -enableAudio 0
Optionally disable audio
.EXAMPLE
PS> .\RustdeskInstall.ps1 -toNextcloudPassword 1 -ncBaseUrl "https://some.nextcloud.url/index.php/apps/passwords" -ncUsername "user.name" -ncToken "12345-abcde-67890" -ncFolder "NextcloudPasswordFolderUUID"
#>
param(
[string]$rdServer = "rs-ny.rustdesk.com",
[string]$rdKey = $null,
[int]$pwLength = 8,
[bool]$enableAudio = $True,
[bool]$enablePrinter = $True,
[bool]$toNextcloudPassword = $False,
[string]$ncBaseUrl,
[string]$ncUsername,
[string]$ncToken,
[string]$ncFolder
)
if ($rdServer -ne "rs-ny.rustdesk.com") {
if (!($rdKey)) {
Write-Host("Required parameter '-rdKey' was not set! Exiting!")
exit 1
}
}
if ($toNextcloudPassword) {
if (!($ncBaseUrl)) {
Write-Host("Required parameter '-ncBaseUrl' was not set! Exiting!")
exit 1
}
if (!($ncUsername)) {
Write-Host("Required parameter '-ncUsername' was not set! Exiting!")
exit 1
}
if (!($ncToken)) {
Write-Host("Required parameter '-ncToken' was not set! Exiting!")
exit 1
}
if (!($ncFolder)) {
Write-Host("Require parameter '-ncFolder' was not set! Exiting!")
exit 1
}
}
$ErrorActionPreference = 'silentlycontinue'
#Run as administrator and stays in the current directory
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
Start-Process PowerShell -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"";
exit;
}
}
$rustdeskURL = 'https://github.com/rustdesk/rustdesk/releases/latest'
$rustdeskReg = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\RustDesk\'
function PreqRustdeskUpstreamVersion([string]$rustdeskURL) {
#Get latest upstream version number
$upstream_rustdesk_version = [System.Net.WebRequest]::Create($rustdeskURL).GetResponse().ResponseUri.OriginalString.split('/')[-1].Trim('v')
return $upstream_rustdesk_version
}
function PreqRustdeskInstalledVersion([string]$rustdeskReg) {
#Check if Rustdesk is already installed and get version number
if (Test-Path $rustdeskReg) {
$installed_rustdesk_version = ((Get-ItemProperty $rustdeskReg).Version)
return $installed_rustdesk_version
} else {
return 0
}
}
function Prerequisites([string]$VersionInstalled, [string]$VersionUpstream) {
if (!(Test-Path $env:Temp)) {
New-Item -ItemType Directory -Force -Path $env:Temp | Out-Null
}
if (!([System.Version]$VersionUpstream -gt [System.Version]$VersionInstalled)) {
Write-Output("Rustdesk version $VersionUpstream is already installed!")
exit
}
}
function DownloadRustdesk([string]$version) {
Write-Output("Download Rustdesk client https://github.com/rustdesk/rustdesk/releases/download/$version/rustdesk-$version-x86_64.exe")
Invoke-WebRequest "https://github.com/rustdesk/rustdesk/releases/download/$version/rustdesk-$version-x86_64.exe" -Outfile "$env:Temp\rustdesk.exe"
}
function InstallRustdesk {
Write-Output("Silently install Rustdesk client")
cmd /c ""$env:Temp\rustdesk.exe --silent-install""
# Workaround: --silent-install does not quit process
Start-Sleep 30
Stop-Process -Name Rustdesk -Force | Out-Null
}
function StartRustdesk([string]$serviceName) {
Write-Output("Start Rustdesk service")
Start-Service $serviceName
}
function StopRustdesk([string]$serviceName) {
Write-Output("Stop Rustdesk service")
$serviceState = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($serviceState -eq $null) {
Start-Sleep -seconds 20
}
while ($serviceState.Status -ne 'Running') {
Start-Service $serviceName
Start-Sleep -seconds 5
$serviceState.Refresh()
}
Stop-Service $serviceName
Stop-Process -Name $serviceName -Force | Out-Null
}
function ConfigureRustdesk([string]$rdServer, [string]$rdKey, [bool]$enableAudio, [bool]$enablePrinter, [string]$serviceName) {
Write-Output("Configure Rustdesk client and service")
$ipAddress = (Get-NetIPConfiguration | Where-Object {$_.IPv4DefaultGateway -ne $null -and $_.NetAdapter.status -ne "Disconnected"}).IPv4Address.IPAddress
# RustDesk2.toml
$rd2Toml = @"
rendezvous_server = '$rdServer'
nat_type = 2
serial = 0
[options]
local-ip-addr = '$ipAddress'
"@
if ($rdServer -ne "rs-ny.rustdesk.com") {
$rd2Toml += "`ncustom-rendezvous-server = '$rdServer'"
$rd2Toml += "`nrelay-server = '$rdServer'"
$rd2Toml += "`napi-server = 'https://$rdServer'"
if ($rdKey) {
$rd2Toml += "`nkey = '$rdKey'"
}
}
if (!($enableAudio)) {
$rd2Toml += "`nenable-audio = 'N'"
}
if (!($enablePrinter)) {
$rd2Toml += "`nenable-remote-printer = 'N'"
}
#Workaround: Copy permanent password settings from:
# $env:Appdata\RustDesk\config\RustDesk.toml
# to:
# $env:WinDir\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk\RustDesk.toml
#
# but not if running as SYSTEM
#
if ("$env:AppData" -ne "$env:WinDir\ServiceProfiles\LocalService\AppData\Roaming") {
if ("$env:AppData" -ne "$env:WinDir\system32\config\systemprofile\AppData\Roaming" ) {
if (!(Test-Path $env:AppData\RustDesk\config\RustDesk2.toml)) {
New-Item $env:AppData\RustDesk\config\RustDesk2.toml
}
Set-Content $env:AppData\RustDesk\config\RustDesk2.toml $rd2Toml | Out-Null
}
}
if (!(Test-Path $env:WinDir\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml)) {
New-Item $env:WinDir\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml
}
Set-Content $env:WinDir\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml $rd2Toml | Out-Null
}
function UninstallRemotePrinter([bool]$enablePrinter) {
if (!($enablePrinter)) {
if ($env:ProgramW6432) {
cmd /c ""$env:ProgramW6432\Rustdesk\rustdesk.exe --uninstall-remote-printer"" | Out-Null
} else {
cmd /c ""$env:ProgramFiles\Rustdesk\rustdesk.exe --uninstall-remote-printer"" | Out-Null
}
}
}
function SetRustdeskPW([int]$pwLength) {
$rustdeskPW = (-join ((65..90) + (97..122) | Get-Random -Count $pwLength | % {[char]$_}))
if ($env:ProgramW6432) {
cmd /c ""$env:ProgramW6432\Rustdesk\rustdesk.exe --password $rustdeskPW"" | Out-Null
} else {
cmd /c ""$env:ProgramFiles\Rustdesk\rustdesk.exe --password $rustdeskPW"" | Out-Null
}
#Workaround: Copy permanent password settings from:
# $env:Appdata\RustDesk\config\RustDesk.toml
# to:
# $env:WinDir\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk\RustDesk.toml
#
# but not if running as SYSTEM
#
if ("$env:AppData" -ne "$env:WinDir\ServiceProfiles\LocalService\AppData\Roaming") {
if ("$env:AppData" -ne "$env:WinDir\system32\config\systemprofile\AppData\Roaming" ) {
Copy-Item -Path "$env:Appdata\RustDesk\config\RustDesk.toml" -Destination "$env:WinDir\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk.toml" -Force
}
}
return $rustdeskPW
}
function GetRustdeskID {
if ($env:ProgramW6432) {
$rustdeskID = cmd /c ""$env:ProgramW6432\Rustdesk\rustdesk.exe --get-id""
} else {
$rustdeskID = cmd /c ""$env:ProgramFiles\Rustdesk\rustdesk.exe --get-id""
}
return $rustdeskID
}
function OutputIDAndPW([string]$rustdeskID, [string]$rustdeskPW) {
Write-Output("######################################################")
Write-Output("# #")
Write-Output("# CONNECTION PARAMETERS: #")
Write-Output("# #")
Write-Output("######################################################")
Write-Output("")
Write-Output(" RustDesk-ID: $rustdeskID")
Write-Output(" RustDesk-Password: $rustdeskPW")
Write-Output("")
}
function WriteRustdeskCredsToNextcloudPasswords([string]$ncBaseUrl, [string]$ncUsername, [string]$ncToken, [string]$ncFolder, [string]$rustdeskID, [string]$rustdeskPW) {
#
# API documentation can be found here https://git.mdns.eu/nextcloud/passwords/-/wikis/Developers/Index
#
$computerName = $env:ComputerName.ToUpper()
$pair = "$($ncUsername):$($ncToken)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
$Headers = @{"Authorization" = "$basicAuthValue"}
$openNCPwSession = Invoke-WebRequest -Uri "$ncBaseUrl/api/1.0/session/open" -Headers $Headers -UseBasicParsing -Method Post -SessionVariable Cookie
$listNCPasswords = Invoke-WebRequest -Uri "$ncBaseUrl/api/1.0/password/list" -Headers $Headers -UseBasicParsing -WebSession $Cookie
$pwEntries = ConvertFrom-Json($listNCPasswords.Content)
# Find record by label
foreach ($pwEntry in $pwEntries) {
if ($pwEntry.folder -eq "$ncFolder") {
if ($pwEntry.label -eq $computerName) {
$pwID = $pwEntry.id
}
}
}
$rustdeskPWStream = [IO.MemoryStream]::new([byte[]][char[]]$rustdeskPW)
$rustdeskPWHash = Get-FileHash -InputStream $rustdeskPWStream -Algorithm SHA1
$rustdeskHash = $rustdeskPWHash.Hash.ToLower()
if ($pwID -eq $null) {
#create entry
$JSON = @{
"password" = "$rustdeskPW";
"label" = "$computerName";
"username" = "$rustdeskID";
"folder" = "$ncFolder";
"hash" = "$rustdeskHash";
"url" = "rustdesk://connection/new/$rustdeskID";
} | ConvertTo-Json
$createNCPassword = Invoke-WebRequest -Uri "$ncBaseUrl/api/1.0/password/create" -Headers $Headers -UseBasicParsing -WebSession $Cookie -Body $JSON -ContentType application/json -Method Post
} else {
#update entry
$JSON = @{
"id" = "$pwID";
"password" = "$rustdeskPW";
"label" = "$computerName";
"username" = "$rustdeskID";
"folder" = "$ncFolder";
"hash" = "$rustdeskHash";
"url" = "rustdesk://connection/new/$rustdeskID";
} | ConvertTo-Json
$updateNCPassword = Invoke-WebRequest -Uri "$ncBaseUrl/api/1.0/password/update" -Headers $Headers -UseBasicParsing -WebSession $Cookie -Body $JSON -ContentType application/json -Method Patch
}
}
# Run all functions
$rdUpstreamVersion = PreqRustdeskUpstreamVersion -rustdeskURL $rustdeskURL
$rdInstalledVersion = PreqRustdeskInstalledVersion -rustdeskReg $rustdeskReg
$serviceName = 'Rustdesk'
Prerequisites -VersionInstalled $rdInstalledVersion -VersionUpstream $rdUpstreamVersion
DownloadRustdesk -version $rdUpstreamVersion
InstallRustdesk
StopRustdesk -serviceName $serviceName
ConfigureRustdesk -rdServer $rdServer -rdKey $rdKey -enableAudio $enableAudio -enablePrinter $enablePrinter -serviceName $serviceName
$rustdeskPW = SetRustdeskPW -pwLength $pwLength
$rustdeskID = GetRustdeskID
OutputIDAndPW -rustdeskID $rustdeskID -rustdeskPW $rustdeskPW
UninstallRemotePrinter -enablePrinter $enablePrinter
StartRustdesk -serviceName $serviceName
if ($toNextcloudPassword) {
Write-Output("Send Rustdesk credentials to Nextcloud Passwords app")
WriteRustdeskCredsToNextcloudPasswords -ncBaseUrl "$ncBaseUrl" -ncUsername "$ncUsername" -ncToken "$ncToken" -ncFolder "$ncFolder" -rustdeskID "$rustdeskID" -rustdeskPW "$rustdeskPW"
}