internal/functions/Test-Module.ps1

function Test-Module
{
<#
    .SYNOPSIS
        Tests for module existence.
     
    .DESCRIPTION
        Tests whether a module - or set of modules - exists on the target machine(s).
        Includes support for version requirements (minimum or maximum).
     
    .PARAMETER Name
        Name of the module(s) to search for.
     
    .PARAMETER Version
        The version constraint.
        Whether that is the minimum, maximum or exactly this version is governed by the -Test parameter.
        The same version constraint will be applied to all modules specified!
        For custom versions per module, please use the -Module parameter to specify a hashtable with the mapping.
     
    .PARAMETER Module
        The combination of modules and versions to test.
        Specify the modulename as key and the version as value.
        E.g.: @{ MailDaemon = '1.0.0' }
        Specify '0.0' in order to not test about any specific version.
     
    .PARAMETER Test
        How to test for version.
        By default, the test will search for 'GreaterEqual' (that is: At least the specified version).
        Supported scenarios: 'LesserThan', 'LesserEqual', 'Equal', 'GreaterEqual', 'GreaterThan'
        Note on Lesser* comparisons: This only tests whether a version below the limit is present. It does not Test that NO greater version is available!
     
    .PARAMETER Quiet
        Disables output objects and instead returns $true if all modules specified meet the requirements, $false if not so.
     
    .PARAMETER ComputerName
        The computers on which to test.
        Uses WinRM / PowerShell Remoting to perform test.
     
    .PARAMETER Credential
        The credentials to use for connecting to computers for the test.
        Will be ignored for localhost.
     
    .EXAMPLE
        PS C:\> Test-Module -Name 'MyModule'
     
        Tests whether the module MyModule is available in any version.
     
    .EXAMPLE
        PS C:\> Test-Module -Name MailDaemon -Version 1.1.0 -ComputerName 'server1', 'Server2'
     
        Tests whether the module MailDaemon is available in at least version 1.1.0 on the computers server1 and server2.
     
    .EXAMPLE
        PS C:\> Test-Module -Name PSFramework -Version 1.0.0 -Quiet -Test 'Equal'
     
        Returns $true if the module PSFramework exists locally in exactly version 1.0.0, $false otherwise.
     
    .EXAMPLE
        PS C:\> Test-Module -Module @{ PSFramework = '1.0.0'; MailDaemon = '1.1.0' } -Test 'LesserThan'
     
        Returns whether PSFramework is present in any version less than 1.0.0
        Returns whether MailDaemon is present in any version less than 1.1.0
#>

    [CmdletBinding(DefaultParameterSetName = 'Name')]
    param (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'Name')]
        [string[]]
        $Name,
        
        [Parameter(Position = 1, ParameterSetName = 'Name')]
        [version]
        $Version = '0.0.0.0',
        
        [Parameter(Mandatory = $true, ParameterSetName = 'Hash')]
        [hashtable]
        $Module = @{ },
        
        [ValidateSet('LesserThan', 'LesserEqual', 'Equal', 'GreaterEqual', 'GreaterThan')]
        [string]
        $Test = 'GreaterEqual',
        
        [switch]
        $Quiet,
        
        [Parameter(ValueFromPipeline = $true)]
        [PSFComputer[]]
        $ComputerName = $env:COMPUTERNAME,
        
        [AllowNull()]
        [PSCredential]
        $Credential
    )
    
    begin
    {
        #region Prepare Module parameter
        $moduleHash = $Module
        foreach ($moduleName in $Name)
        {
            $moduleHash[$moduleName] = $Version
        }
        foreach ($key in ([string[]]$moduleHash.Keys))
        {
            $moduleHash[$key] = $moduleHash[$key] -as [Version]
            if (-not $moduleHash[$key]) { $moduleHash[$key] = ([Version]'0.0.0.0') }
        }
        #endregion Prepare Module parameter
        
        #region Validation Scriptblock
        $scriptBlock = {
            param (
                [hashtable]
                $ModuleHash,
                
                [string]
                $Test,
                
                [bool]
                $Quiet
            )
            
            #region Utility Functions
            function Write-Result
            {
                [CmdletBinding()]
                param (
                    [string]
                    $Name,
                    
                    $Success,
                    
                    [AllowNull()]
                    [AllowEmptyCollection()]
                    $VersionsFound,
                    
                    [string]
                    $Test
                )
                
                $result = [bool]$Success
                
                [PSCustomObject]@{
                    Name          = $Name
                    Success          = $result
                    VersionsFound = $VersionsFound
                    ComputerName  = $env:COMPUTERNAME
                    Test          = $Test
                }
            }
            #endregion Utility Functions
            
            #region Validate each module specified
            foreach ($module in $ModuleHash.Keys)
            {
                $modulesFound = Get-Module -Name $module -ListAvailable
                if ($Quiet -and (-not $modulesFound)) { return $false }
                
                if ($ModuleHash[$module] -le '0.0.0.0')
                {
                    Write-Result -Name $module -Success $modulesFound -VersionsFound $modulesFound.Version -Test $Test
                    continue
                }
                
                #region Quiet Validation [Calls Continue]
                if ($Quiet)
                {
                    switch ($Test)
                    {
                        'LesserThan' { if (-not ($modulesFound | Where-Object Version -LT $ModuleHash[$module])) { return $false } }
                        'LesserEqual' { if (-not ($modulesFound | Where-Object Version -LE $ModuleHash[$module])) { return $false } }
                        'Equal' { if (-not ($modulesFound | Where-Object Version -EQ $ModuleHash[$module])) { return $false } }
                        'GreaterEqual' { if (-not ($modulesFound | Where-Object Version -GE $ModuleHash[$module])) { return $false } }
                        'GreaterThan' { if (-not ($modulesFound | Where-Object Version -GT $ModuleHash[$module])) { return $false } }
                    }
                    continue
                }
                #endregion Quiet Validation [Calls Continue]
                
                switch ($Test)
                {
                    'LesserThan' { Write-Result -Name $module -Success ($modulesFound | Where-Object Version -LT $ModuleHash[$module]) -VersionsFound $modulesFound.Version -Test $Test }
                    'LesserEqual' { Write-Result -Name $module -Success ($modulesFound | Where-Object Version -LE $ModuleHash[$module]) -VersionsFound $modulesFound.Version -Test $Test }
                    'Equal' { Write-Result -Name $module -Success ($modulesFound | Where-Object Version -EQ $ModuleHash[$module]) -VersionsFound $modulesFound.Version -Test $Test }
                    'GreaterEqual' { Write-Result -Name $module -Success ($modulesFound | Where-Object Version -GE $ModuleHash[$module]) -VersionsFound $modulesFound.Version -Test $Test }
                    'GreaterThan' { Write-Result -Name $module -Success ($modulesFound | Where-Object Version -GT $ModuleHash[$module]) -VersionsFound $modulesFound.Version -Test $Test }
                }
            }
            #endregion Validate each module specified
            
            if ($Quiet) { return $true }
        }
        #endregion Validation Scriptblock
    }
    process
    {
        Invoke-PSFCommand -ComputerName $ComputerName -Credential $Credential -ScriptBlock $scriptBlock -ArgumentList $moduleHash, $Test, $Quiet.ToBool() -HideComputerName
    }
}