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
        }
    }
}