Private/Utility/Install-NeededModule.ps1
|
function Install-NeededModule { <# .SYNOPSIS Installs and imports a PowerShell module if needed. .DESCRIPTION Checks if a module is loaded, and if not, prompts the user to import or install it. Supports both required and optional modules with different handling for each. For required modules (-Mandatory): - Displays error and exits if user declines or installation fails For optional modules (no -Mandatory): - Displays warning and continues if user declines or installation fails - Requires -WarningMessage parameter to describe missing functionality The -Force switch bypasses user prompts and automatically installs/imports the module. .PARAMETER Name The name of the module to check, install, and import. .PARAMETER Mandatory Indicates that the module is required for the application to function. If set, failures will result in errors and application exit. If not set, failures will result in warnings and continued execution. .PARAMETER WarningMessage Custom warning message to display when an optional module cannot be loaded. Should describe what functionality will be unavailable without the module. Recommended for optional modules to inform users of the impact. .PARAMETER Force Bypasses user prompts and automatically installs/imports the module. Useful for automation scenarios and non-interactive execution. .INPUTS None. This function does not accept pipeline input. .OUTPUTS System.Boolean Returns $true if the module was successfully loaded (or already loaded). Returns $false if an optional module failed to load. Exits the script if a required module fails to load. .EXAMPLE Install-NeededModule -Name 'PSCertutil' -Mandatory Prompts to install/import PSCertutil as a required module. Exits if user declines or it fails. .EXAMPLE Install-NeededModule -Name 'PwshSpectreConsole' -WarningMessage 'Interactive UI features will be disabled.' Prompts to install/import an optional module. Warns and continues if it fails. .EXAMPLE Install-NeededModule -Name 'PSWriteHTML' -Mandatory -Force Automatically installs/imports PSWriteHTML without prompting. Exits on failure. .EXAMPLE if (Install-NeededModule -Name 'SpecialModule' -WarningMessage 'Feature X unavailable.') { # Module loaded successfully, use feature X Use-FeatureX } Checks return value to conditionally execute code that requires the module. .NOTES The function will skip all actions if the module is already loaded. Modules are installed to CurrentUser scope with -Force to avoid additional prompts. Required modules use Write-Error and exit on failure. Optional modules use Write-Warning and return $false on failure. The WarningMessage parameter is only used for optional modules (when -Mandatory is not specified). It is ignored for mandatory modules since errors are displayed instead of warnings. #> [CmdletBinding()] [OutputType([bool])] param ( [Parameter(Mandatory)] [string]$Name, [switch]$Mandatory, [string]$WarningMessage, [switch]$Force ) #requires -Version 5.1 # Skip if already loaded if (Test-IsModuleLoaded -Name $Name) { Write-Verbose "Module '$Name' is already loaded." return $true } $isAvailable = Test-IsModuleAvailable -Name $Name # Prompt user with appropriate message (unless Force is specified) if (-not $Force) { $question = if ($isAvailable) { "Module $Name is available for importation. Do you want to import it?" } else { if ($Mandatory) { "Module $Name is required. Do you want to install it?" } else { "Module $Name is optional. Do you want to install it?" } } $Choice = Read-Choice -Question $question } else { $Choice = 'y' } # Handle user decline based on Mandatory flag if ($Choice -ne 'y') { $action = if ($isAvailable) { 'imported' } else { 'installed' } if ($Mandatory) { $errorRecord = [System.Management.Automation.ErrorRecord]::new( [System.Exception]::new("Module '$Name' is required but was not $action."), 'ModuleRequired', [System.Management.Automation.ErrorCategory]::NotInstalled, $Name ) $PSCmdlet.ThrowTerminatingError($errorRecord) } else { if ($WarningMessage) { Write-Warning $WarningMessage } else { Write-Warning "Module '$Name' was not $action. Some features may be unavailable." } return $false } } # Install and/or import try { if (-not $isAvailable) { Write-Verbose "Installing module: $Name" Install-Module -Name $Name -Scope CurrentUser -Force -ErrorAction Stop } Import-Module -Global -Name $Name -ErrorAction Stop Write-Verbose "Successfully imported module: $Name" return $true } catch { $verb = if ($isAvailable) { 'import' } else { 'install or import' } if ($Mandatory) { $errorRecord = [System.Management.Automation.ErrorRecord]::new( $_.Exception, 'ModuleInstallationFailed', [System.Management.Automation.ErrorCategory]::NotInstalled, $Name ) $PSCmdlet.ThrowTerminatingError($errorRecord) } else { if ($WarningMessage) { Write-Warning $WarningMessage } else { Write-Warning "Failed to $verb module '$Name': $_" } return $false } } } |