PowerShell Installer Template
Overview
Complete PowerShell Installer Template
#Requires -Version 5.1
<#
.SYNOPSIS
{{app_name}} Installer Script for Windows
.DESCRIPTION
{{app_description}}
Generated by I.S.A.A.C. v{{isaac_version}}
.PARAMETER ConfigFile
Path to configuration file (optional)
.PARAMETER NonInteractive
Run in non-interactive mode using defaults
.PARAMETER SkipDependencies
Skip dependency installation
.PARAMETER DryRun
Show what would be installed without making changes
.PARAMETER Verbose
Enable verbose logging
.PARAMETER Force
Force installation over existing installation
.EXAMPLE
.\install_windows.ps1
.EXAMPLE
.\install_windows.ps1 -NonInteractive -Verbose
.NOTES
Version: {{app_version}}
Generated: {{generation_timestamp}}
Requires: PowerShell 5.1 or later, Administrator privileges
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$false)]
[string]$ConfigFile = "",
[Parameter(Mandatory=$false)]
[switch]$NonInteractive,
[Parameter(Mandatory=$false)]
[switch]$SkipDependencies,
[Parameter(Mandatory=$false)]
[switch]$DryRun,
[Parameter(Mandatory=$false)]
[switch]$Force
)
# =============================================================================
# CONFIGURATION VARIABLES
# =============================================================================
# Set error handling
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
# Script configuration
$Script:ScriptName = $MyInvocation.MyCommand.Name
$Script:ScriptPath = $PSScriptRoot
$Script:LogFile = "$env:TEMP\{{app_name}}_install_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
$Script:TempDir = "$env:TEMP\{{app_name}}_install_$PID"
$Script:BackupDir = "$env:TEMP\{{app_name}}_backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
# Application configuration
$Script:AppName = "{{app_name}}"
$Script:AppVersion = "{{app_version}}"
$Script:AppDescription = "{{app_description}}"
$Script:InstallPath = "{{install_path}}"
$Script:ServiceName = "{{service_name}}"
$Script:ServiceDisplayName = "{{service_display_name}}"
# Default configuration values
$Script:Config = @{
{{#each default_config}}
{{name}} = "{{value}}"
{{/each}}
}
# Installation flags
$Script:InteractiveMode = -not $NonInteractive
$Script:VerboseLogging = $VerbosePreference -eq "Continue"
# =============================================================================
# UTILITY FUNCTIONS
# =============================================================================
function Write-Log {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Message,
[Parameter(Mandatory=$false)]
[ValidateSet("Info", "Warning", "Error", "Debug")]
[string]$Level = "Info"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$Level] $timestamp - $Message"
# Write to console with appropriate color
switch ($Level) {
"Info" { Write-Host $logEntry -ForegroundColor Green }
"Warning" { Write-Host $logEntry -ForegroundColor Yellow }
"Error" { Write-Host $logEntry -ForegroundColor Red }
"Debug" { if ($Script:VerboseLogging) { Write-Host $logEntry -ForegroundColor Cyan } }
}
# Write to log file
Add-Content -Path $Script:LogFile -Value $logEntry -Encoding UTF8
}
function Show-Progress {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[int]$Current,
[Parameter(Mandatory=$true)]
[int]$Total,
[Parameter(Mandatory=$true)]
[string]$Activity,
[Parameter(Mandatory=$false)]
[string]$Status = "Processing..."
)
$percentComplete = [math]::Round(($Current / $Total) * 100, 0)
Write-Progress -Activity $Activity -Status $Status -PercentComplete $percentComplete
if ($Current -eq $Total) {
Write-Progress -Activity $Activity -Completed
}
}
function Test-Administrator {
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Test-PowerShellVersion {
$requiredVersion = [Version]"5.1"
$currentVersion = $PSVersionTable.PSVersion
if ($currentVersion -lt $requiredVersion) {
throw "PowerShell $requiredVersion or later is required. Current version: $currentVersion"
}
return $true
}
function Get-WindowsVersion {
$version = [System.Environment]::OSVersion.Version
return "$($version.Major).$($version.Minor).$($version.Build)"
}
function Test-WindowsVersion {
$currentVersion = Get-WindowsVersion
$requiredVersion = "{{min_windows_version}}"
if ([Version]$currentVersion -lt [Version]$requiredVersion) {
throw "Windows $requiredVersion or later is required. Current version: $currentVersion"
}
return $true
}
# =============================================================================
# USER INTERFACE FUNCTIONS
# =============================================================================
function Show-Welcome {
Clear-Host
$welcomeText = @"
╔══════════════════════════════════════════════════════════════╗
║ {{app_name}} Installer ║
║ Version {{app_version}} ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ {{app_description}} ║
║ ║
║ This installer will guide you through the setup process. ║
║ You can exit at any time with Ctrl+C. ║
║ ║
║ Estimated installation time: {{estimated_time}} ║
║ Required disk space: {{required_space}} ║
║ ║
╚══════════════════════════════════════════════════════════════╝
"@
Write-Host $welcomeText -ForegroundColor Cyan
Write-Host ""
if ($Script:InteractiveMode) {
Read-Host "Press Enter to continue or Ctrl+C to exit"
}
}
function Show-Menu {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Title,
[Parameter(Mandatory=$true)]
[string[]]$Options
)
Write-Host ""
Write-Host "┌─ $Title ─────────────────────────────────────────────────────┐" -ForegroundColor Cyan
Write-Host "│ │" -ForegroundColor Cyan
for ($i = 0; $i -lt $Options.Length; $i++) {
$option = " $($i + 1)) $($Options[$i])"
Write-Host "│$($option.PadRight(62))│" -ForegroundColor White
}
Write-Host "│ │" -ForegroundColor Cyan
Write-Host "│ Q) Quit installer │" -ForegroundColor Yellow
Write-Host "│ H) Help │" -ForegroundColor Yellow
Write-Host "└──────────────────────────────────────────────────────────────┘" -ForegroundColor Cyan
Write-Host ""
}
function Get-MenuChoice {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[int]$MaxChoice
)
do {
$choice = Read-Host "Enter your choice [1-$MaxChoice, Q, H]"
switch ($choice.ToUpper()) {
"Q" {
Write-Host "Installation cancelled by user." -ForegroundColor Yellow
Exit-Installation 0
}
"H" {
Show-Help
continue
}
default {
if ($choice -match '^\d+$' -and [int]$choice -ge 1 -and [int]$choice -le $MaxChoice) {
return [int]$choice
} else {
Write-Host "Invalid choice. Please enter a number between 1 and $MaxChoice, Q to quit, or H for help." -ForegroundColor Red
}
}
}
} while ($true)
}
function Get-ValidatedInput {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Prompt,
[Parameter(Mandatory=$true)]
[scriptblock]$Validator,
[Parameter(Mandatory=$false)]
[string]$Default = ""
)
do {
if ($Default) {
$input = Read-Host "$Prompt [$Default]"
if ([string]::IsNullOrEmpty($input)) { $input = $Default }
} else {
$input = Read-Host $Prompt
}
if ([string]::IsNullOrEmpty($input) -and [string]::IsNullOrEmpty($Default)) {
Write-Host "This field is required. Please enter a value." -ForegroundColor Red
continue
}
$validationResult = & $Validator $input
if ($validationResult -eq $true) {
return $input
} else {
Write-Host "Invalid input: $validationResult" -ForegroundColor Red
Write-Host "Please try again." -ForegroundColor Yellow
}
} while ($true)
}
# =============================================================================
# VALIDATION FUNCTIONS
# =============================================================================
function Test-Port {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Port
)
# Check if it's a number
if (-not ($Port -match '^\d+$')) {
return "Port must be a number"
}
$portNumber = [int]$Port
# Check range
if ($portNumber -lt 1024 -or $portNumber -gt 65535) {
return "Port must be between 1024 and 65535"
}
# Check if port is in use
$portInUse = Get-NetTCPConnection -LocalPort $portNumber -ErrorAction SilentlyContinue
if ($portInUse) {
if ($Script:InteractiveMode) {
$continue = Read-Host "Warning: Port $portNumber appears to be in use. Continue anyway? [y/N]"
if ($continue -notmatch '^[Yy]$') {
return "Port is in use and user chose not to continue"
}
} else {
return "Port $portNumber is in use"
}
}
return $true
}
function Test-Path {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Path
)
# Check if path is valid
try {
$null = [System.IO.Path]::GetFullPath($Path)
} catch {
return "Invalid path format"
}
# Check if parent directory exists or can be created
$parentPath = Split-Path -Path $Path -Parent
if (-not (Test-Path -Path $parentPath)) {
try {
New-Item -Path $parentPath -ItemType Directory -Force -WhatIf | Out-Null
} catch {
return "Cannot create parent directory: $parentPath"
}
}
return $true
}
# =============================================================================
# SYSTEM CHECKS
# =============================================================================
function Test-SystemRequirements {
Write-Log "Checking system requirements..." -Level Info
try {
# Check PowerShell version
Test-PowerShellVersion
Write-Log "PowerShell version check passed" -Level Info
# Check Windows version
Test-WindowsVersion
$windowsVersion = Get-WindowsVersion
Write-Log "Windows version: $windowsVersion - compatible" -Level Info
# Check administrator privileges
if (-not (Test-Administrator)) {
throw "Administrator privileges are required for installation"
}
Write-Log "Administrator privileges confirmed" -Level Info
# Check architecture
$architecture = $env:PROCESSOR_ARCHITECTURE
Write-Log "Architecture: $architecture" -Level Info
if ($architecture -notin @("AMD64", "ARM64")) {
Write-Log "Untested architecture: $architecture" -Level Warning
}
# Check required tools
$requiredTools = @({{#each required_tools}}"{{this}}"{{#unless @last}}, {{/unless}}{{/each}})
foreach ($tool in $requiredTools) {
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) {
throw "Required tool not found: $tool"
}
}
Write-Log "Required tools check passed" -Level Info
# Check disk space
$installDrive = (Split-Path -Path $Script:InstallPath -Qualifier)
$driveInfo = Get-WmiObject -Class Win32_LogicalDisk | Where-Object { $_.DeviceID -eq $installDrive }
$availableSpaceGB = [math]::Round($driveInfo.FreeSpace / 1GB, 2)
$requiredSpaceGB = {{required_disk_space_gb}}
if ($availableSpaceGB -lt $requiredSpaceGB) {
throw "Insufficient disk space. Required: ${requiredSpaceGB}GB, Available: ${availableSpaceGB}GB"
}
Write-Log "Disk space check passed: ${availableSpaceGB}GB available" -Level Info
Write-Log "System requirements check completed successfully" -Level Info
return $true
} catch {
Write-Log "System requirements check failed: $($_.Exception.Message)" -Level Error
throw
}
}
# =============================================================================
# CONFIGURATION COLLECTION
# =============================================================================
function Get-Configuration {
if (-not $Script:InteractiveMode) {
Write-Log "Using default configuration (non-interactive mode)" -Level Info
return
}
Write-Log "Collecting configuration parameters..." -Level Info
Write-Host ""
Write-Host "╔══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║ Configuration Setup ║" -ForegroundColor Cyan
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
Write-Host ""
{{#each configurable_parameters}}
{{#if (eq type "choice")}}
# {{description}}
Write-Host "{{prompt}}:" -ForegroundColor Yellow
$choices = @(
{{#each choices}}
@{ Value = "{{value}}"; Label = "{{label}}"; Description = "{{description}}" }{{#unless @last}},{{/unless}}
{{/each}}
)
for ($i = 0; $i -lt $choices.Length; $i++) {
Write-Host " $($i + 1)) $($choices[$i].Label) - $($choices[$i].Description)" -ForegroundColor White
}
do {
$choice = Read-Host "Choice [{{default_index}}]"
if ([string]::IsNullOrEmpty($choice)) { $choice = "{{default_index}}" }
if ($choice -match '^\d+$' -and [int]$choice -ge 1 -and [int]$choice -le $choices.Length) {
$Script:Config.{{name}} = $choices[[int]$choice - 1].Value
break
} else {
Write-Host "Invalid choice. Please try again." -ForegroundColor Red
}
} while ($true)
{{else}}
# {{description}}
$Script:Config.{{name}} = Get-ValidatedInput -Prompt "{{prompt}}" -Validator {
param($value)
{{#if validation.pattern}}
if ($value -notmatch "{{validation.pattern}}") {
return "Value must match pattern: {{validation.pattern}}"
}
{{/if}}
{{#if validation.min}}
if ([int]$value -lt {{validation.min}}) {
return "Value must be at least {{validation.min}}"
}
{{/if}}
{{#if validation.max}}
if ([int]$value -gt {{validation.max}}) {
return "Value must be at most {{validation.max}}"
}
{{/if}}
return $true
} -Default "{{default}}"
{{/if}}
{{/each}}
Write-Log "Configuration collection completed" -Level Info
}
function Show-ConfigurationSummary {
Write-Host ""
Write-Host "╔══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║ Configuration Summary ║" -ForegroundColor Cyan
Write-Host "╠══════════════════════════════════════════════════════════════╣" -ForegroundColor Cyan
Write-Host "║ ║" -ForegroundColor Cyan
Write-Host "║ Application: $($Script:AppName) v$($Script:AppVersion)".PadRight(62) + "║" -ForegroundColor White
Write-Host "║ Installation Path: $($Script:InstallPath)".PadRight(62) + "║" -ForegroundColor White
{{#each configurable_parameters}}
Write-Host "║ {{display_name}}: $($Script:Config.{{name}})".PadRight(62) + "║" -ForegroundColor White
{{/each}}
Write-Host "║ ║" -ForegroundColor Cyan
Write-Host "╠══════════════════════════════════════════════════════════════╣" -ForegroundColor Cyan
Write-Host "║ ║" -ForegroundColor Cyan
Write-Host "║ The installer will now proceed with these settings. ║" -ForegroundColor Cyan
Write-Host "║ This process may take several minutes to complete. ║" -ForegroundColor Cyan
Write-Host "║ ║" -ForegroundColor Cyan
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
Write-Host ""
if ($Script:InteractiveMode) {
$confirm = Read-Host "Proceed with installation? [Y/n]"
if ($confirm -match '^[Nn]$') {
Write-Host "Installation cancelled by user." -ForegroundColor Yellow
Exit-Installation 0
}
}
}
# =============================================================================
# INSTALLATION FUNCTIONS
# =============================================================================
function Install-Dependencies {
if ($SkipDependencies) {
Write-Log "Skipping dependency installation" -Level Info
return
}
Write-Log "Installing dependencies..." -Level Info
# Check for package managers
$packageManagers = @()
if (Get-Command choco -ErrorAction SilentlyContinue) {
$packageManagers += "chocolatey"
}
if (Get-Command winget -ErrorAction SilentlyContinue) {
$packageManagers += "winget"
}
if ($packageManagers.Count -eq 0) {
Write-Log "Installing Chocolatey package manager..." -Level Info
if (-not $DryRun) {
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
}
$packageManagers += "chocolatey"
}
Write-Log "Available package managers: $($packageManagers -join ', ')" -Level Info
# Install dependencies
{{#each windows_dependencies}}
Write-Log "Installing {{name}}..." -Level Info
if (-not $DryRun) {
$installed = $false
foreach ($pm in $packageManagers) {
try {
switch ($pm) {
"chocolatey" {
choco install {{chocolatey_name}} -y
$installed = $true
break
}
"winget" {
winget install {{winget_id}}
$installed = $true
break
}
}
} catch {
Write-Log "Failed to install {{name}} using $pm" -Level Warning
continue
}
}
if (-not $installed) {
throw "Failed to install dependency: {{name}}"
}
# Verify installation
if (-not ({{verify_command}})) {
throw "Installation verification failed for {{name}}"
}
}
{{/each}}
Write-Log "Dependencies installed successfully" -Level Info
}
# =============================================================================
# CLEANUP AND ERROR HANDLING
# =============================================================================
function Remove-TempFiles {
if (Test-Path -Path $Script:TempDir) {
Write-Log "Cleaning up temporary directory: $($Script:TempDir)" -Level Debug
Remove-Item -Path $Script:TempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
function Exit-Installation {
[CmdletBinding()]
param(
[Parameter(Mandatory=$false)]
[int]$ExitCode = 0
)
Write-Log "Cleaning up and exiting..." -Level Info
Remove-TempFiles
if ($ExitCode -eq 0) {
Write-Log "Installation completed successfully!" -Level Info
$successMessage = @"
╔══════════════════════════════════════════════════════════════╗
║ Installation Complete! ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ {{app_name}} has been successfully installed. ║
║ ║
║ Service status: Get-Service {{service_name}} ║
║ Logs location: {{log_path}} ║
║ Configuration: {{config_path}} ║
║ ║
╚══════════════════════════════════════════════════════════════╝
"@
Write-Host $successMessage -ForegroundColor Green
} else {
Write-Log "Installation failed with exit code: $ExitCode" -Level Error
Write-Host "Check the log file for details: $($Script:LogFile)" -ForegroundColor Red
}
exit $ExitCode
}
# =============================================================================
# MAIN INSTALLATION WORKFLOW
# =============================================================================
function Start-Installation {
try {
# Create temporary directory
New-Item -Path $Script:TempDir -ItemType Directory -Force | Out-Null
# Start installation
Write-Log "Starting $($Script:AppName) installation..." -Level Info
Write-Log "Log file: $($Script:LogFile)" -Level Info
# Installation steps
Show-Welcome
Test-SystemRequirements
Get-Configuration
Show-ConfigurationSummary
# Main installation phases
$totalSteps = {{total_installation_steps}}
$currentStep = 0
{{#each installation_steps}}
$currentStep++
Show-Progress -Current $currentStep -Total $totalSteps -Activity "Installing $($Script:AppName)" -Status "{{description}}"
{{function_name}}
{{/each}}
# Final validation
$currentStep++
Show-Progress -Current $currentStep -Total $totalSteps -Activity "Installing $($Script:AppName)" -Status "Validating installation"
Test-Installation
# Success
Exit-Installation 0
} catch {
Write-Log "Installation failed: $($_.Exception.Message)" -Level Error
if ($Script:InteractiveMode) {
Write-Host ""
Write-Host "Installation failed. Would you like to:" -ForegroundColor Yellow
Write-Host "1) View the error log" -ForegroundColor White
Write-Host "2) Attempt to rollback changes" -ForegroundColor White
Write-Host "3) Exit" -ForegroundColor White
$choice = Read-Host "Choice [3]"
if ([string]::IsNullOrEmpty($choice)) { $choice = "3" }
switch ($choice) {
"1" { Get-Content $Script:LogFile | Out-Host -Paging }
"2" { Invoke-Rollback }
}
}
Exit-Installation 1
}
}
# Execute main function
Start-InstallationTemplate Features
Windows-Specific Capabilities
Advanced Error Handling
User Experience Features
Configuration Management
Last updated