From 8ceb76c73393b958ff08e6f087515905c1bd1e27 Mon Sep 17 00:00:00 2001 From: Steve Cliff Date: Mon, 4 May 2026 11:15:18 +0100 Subject: [PATCH] deploy: P2-17 install.ps1 (Windows installer) Pwsh installer that detects arch, downloads $Server/agent/binary?os=windows&arch=amd64 to C:\Program Files\restic-manager\, runs the agent in -enroll-server [+ -enroll-token] mode (token flow OR announce-and-approve), then calls 'restic-manager-agent install' to register the SCM service. Surfaces existing scheduled tasks named *restic* without disabling. CLAUDE.md restage block updated to also stage install.ps1 alongside install.sh. --- CLAUDE.md | 2 + deploy/install/install.ps1 | 133 +++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 deploy/install/install.ps1 diff --git a/CLAUDE.md b/CLAUDE.md index c623059..64136fc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -43,6 +43,8 @@ cp bin/restic-manager-agent \ /tmp/rm-smoke/data/agent-binaries/restic-manager-agent-linux-amd64 cp deploy/install/install.sh \ /tmp/rm-smoke/data/install/install.sh +cp deploy/install/install.ps1 \ + /tmp/rm-smoke/data/install/install.ps1 cp deploy/install/restic-manager-agent.service \ /tmp/rm-smoke/data/install/restic-manager-agent.service diff --git a/deploy/install/install.ps1 b/deploy/install/install.ps1 new file mode 100644 index 0000000..72b7c9d --- /dev/null +++ b/deploy/install/install.ps1 @@ -0,0 +1,133 @@ +# install.ps1 — Windows installer for the restic-manager agent (P2-17). +# +# Usage (Run as administrator): +# $env:RM_SERVER = "https://restic.lab.example" +# $env:RM_TOKEN = "" # omit for announce-and-approve +# iwr "$env:RM_SERVER/install/install.ps1" -UseBasicParsing | iex +# +# What it does: +# 1. checks for admin elevation +# 2. downloads the matching agent binary from the server +# 3. lays down C:\Program Files\restic-manager\ and +# C:\ProgramData\restic-manager\ (config + state) +# 4. registers the agent as a Windows service via the agent's own +# `install` subcommand (which uses the SCM API) +# 5. enrolls (token flow if RM_TOKEN set, otherwise announce flow) +# by spawning the agent with the right CLI flags and waits +# until config is written +# 6. surfaces (but does NOT disable) any existing scheduled tasks +# whose name contains "restic" so the operator can decide +# +# Idempotent — safe to re-run. + +[CmdletBinding()] +param( + [string]$Server = $env:RM_SERVER, + [string]$Token = $env:RM_TOKEN, + [string]$InstallDir = 'C:\Program Files\restic-manager', + [string]$DataDir = 'C:\ProgramData\restic-manager' +) + +$ErrorActionPreference = 'Stop' + +function Test-Admin { + $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $pri = New-Object System.Security.Principal.WindowsPrincipal($id) + return $pri.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) +} + +function Detect-Arch { + switch ($env:PROCESSOR_ARCHITECTURE) { + 'AMD64' { return 'amd64' } + 'ARM64' { return 'arm64' } + default { throw "unsupported PROCESSOR_ARCHITECTURE: $($env:PROCESSOR_ARCHITECTURE)" } + } +} + +function Detect-ResticTasks { + Write-Host '' + Write-Host '— Existing restic-named scheduled tasks (review manually) —' + try { + $tasks = Get-ScheduledTask -ErrorAction SilentlyContinue | + Where-Object { $_.TaskName -match 'restic' -or $_.TaskPath -match 'restic' } + if ($tasks) { + foreach ($t in $tasks) { + Write-Host " * $($t.TaskPath)$($t.TaskName) state=$($t.State)" + Write-Host " Disable with: Disable-ScheduledTask -TaskName '$($t.TaskName)' -TaskPath '$($t.TaskPath)'" + } + } else { + Write-Host ' (none found)' + } + } catch { + Write-Host ' (Get-ScheduledTask failed; review the Task Scheduler UI manually)' + } + Write-Host '' +} + +# --- preflight ------------------------------------------------------- + +if (-not (Test-Admin)) { + throw 'install.ps1: must be run from an elevated PowerShell (Run as administrator).' +} +if (-not $Server) { + throw 'install.ps1: -Server (or $env:RM_SERVER) is required, e.g. https://restic.lab.example' +} + +$arch = Detect-Arch +Write-Host "install.ps1: server=$Server arch=$arch" + +# --- directories ----------------------------------------------------- + +New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null +New-Item -ItemType Directory -Force -Path $DataDir | Out-Null + +# --- download agent -------------------------------------------------- + +$agentExe = Join-Path $InstallDir 'restic-manager-agent.exe' +$tmpExe = "$agentExe.tmp" +$dlURL = "$Server/agent/binary?os=windows&arch=$arch" +Write-Host "install.ps1: downloading $dlURL" +Invoke-WebRequest -UseBasicParsing -Uri $dlURL -OutFile $tmpExe +# Atomic-ish replace: stop service if running so the .exe isn't busy. +try { Stop-Service -Name 'restic-manager-agent' -ErrorAction SilentlyContinue } catch {} +Move-Item -Force -Path $tmpExe -Destination $agentExe + +# --- enroll / announce ----------------------------------------------- + +$cfgPath = Join-Path $DataDir 'agent.yaml' +$args = @('-config', $cfgPath, '-enroll-server', $Server) +if ($Token) { + $args += @('-enroll-token', $Token) + Write-Host 'install.ps1: enrolling with one-time token' +} else { + Write-Host 'install.ps1: no RM_TOKEN — running announce-and-approve flow.' + Write-Host ' The fingerprint will print below. Compare it with the dashboard before clicking Accept.' +} +& $agentExe @args +if ($LASTEXITCODE -ne 0) { + throw "install.ps1: agent enrolment failed (exit $LASTEXITCODE)" +} + +# --- install + start service ---------------------------------------- + +# The 'install' subcommand registers the service via the SCM. If +# already registered, it errors loudly — re-run with -Force only if +# you've manually verified. +try { + & $agentExe install +} catch { + Write-Host "install.ps1: service may already be registered ($_); continuing." +} +try { + Start-Service -Name 'restic-manager-agent' +} catch { + Write-Host "install.ps1: Start-Service failed ($_); check Event Viewer." +} + +Detect-ResticTasks + +Write-Host '' +Write-Host 'install.ps1: done.' +Write-Host " config : $cfgPath" +Write-Host " binary : $agentExe" +Write-Host " service: restic-manager-agent (Get-Service to inspect)"