diff --git a/supplemental/scripts/upgrade-agent-wrapper.ps1 b/supplemental/scripts/upgrade-agent-wrapper.ps1 new file mode 100644 index 0000000..2f93675 --- /dev/null +++ b/supplemental/scripts/upgrade-agent-wrapper.ps1 @@ -0,0 +1,82 @@ +param ( + [switch]$Elevated +) + +# Beszel Agent Upgrade Wrapper +# This script downloads and executes the latest upgrade script from GitHub + +$ErrorActionPreference = "Stop" + +try { + Write-Host "Beszel Agent Upgrade Wrapper" -ForegroundColor Cyan + Write-Host "============================" -ForegroundColor Cyan + Write-Host "" + + # Define the URL for the latest upgrade script + $scriptUrl = "https://raw.githubusercontent.com/henrygd/beszel/main/supplemental/scripts/upgrade-agent.ps1" + $tempScriptPath = "$env:TEMP\beszel-upgrade-agent-$(Get-Date -Format 'yyyyMMdd-HHmmss').ps1" + + Write-Host "Downloading latest upgrade script..." -ForegroundColor Yellow + Write-Host "From: $scriptUrl" + Write-Host "To: $tempScriptPath" + + # Download the latest upgrade script + try { + Invoke-WebRequest -Uri $scriptUrl -OutFile $tempScriptPath -UseBasicParsing + Write-Host "Download completed successfully." -ForegroundColor Green + } + catch { + Write-Host "Failed to download upgrade script: $($_.Exception.Message)" -ForegroundColor Red + Write-Host "Please check your internet connection and try again." -ForegroundColor Red + exit 1 + } + + # Verify the script was downloaded + if (-not (Test-Path $tempScriptPath)) { + Write-Host "ERROR: Downloaded script not found at $tempScriptPath" -ForegroundColor Red + exit 1 + } + + Write-Host "" + Write-Host "Executing upgrade script..." -ForegroundColor Yellow + + # Execute the downloaded script with the same parameters + if ($Elevated) { + & $tempScriptPath -Elevated + } else { + & $tempScriptPath + } + + $scriptExitCode = $LASTEXITCODE + + Write-Host "" + Write-Host "Cleaning up temporary files..." -ForegroundColor Yellow + + # Clean up the temporary script + try { + Remove-Item $tempScriptPath -Force -ErrorAction SilentlyContinue + Write-Host "Cleanup completed." -ForegroundColor Green + } + catch { + Write-Host "Warning: Could not remove temporary script: $tempScriptPath" -ForegroundColor Yellow + } + + # Exit with the same code as the upgrade script + exit $scriptExitCode +} +catch { + Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red + Write-Host "Upgrade wrapper failed. Please check the error message above." -ForegroundColor Red + + # Clean up on error + if ($tempScriptPath -and (Test-Path $tempScriptPath)) { + try { + Remove-Item $tempScriptPath -Force -ErrorAction SilentlyContinue + } + catch { + # Ignore cleanup errors + } + } + + exit 1 +} \ No newline at end of file diff --git a/supplemental/scripts/upgrade-agent.ps1 b/supplemental/scripts/upgrade-agent.ps1 new file mode 100644 index 0000000..c0f0996 --- /dev/null +++ b/supplemental/scripts/upgrade-agent.ps1 @@ -0,0 +1,397 @@ +param ( + [switch]$Elevated +) + +# Stop on first error +$ErrorActionPreference = "Stop" + +#region Utility Functions + +# Function to check if running as admin +function Test-Admin { + return ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +# Function to check if a command exists +function Test-CommandExists { + param ( + [Parameter(Mandatory=$true)] + [string]$Command + ) + 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 Upgrade Functions + +# Function to upgrade beszel-agent with Scoop +function Upgrade-BeszelAgentWithScoop { + Write-Host "Upgrading beszel-agent with Scoop..." + scoop update beszel-agent + + if (-not (Test-CommandExists "beszel-agent")) { + throw "Failed to upgrade beszel-agent with Scoop" + } + + return $(Join-Path -Path $(scoop prefix beszel-agent) -ChildPath "beszel-agent.exe") +} + +# Function to upgrade beszel-agent with WinGet +function Upgrade-BeszelAgentWithWinGet { + Write-Host "Upgrading beszel-agent with WinGet..." + + # Temporarily change ErrorActionPreference to allow WinGet to complete and show output + $originalErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Continue" + + # Use call operator (&) and capture exit code properly + & winget upgrade --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") + + # Find the path to the beszel-agent executable + $agentPath = (Get-Command beszel-agent -ErrorAction SilentlyContinue).Source + + if (-not $agentPath) { + # Try to find it using our search function + $agentPath = Find-BeszelAgent + if (-not $agentPath) { + throw "Could not find beszel-agent executable path after upgrade" + } + } + + return $agentPath +} + +# Function to get current service configuration +function Get-ServiceConfiguration { + param ( + [string]$NSSMPath = "" + ) + + # 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" + } + + # Check if service exists + $existingService = Get-Service -Name "beszel-agent" -ErrorAction SilentlyContinue + if (-not $existingService) { + throw "beszel-agent service does not exist. Please run the installation script first." + } + + # Get current service configuration + $config = @{} + + try { + # Get current application path + $currentPath = & $nssmCommand get beszel-agent Application + if ($LASTEXITCODE -eq 0) { + $config.CurrentPath = $currentPath.Trim() + } + + # Get environment variables + $envVars = & $nssmCommand get beszel-agent AppEnvironmentExtra + if ($LASTEXITCODE -eq 0 -and $envVars) { + $config.EnvironmentVars = $envVars + } + + Write-Host "Current service configuration retrieved successfully." + Write-Host "Current agent path: $($config.CurrentPath)" + + return $config + } + catch { + throw "Failed to retrieve current service configuration: $($_.Exception.Message)" + } +} + +# Function to update service path +function Update-ServicePath { + param ( + [Parameter(Mandatory=$true)] + [string]$NewAgentPath, + [string]$NSSMPath = "" + ) + + Write-Host "Updating beszel-agent service path..." + + # 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" + } + + # Stop the service + Write-Host "Stopping beszel-agent service..." + & $nssmCommand stop beszel-agent + if ($LASTEXITCODE -ne 0) { + Write-Host "Warning: Failed to stop service, continuing anyway..." -ForegroundColor Yellow + } + + # Update the application path + & $nssmCommand set beszel-agent Application $NewAgentPath + if ($LASTEXITCODE -ne 0) { + throw "Failed to update beszel-agent service path" + } + + Write-Host "Service path updated to: $NewAgentPath" + + # Start the service + Write-Host "Starting beszel-agent service..." + & $nssmCommand start beszel-agent + $startResult = $LASTEXITCODE + + # Only enter the status check loop if the NSSM start command failed + if ($startResult -ne 0) { + Write-Host "NSSM start command returned error code: $startResult" -ForegroundColor Yellow + Write-Host "This could be due to 'SERVICE_START_PENDING' state. Checking service status..." + + # Allow up to 10 seconds for the service to start, checking every second + $maxWaitTime = 10 # seconds + $elapsedTime = 0 + $serviceStarted = $false + + while (-not $serviceStarted -and $elapsedTime -lt $maxWaitTime) { + Start-Sleep -Seconds 1 + $elapsedTime += 1 + + $serviceStatus = & $nssmCommand status beszel-agent + + if ($serviceStatus -eq "SERVICE_RUNNING") { + $serviceStarted = $true + Write-Host "Success! The beszel-agent service is now running with the updated path." -ForegroundColor Green + } + elseif ($serviceStatus -like "*PENDING*") { + Write-Host "Service is still starting (status: $serviceStatus)... waiting" -ForegroundColor Yellow + } + else { + Write-Host "Warning: The service status is '$serviceStatus' instead of 'SERVICE_RUNNING'." -ForegroundColor Yellow + Write-Host "You may need to troubleshoot the service installation." -ForegroundColor Yellow + break + } + } + + if (-not $serviceStarted) { + Write-Host "Service did not reach running state." -ForegroundColor Yellow + Write-Host "You can check status manually with 'nssm status beszel-agent'" -ForegroundColor Yellow + } + } else { + # NSSM start command was successful + Write-Host "Success! The beszel-agent service is running with the updated path." -ForegroundColor Green + } +} + +#endregion + +#region Main Script Execution + +# Check if we're running as admin +$isAdmin = Test-Admin + +try { + Write-Host "Beszel Agent Upgrade Script" -ForegroundColor Cyan + Write-Host "===========================" -ForegroundColor Cyan + Write-Host "" + + # First: Check if service exists (doesn't require admin) + $existingService = Get-Service -Name "beszel-agent" -ErrorAction SilentlyContinue + if (-not $existingService) { + Write-Host "ERROR: beszel-agent service does not exist." -ForegroundColor Red + Write-Host "Please run the installation script first before attempting to upgrade." -ForegroundColor Red + exit 1 + } + + # Find current NSSM and agent paths + $nssmPath = Find-NSSM + if (-not $nssmPath -and (Test-CommandExists "nssm")) { + $nssmPath = (Get-Command "nssm" -ErrorAction SilentlyContinue).Source + } + + if (-not $nssmPath) { + Write-Host "ERROR: NSSM not found. Cannot manage the service without NSSM." -ForegroundColor Red + exit 1 + } + + # Get current service configuration (doesn't require admin) + Write-Host "Retrieving current service configuration..." + $currentConfig = Get-ServiceConfiguration -NSSMPath $nssmPath + + # Upgrade the agent (doesn't require admin) + Write-Host "Upgrading beszel-agent..." + $newAgentPath = $null + + if (Test-CommandExists "scoop") { + Write-Host "Using Scoop for upgrade..." + $newAgentPath = Upgrade-BeszelAgentWithScoop + } + elseif (Test-CommandExists "winget") { + Write-Host "Using WinGet for upgrade..." + $newAgentPath = Upgrade-BeszelAgentWithWinGet + } + else { + Write-Host "ERROR: Neither Scoop nor WinGet is available for upgrading." -ForegroundColor Red + exit 1 + } + + if (-not $newAgentPath) { + $newAgentPath = Find-BeszelAgent + if (-not $newAgentPath) { + throw "Could not find beszel-agent executable after upgrade." + } + } + + Write-Host "New agent path: $newAgentPath" + + # Check if the path has changed + if ($currentConfig.CurrentPath -eq $newAgentPath) { + Write-Host "Agent path has not changed. Service update not needed." -ForegroundColor Green + Write-Host "Upgrade completed successfully!" -ForegroundColor Green + exit 0 + } + + Write-Host "Agent path has changed from:" -ForegroundColor Yellow + Write-Host " Old: $($currentConfig.CurrentPath)" -ForegroundColor Yellow + Write-Host " New: $newAgentPath" -ForegroundColor Yellow + Write-Host "" + + # If we need admin rights for service update and we don't have them, relaunch + if (-not $isAdmin -and -not $Elevated) { + Write-Host "Admin privileges required for service path update. Relaunching as admin..." -ForegroundColor Yellow + + # Prepare arguments for the elevated script + $argumentList = @( + "-ExecutionPolicy", "Bypass", + "-File", "`"$PSCommandPath`"", + "-Elevated" + ) + + # Relaunch the script with the -Elevated switch + Start-Process powershell.exe -Verb RunAs -ArgumentList $argumentList + exit + } + + # Update service path (requires admin) + if ($isAdmin -or $Elevated) { + Update-ServicePath -NewAgentPath $newAgentPath -NSSMPath $nssmPath + + Write-Host "" + Write-Host "Upgrade completed successfully!" -ForegroundColor Green + Write-Host "The beszel-agent service has been updated to use the new executable path." -ForegroundColor Green + + # Pause to see results if this is an elevated window + if ($Elevated) { + Write-Host "" + Write-Host "Press any key to exit..." -ForegroundColor Cyan + $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") + } + } +} +catch { + Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red + Write-Host "Upgrade failed. Please check the error message above." -ForegroundColor Red + + # Pause if this is likely a new window + if ($Elevated -or (-not $isAdmin)) { + Write-Host "Press any key to exit..." -ForegroundColor Red + $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") + } + exit 1 +} + +#endregion \ No newline at end of file