#Requires -RunAsAdministrator $ErrorActionPreference = "Stop" # Verify running as Administrator $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = [Security.Principal.WindowsPrincipal]$identity if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Error "This script must be run as a machine Administrator." exit 1 } $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition $InstallDir = "C:\Program Files\Nebula" $ConfigPath = "$InstallDir\config.yml" $ExpectedBinPath = "`"$InstallDir\nebula.exe`" -service run -config `"$ConfigPath`"" # --- Validate all required files before making any changes --- $missing = @() # Per-host files expected in the working directory $HostFiles = @("config.yml", "host.crt", "host.key") foreach ($file in $HostFiles) { if (-not (Test-Path (Join-Path $PWD $file))) { $missing += "$file (expected in working directory: $PWD)" } } # Package files expected alongside the script $PackageFiles = @("ca.crt", "nebula.exe") foreach ($file in $PackageFiles) { if (-not (Test-Path (Join-Path $ScriptDir $file))) { $missing += "$file (expected in script directory: $ScriptDir)" } } if (-not (Test-Path (Join-Path $ScriptDir "dist"))) { $missing += "dist\ (expected in script directory: $ScriptDir)" } if ($missing.Count -gt 0) { Write-Error "Missing required files:`n - $($missing -join "`n - ")" exit 1 } # --- Check if already installed and running correctly --- $existing = Get-Service -Name "nebula" -ErrorAction SilentlyContinue if ($existing) { $svcConfig = sc.exe qc nebula 2>&1 | Out-String $correctBinary = $svcConfig -match [regex]::Escape($ExpectedBinPath) if ($existing.Status -eq "Running" -and $correctBinary) { # Compare all source files against installed files by hash $allMatch = $true foreach ($file in $PackageFiles) { $src = (Get-FileHash (Join-Path $ScriptDir $file)).Hash $dst = (Get-FileHash (Join-Path $InstallDir $file) -ErrorAction SilentlyContinue).Hash if ($src -ne $dst) { $allMatch = $false; break } } if ($allMatch) { foreach ($file in $HostFiles) { $src = (Get-FileHash (Join-Path $PWD $file)).Hash $dst = (Get-FileHash (Join-Path $InstallDir $file) -ErrorAction SilentlyContinue).Hash if ($src -ne $dst) { $allMatch = $false; break } } } if ($allMatch) { Write-Host "Nebula is already installed and running with matching files. No changes needed." exit 0 } Write-Host "Nebula is running but files have changed. Reinstalling..." } Write-Host "Stopping existing Nebula service..." if ($existing.Status -eq "Running") { sc.exe stop nebula | Out-Null # Wait for the service to fully stop $timeout = 10 while ($timeout -gt 0) { $svc = Get-Service -Name "nebula" -ErrorAction SilentlyContinue if ($svc.Status -eq "Stopped") { break } Start-Sleep -Seconds 1 $timeout-- } if ($timeout -eq 0) { Write-Error "Timed out waiting for Nebula service to stop." exit 1 } } sc.exe delete nebula | Out-Null if ($LASTEXITCODE -ne 0) { Write-Error "Failed to remove existing Nebula service. A reboot may be required if it is marked for deletion." exit 1 } Start-Sleep -Seconds 1 } # --- Create install directory --- if (-not (Test-Path $InstallDir)) { try { New-Item -ItemType Directory -Path $InstallDir | Out-Null } catch { Write-Error "Failed to create install directory: $_" exit 1 } } # --- Copy files --- try { Write-Host "Copying package files..." Copy-Item (Join-Path $ScriptDir "ca.crt") $InstallDir -Force Copy-Item (Join-Path $ScriptDir "nebula.exe") $InstallDir -Force Copy-Item (Join-Path $ScriptDir "dist") $InstallDir -Recurse -Force Write-Host "Copying host files..." foreach ($file in $HostFiles) { Copy-Item (Join-Path $PWD $file) $InstallDir -Force } } catch { Write-Error "Failed to copy files: $_" exit 1 } # --- Verify files landed in install directory --- $AllFiles = $PackageFiles + $HostFiles foreach ($file in $AllFiles) { if (-not (Test-Path (Join-Path $InstallDir $file))) { Write-Error "File copy verification failed: $file not found in $InstallDir" exit 1 } } if (-not (Test-Path (Join-Path $InstallDir "dist"))) { Write-Error "File copy verification failed: dist\ not found in $InstallDir" exit 1 } # --- Install and configure service --- Write-Host "Installing Nebula service..." & "$InstallDir\nebula.exe" -service install -config "$ConfigPath" if ($LASTEXITCODE -ne 0) { Write-Error "Failed to install Nebula service." exit 1 } # Wait for real network connectivity before starting, not just the TCP/IP stack sc.exe config nebula depend= Tcpip/NlaSvc | Out-Null if ($LASTEXITCODE -ne 0) { Write-Error "Failed to set service dependencies." exit 1 } # Delayed start gives Wi-Fi/Ethernet time to fully associate and get an IP sc.exe config nebula start= delayed-auto | Out-Null if ($LASTEXITCODE -ne 0) { Write-Error "Failed to set delayed-auto start." exit 1 } # --- Start and verify --- Write-Host "Starting Nebula service..." sc.exe start nebula | Out-Null if ($LASTEXITCODE -ne 0) { Write-Error "Failed to start Nebula service. Check Event Viewer (Application log, source 'nebula')." exit 1 } Start-Sleep -Seconds 3 $svc = Get-Service -Name "nebula" if ($svc.Status -eq "Running") { Write-Host "Nebula service is running." } else { Write-Error "Nebula service failed to start. Check Event Viewer (Application log, source 'nebula')." exit 1 }