Public/ps1/Configuration/Service/Install-ApprxrService.ps1

<#
.SYNOPSIS
    Installs Apprxr as a Windows service using NSSM (Non-Sucking Service Manager).
 
.DESCRIPTION
    Downloads and extracts NSSM to the specified installation folder (or the Apprxr configuration folder if not specified) if not already present. Configures a Windows service to run Apprxr as a background service using PowerShell and NSSM. The service name and entrypoint can be extended with a custom string using the NameExtension parameter. If NameExtension is provided, it is appended to the service name and Start-Apprxr command, and saved to configuration for consistent service management by other Apprxr service commands.
     
    If InstallationFolder is provided, NSSM and the service will be installed in that folder. Otherwise, the Apprxr configuration folder is used by default.
 
.PARAMETER NameExtension
    Optional. A string to append to the service name and Start-Apprxr entrypoint. If provided, the service will be named 'ApprxrService<NameExtension>' and will run 'Start-Apprxr<NameExtension>'. This value is also saved to configuration for use by other service management commands.
 
.PARAMETER InstallationFolder
    Optional. The folder where NSSM and the service should be installed. If not specified, the Apprxr configuration folder is used.
 
.EXAMPLE
    Install-ApprxrService
    # Installs the service as 'ApprxrService' running 'Start-Apprxr' from the Apprxr module, using the default configuration folder.
 
.EXAMPLE
    Install-ApprxrService -NameExtension 'Test'
    # Installs the service as 'ApprxrServiceTest' running 'Start-ApprxrTest', and saves 'Test' as the ServiceNameExtension in configuration.
 
.EXAMPLE
    Install-ApprxrService -NameExtension 'Prod'
    # Installs the service as 'ApprxrServiceProd' running 'Start-ApprxrProd'.
 
.EXAMPLE
    Install-ApprxrService -InstallationFolder 'D:\Services\Apprxr'
    # Installs the service and NSSM in the specified folder instead of the default configuration folder.
 
.EXAMPLE
    Install-ApprxrService -NameExtension 'QA' -InstallationFolder 'D:\Services\ApprxrQA'
    # Installs the service as 'ApprxrServiceQA' running 'Start-ApprxrQA', and installs NSSM in the specified folder.
 
.NOTES
    - Requires administrative privileges to install a Windows service.
    - NSSM is downloaded from https://nssm.cc/release/nssm-2.24.zip if not present.
    - The service will run PowerShell with the Apprxr module imported and execute the Start-Apprxr* command.
    - The NameExtension value is saved to configuration for use by Start/Stop/Remove/Restart-ApprxrService commands.
    - If NSSM extraction fails, the script will throw an error.
    - If InstallationFolder is not specified, the Apprxr configuration folder is used by default.
#>


function Install-ApprxrService {

    [CmdletBinding()]
    param(
        [string]$NameExtension,
        [string]$InstallationFolder
    )

    # Check for administrative privileges
    $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    if (-not $isAdmin) {
        throw 'Install-ApprxrService must be run as Administrator.'
    }

    $targetFolder = if ($InstallationFolder) { $InstallationFolder } else { Get-ApprxrConfigurationFolder }
    if (-not (Test-Path $targetFolder)) {
        New-Item -Path $targetFolder -ItemType Directory -Force | Out-Null
    }

    $nssmExe = Join-Path $targetFolder 'nssm.exe'
    if (-not (Test-Path $nssmExe)) {
        Write-Host "Downloading NSSM..." -ForegroundColor Yellow
        $nssmUrl = 'https://nssm.cc/release/nssm-2.24.zip'
        $zipPath = Join-Path $targetFolder 'nssm.zip'
        Invoke-WebRequest -Uri $nssmUrl -OutFile $zipPath
        Add-Type -AssemblyName System.IO.Compression.FileSystem
        [System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $targetFolder)
        Remove-Item $zipPath
        $nssmExeFound = Get-ChildItem -Path $targetFolder -Recurse -Filter nssm.exe | Select-Object -First 1
        if ($nssmExeFound) {
            Copy-Item $nssmExeFound.FullName $nssmExe -Force
        } else {
            throw "NSSM executable not found after extraction."
        }
    }
    # Use pwsh if available, otherwise fallback to powershell.exe
    $pwshCmd = Get-Command pwsh -ErrorAction SilentlyContinue
    if ($pwshCmd) {
        $pwshPath = $pwshCmd.Source
    } else {
        $pwshCmd = Get-Command powershell -ErrorAction SilentlyContinue
        if ($pwshCmd) {
            $pwshPath = $pwshCmd.Source
        } else {
            throw 'Neither pwsh nor powershell.exe was found on this system.'
        }
    }
    $modulePath = Join-Path $PSScriptRoot '..'
    $modulePath = Join-Path $modulePath '..'
    $modulePath = Join-Path $modulePath '..'
    $modulePath = Join-Path $modulePath 'Apprxr.psm1'
    $modulePath = [System.IO.Path]::GetFullPath($modulePath)
    $serviceSuffix = if ($NameExtension) { "$NameExtension" } else { '' }
    $ServiceName = "ApprxrService$serviceSuffix"
    $startCommand = "Start-Apprxr"
    $arguments = "start-apprxr; start-sleep 86400"
    & $nssmExe install $ServiceName $pwshPath $arguments
    if ($NameExtension) {
        Set-ApprxrConfigurationValue -Name 'ServiceNameExtension' -Value $NameExtension
    }
    Log-Apprxr "Service '$ServiceName' installed to run $startCommand from module using NSSM in folder '$targetFolder'."
}