PowerShell Installer Template
Overview
The PowerShell Installer Template provides a comprehensive foundation for creating robust, interactive installation scripts for Windows systems. This template includes error handling, progress tracking, user interaction, and Windows service management.
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
PowerShell 5.1+ Compatibility: Leverages modern PowerShell features
Administrator Privilege Checking: Ensures proper permissions
Windows Service Management: Native Windows service integration
Package Manager Support: Chocolatey and WinGet integration
Registry Management: Windows registry configuration support
Advanced Error Handling
Comprehensive Try-Catch: Proper PowerShell error handling
Interactive Recovery: User-guided error recovery options
Detailed Logging: Structured logging with multiple levels
Rollback Capabilities: Automatic cleanup on failure
User Experience Features
Rich Console Output: Colored output with progress indicators
Interactive Menus: User-friendly configuration collection
Input Validation: Robust parameter validation
Help System: Built-in help and guidance
Configuration Management
Dynamic Parameter Collection: Based on manifest configuration
Type-Safe Validation: PowerShell-native type checking
Default Value Handling: Intelligent default value management
Configuration Persistence: Save and restore configuration settings
This PowerShell template provides enterprise-grade installation capabilities specifically designed for Windows environments while maintaining the same high standards of user experience and reliability as the Bash template.
Last updated