From 748d18321d0f925d7cab86040b2f33c74d58b377 Mon Sep 17 00:00:00 2001 From: henrygd Date: Sun, 25 May 2025 23:57:24 -0400 Subject: [PATCH] fix: windows paths when regular and admin install users differ (#739) --- supplemental/scripts/install-agent.ps1 | 227 ++++++++++++++++++++++--- 1 file changed, 204 insertions(+), 23 deletions(-) diff --git a/supplemental/scripts/install-agent.ps1 b/supplemental/scripts/install-agent.ps1 index 10312ab..a0b291e 100644 --- a/supplemental/scripts/install-agent.ps1 +++ b/supplemental/scripts/install-agent.ps1 @@ -3,7 +3,8 @@ param ( [Parameter(Mandatory=$true)] [string]$Key, [int]$Port = 45876, - [string]$AgentPath = "" + [string]$AgentPath = "", + [string]$NSSMPath = "" ) # Check if key is provided or empty @@ -32,6 +33,78 @@ function Test-CommandExists { return (Get-Command $Command -ErrorAction SilentlyContinue) } +# Function to find beszel-agent in common installation locations +function Find-BeszelAgent { + # First check if it's in PATH + $agentCmd = Get-Command "beszel-agent" -ErrorAction SilentlyContinue + if ($agentCmd) { + return $agentCmd.Source + } + + # Common installation paths to check + $commonPaths = @( + "$env:USERPROFILE\scoop\apps\beszel-agent\current\beszel-agent.exe", + "$env:ProgramData\scoop\apps\beszel-agent\current\beszel-agent.exe", + "$env:LOCALAPPDATA\Microsoft\WinGet\Packages\henrygd.beszel-agent*\beszel-agent.exe", + "$env:ProgramFiles\WinGet\Packages\henrygd.beszel-agent*\beszel-agent.exe", + "${env:ProgramFiles(x86)}\WinGet\Packages\henrygd.beszel-agent*\beszel-agent.exe", + "$env:ProgramFiles\beszel-agent\beszel-agent.exe", + "$env:ProgramFiles(x86)\beszel-agent\beszel-agent.exe", + "$env:SystemDrive\Users\*\scoop\apps\beszel-agent\current\beszel-agent.exe" + ) + + foreach ($path in $commonPaths) { + # Handle wildcard paths + if ($path.Contains("*")) { + $foundPaths = Get-ChildItem -Path $path -ErrorAction SilentlyContinue + if ($foundPaths) { + return $foundPaths[0].FullName + } + } else { + if (Test-Path $path) { + return $path + } + } + } + + return $null +} + +# Function to find NSSM in common installation locations +function Find-NSSM { + # First check if it's in PATH + $nssmCmd = Get-Command "nssm" -ErrorAction SilentlyContinue + if ($nssmCmd) { + return $nssmCmd.Source + } + + # Common installation paths to check + $commonPaths = @( + "$env:USERPROFILE\scoop\apps\nssm\current\nssm.exe", + "$env:ProgramData\scoop\apps\nssm\current\nssm.exe", + "$env:LOCALAPPDATA\Microsoft\WinGet\Packages\NSSM.NSSM*\nssm.exe", + "$env:ProgramFiles\WinGet\Packages\NSSM.NSSM*\nssm.exe", + "${env:ProgramFiles(x86)}\WinGet\Packages\NSSM.NSSM*\nssm.exe", + "$env:SystemDrive\Users\*\scoop\apps\nssm\current\nssm.exe" + ) + + foreach ($path in $commonPaths) { + # Handle wildcard paths + if ($path.Contains("*")) { + $foundPaths = Get-ChildItem -Path $path -ErrorAction SilentlyContinue + if ($foundPaths) { + return $foundPaths[0].FullName + } + } else { + if (Test-Path $path) { + return $path + } + } + } + + return $null +} + #endregion #region Installation Methods @@ -39,11 +112,17 @@ function Test-CommandExists { # Function to install Scoop function Install-Scoop { Write-Host "Installing Scoop..." + + # Check if running as admin - Scoop should not be installed as admin + if (Test-Admin) { + throw "Scoop cannot be installed with administrator privileges. Please run this script as a regular user first to install Scoop and beszel-agent, then run as admin to configure the service." + } + try { Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression if (-not (Test-CommandExists "scoop")) { - throw "Failed to install Scoop" + throw "Failed to install Scoop - command not available after installation" } Write-Host "Scoop installed successfully." } @@ -102,8 +181,8 @@ function Install-BeszelAgentWithScoop { Write-Host "Adding beszel bucket..." scoop bucket add beszel https://github.com/henrygd/beszel-scoops | Out-Null - Write-Host "Installing beszel-agent..." - scoop install beszel-agent | Out-Null + Write-Host "Installing / updating beszel-agent..." + scoop install beszel-agent if (-not (Test-CommandExists "beszel-agent")) { throw "Failed to install beszel-agent" @@ -114,8 +193,29 @@ function Install-BeszelAgentWithScoop { # Function to install beszel-agent with WinGet function Install-BeszelAgentWithWinGet { - Write-Host "Installing beszel-agent..." - winget install --exact --id henrygd.beszel-agent --accept-source-agreements --accept-package-agreements | Out-Null + Write-Host "Installing / updating beszel-agent..." + + # Temporarily change ErrorActionPreference to allow WinGet to complete and show output + $originalErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Continue" + + # Use call operator (&) and capture exit code properly + & winget install --exact --id henrygd.beszel-agent --accept-source-agreements --accept-package-agreements | Out-Null + $wingetExitCode = $LASTEXITCODE + + # Restore original ErrorActionPreference + $ErrorActionPreference = $originalErrorActionPreference + + # WinGet exit codes: + # 0 = Success + # -1978335212 (0x8A150014) = No applicable upgrade found (package is up to date) + # -1978335189 (0x8A15002B) = Another "no upgrade needed" variant + # Other codes indicate actual errors + if ($wingetExitCode -eq -1978335212 -or $wingetExitCode -eq -1978335189) { + Write-Host "Package is already up to date." -ForegroundColor Green + } elseif ($wingetExitCode -ne 0) { + Write-Host "WinGet exit code: $wingetExitCode" -ForegroundColor Yellow + } # Refresh PATH environment variable to make beszel-agent available in current session $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") @@ -123,7 +223,6 @@ function Install-BeszelAgentWithWinGet { # Find the path to the beszel-agent executable $agentPath = (Get-Command beszel-agent -ErrorAction SilentlyContinue).Source - if (-not $agentPath) { throw "Could not find beszel-agent executable path after installation" } @@ -204,31 +303,41 @@ function Install-NSSMService { [Parameter(Mandatory=$true)] [string]$Key, [Parameter(Mandatory=$true)] - [int]$Port + [int]$Port, + [string]$NSSMPath = "" ) Write-Host "Installing beszel-agent service..." + # Determine the NSSM executable to use + $nssmCommand = "nssm" + if ($NSSMPath -and (Test-Path $NSSMPath)) { + $nssmCommand = $NSSMPath + Write-Host "Using NSSM from: $NSSMPath" + } elseif (-not (Test-CommandExists "nssm")) { + throw "NSSM is not available in PATH and no valid NSSMPath was provided" + } + # Check if service already exists $existingService = Get-Service -Name "beszel-agent" -ErrorAction SilentlyContinue if ($existingService) { Write-Host "Service already exists. Stopping and removing existing service..." try { - nssm stop beszel-agent - nssm remove beszel-agent confirm + & $nssmCommand stop beszel-agent + & $nssmCommand remove beszel-agent confirm } catch { Write-Host "Warning: Failed to remove existing service: $($_.Exception.Message)" -ForegroundColor Yellow } } - nssm install beszel-agent $AgentPath + & $nssmCommand install beszel-agent $AgentPath if ($LASTEXITCODE -ne 0) { throw "Failed to install beszel-agent service" } Write-Host "Configuring service environment variables..." - nssm set beszel-agent AppEnvironmentExtra "+KEY=$Key" - nssm set beszel-agent AppEnvironmentExtra "+PORT=$Port" + & $nssmCommand set beszel-agent AppEnvironmentExtra "+KEY=$Key" + & $nssmCommand set beszel-agent AppEnvironmentExtra "+PORT=$Port" # Configure log files $logDir = "$env:ProgramData\beszel-agent\logs" @@ -236,8 +345,8 @@ function Install-NSSMService { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } $logFile = "$logDir\beszel-agent.log" - nssm set beszel-agent AppStdout $logFile - nssm set beszel-agent AppStderr $logFile + & $nssmCommand set beszel-agent AppStdout $logFile + & $nssmCommand set beszel-agent AppStderr $logFile } # Function to configure firewall rules @@ -275,8 +384,21 @@ function Configure-Firewall { # Function to start and monitor the service function Start-BeszelAgentService { + param ( + [string]$NSSMPath = "" + ) + Write-Host "Starting beszel-agent service..." - nssm start beszel-agent + + # Determine the NSSM executable to use + $nssmCommand = "nssm" + if ($NSSMPath -and (Test-Path $NSSMPath)) { + $nssmCommand = $NSSMPath + } elseif (-not (Test-CommandExists "nssm")) { + throw "NSSM is not available in PATH and no valid NSSMPath was provided" + } + + & $nssmCommand start beszel-agent $startResult = $LASTEXITCODE # Only enter the status check loop if the NSSM start command failed @@ -293,7 +415,7 @@ function Start-BeszelAgentService { Start-Sleep -Seconds 1 $elapsedTime += 1 - $serviceStatus = nssm status beszel-agent + $serviceStatus = & $nssmCommand status beszel-agent if ($serviceStatus -eq "SERVICE_RUNNING") { $serviceStarted = $true @@ -331,13 +453,15 @@ try { if (-not $AgentPath) { # Check for problematic case: running as admin and need Scoop if ($isAdmin -and -not (Test-CommandExists "scoop") -and -not (Test-CommandExists "winget")) { - Write-Host "ERROR: You're running as administrator but need to install Scoop." -ForegroundColor Red + Write-Host "ERROR: You're running as administrator but neither Scoop nor WinGet is available." -ForegroundColor Red Write-Host "Scoop should be installed without admin privileges." -ForegroundColor Red - Write-Host "Please run this script again without administrator privileges." -ForegroundColor Red + Write-Host "" + Write-Host "Please either:" -ForegroundColor Yellow + Write-Host "1. Run this script again without administrator privileges" -ForegroundColor Yellow + Write-Host "2. Install WinGet and run this script again" -ForegroundColor Yellow exit 1 } - Write-Host "Installing beszel-agent..." if (Test-CommandExists "scoop") { Write-Host "Using Scoop for installation..." $AgentPath = Install-WithScoop -Key $Key -Port $Port @@ -356,27 +480,84 @@ try { throw "Could not find beszel-agent executable. Make sure it was properly installed." } + # Find NSSM path if not already provided + if (-not $NSSMPath) { + $NSSMPath = Find-NSSM + + if (-not $NSSMPath -and (Test-CommandExists "nssm")) { + $NSSMPath = (Get-Command "nssm" -ErrorAction SilentlyContinue).Source + } + + # If we still don't have NSSM, try to install it if we have package managers + if (-not $NSSMPath) { + if (Test-CommandExists "winget") { + Write-Host "NSSM not found. Attempting to install via WinGet..." + try { + Install-NSSM -Method "WinGet" + $NSSMPath = Find-NSSM + if (-not $NSSMPath -and (Test-CommandExists "nssm")) { + $NSSMPath = (Get-Command "nssm" -ErrorAction SilentlyContinue).Source + } + } catch { + Write-Host "Failed to install NSSM via WinGet: $($_.Exception.Message)" -ForegroundColor Yellow + } + } elseif (Test-CommandExists "scoop") { + Write-Host "NSSM not found. Attempting to install via Scoop..." + try { + Install-NSSM -Method "Scoop" + $NSSMPath = Find-NSSM + if (-not $NSSMPath -and (Test-CommandExists "nssm")) { + $NSSMPath = (Get-Command "nssm" -ErrorAction SilentlyContinue).Source + } + } catch { + Write-Host "Failed to install NSSM via Scoop: $($_.Exception.Message)" -ForegroundColor Yellow + } + } + + # Final check - if we still don't have NSSM and we're admin, we have a problem + if (-not $NSSMPath -and ($isAdmin -or $Elevated)) { + throw "NSSM is required for service installation but was not found and could not be installed. Please install NSSM manually or run as a regular user to install it." + } + } + } + # Second: If we need admin rights for service installation and we don't have them, relaunch if (-not $isAdmin -and -not $Elevated) { Write-Host "Admin privileges required for service installation. Relaunching as admin..." -ForegroundColor Yellow Write-Host "Check service status with 'nssm status beszel-agent'" Write-Host "Edit service configuration with 'nssm edit beszel-agent'" + # Prepare arguments for the elevated script + $argumentList = @( + "-ExecutionPolicy", "Bypass", + "-File", "`"$PSCommandPath`"", + "-Elevated", + "-Key", "`"$Key`"", + "-Port", $Port, + "-AgentPath", "`"$AgentPath`"" + ) + + # Add NSSMPath if we found it + if ($NSSMPath) { + $argumentList += "-NSSMPath" + $argumentList += "`"$NSSMPath`"" + } + # Relaunch the script with the -Elevated switch and pass parameters - Start-Process powershell.exe -Verb RunAs -ArgumentList "-File `"$PSCommandPath`" -Elevated -Key `"$Key`" -Port $Port -AgentPath `"$AgentPath`"" + Start-Process powershell.exe -Verb RunAs -ArgumentList $argumentList exit } # Third: If we have admin rights, install service and configure firewall if ($isAdmin -or $Elevated) { # Install the service - Install-NSSMService -AgentPath $AgentPath -Key $Key -Port $Port + Install-NSSMService -AgentPath $AgentPath -Key $Key -Port $Port -NSSMPath $NSSMPath # Configure firewall Configure-Firewall -Port $Port # Start the service - Start-BeszelAgentService + Start-BeszelAgentService -NSSMPath $NSSMPath # Pause to see results if this is an elevated window if ($Elevated) {