PSRequiredModules.psm1

<#
    .SYNOPSIS
        Get-RequiredModules
 
    .DESCRIPTION
        Read PSRequiredModules data from PowerShell module manifest.
 
    .PARAMETER ModuleFilePath
        Module file path expect a module file ending with .psd1 or .psm1 (to retrieve automatically associated .psd1)
 
    .EXAMPLE
        PS C:\> Get-RequiredModules -ModuleFilePath 'C:\MyModule\MyModule.psd1'
 
    .INPUTS
        System.String
 
    .OUTPUTS
        System.Boolean
 
    .NOTES
        Author: JDMSFT
        Date: 17/03/21
#>

Function Get-RequiredModules
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$ModuleFilePath
    )

    Try
    {
        If ($ModuleFilePath -like '*.psm1' -or $ModuleFilePath -like '*.psd1')
        {
            $ManifestData = Import-LocalizedData -BaseDirectory (Split-Path $ModuleFilePath) -FileName (Split-Path $ModuleFilePath -Leaf)

            $ManifestData.PrivateData.PSRequiredModules
        }
        Else
        {
            Write-Warning "Please specify a valid module file path (ending with .psm1/.psd1)"
        }
    }
    Catch { Write-Output "[ERROR] $($_)`n[ERROR] [$($_.InvocationInfo.ScriptLineNumber)] $($_.InvocationInfo.ScriptName) >> $($_.InvocationInfo.Line.TrimStart())" }
}

<#
    .SYNOPSIS
        Import-RequiredModules
 
    .DESCRIPTION
        Read PSRequiredModules data from PowerShell module manifest.
 
    .PARAMETER ModuleFilePath
        Module file path expect a module file ending with .psd1 or .psm1 (to retrieve automatically associated .psd1)
 
    .PARAMETER AutoInstall
        Automatically install missing module from PowerShell environments
 
    .PARAMETER Details
        Return full state details isntead returning a boolean (usefull to troubleshoot)
 
    .EXAMPLE
        PS C:\> Import-RequiredModules -ModuleFilePath 'C:\MyModule\MyModule.psd1'
         
        Return if all required modules are imported in the PowerShell session.
 
    .EXAMPLE
        PS C:\> Import-RequiredModules -ModuleFilePath 'C:\MyModule\MyModule.psd1' -AutoInstall
         
        Install missing modules and return if all required modules are imported in the PowerShell session.
 
    .EXAMPLE
        PS C:\> Import-RequiredModules -ModuleFilePath 'C:\MyModule\MyModule.psd1' -Details
         
        Return all required modules states for the current PowerShell session instead returnin a boolean.
 
    .INPUTS
        System.String
 
    .OUTPUTS
        System.Boolean,Hastable
 
    .NOTES
        Author: JDMSFT
        Date: 17/03/21
#>

Function Import-RequiredModules
{
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $true)] 
        [string]$ModuleFilePath,
        [switch]$AutoInstall,
        [switch]$Details
    )

    Try 
    {
        If ($ModuleFilePath -like '*.psm1' -or $ModuleFilePath -like '*.psd1')
        {
            $ModuleObject = @()
            $ManifestData = Import-LocalizedData -BaseDirectory (Split-Path $ModuleFilePath) -FileName (Split-Path $ModuleFilePath -Leaf)

            $ManifestData.PrivateData.PSRequiredModules.Keys | ForEach {

                $IsImported = $false
                $IsInstalled = $false
                $IsDownloadable = $null
                $Action = $null
                $State = $null

                # Get required version / prerelease
                $RequiredModuleName = $_
                $RequiredModuleVersionSemVer = $ManifestData.PrivateData.PSRequiredModules.$_
                If ($RequiredModuleVersionSemVer -like '*-*') 
                { 
                    $PreReleaseVersion = $true
                    $RequiredModuleVersion = ($RequiredModuleVersionSemVer -split '-')[0]
                    $RequiredModulePrerelease = ($RequiredModuleVersionSemVer -split '-')[1]
                } 
                Else 
                { 
                    $PreReleaseVersion = $false 
                    $RequiredModuleVersion = $RequiredModuleVersionSemVer
                }

                # Checking for imported module version ...
                $MatchVersion = $false
                $RequiredModuleImportedMatches = Get-Module $RequiredModuleName
                If ($PreReleaseVersion)
                {
                    Write-Verbose "Checking for imported module $RequiredModuleName v$RequiredModuleVersionSemVer... (prerelease)"
                    $RequiredModuleImportedMatches | ForEach { 
                        If ($_.Version -eq $RequiredModuleVersion -and $_.PrivateData.PSData.Prerelease -eq $RequiredModulePrerelease) 
                        { 
                            Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer already imported !"
                            $MatchVersion = $true
                            $RequiredModule = $_ 
                            $IsImported = $true
                            $IsInstalled = $true
                            $State = 'OK'
                        } 
                    }
                }
                Else
                {
                    Write-Verbose "Checking for imported module $RequiredModuleName v$RequiredModuleVersionSemVer... (not prerelease)"
                    $RequiredModuleImportedMatches | ForEach { 
                        If ($_.Version -eq $RequiredModuleVersion) 
                        { 
                            Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer already imported !"
                            $MatchVersion = $true
                            $RequiredModule = $_ 
                            $IsImported = $true
                            $IsInstalled = $true
                            $State = 'OK'
                        } 
                    } 
                }

                # Checking for installed module version ...
                $RequiredModuleInstalledMatches = Get-Module $RequiredModuleName -ListAvailable
                If ($MatchVersion -ne $true) 
                {
                    If ($PreReleaseVersion)
                    {
                        Write-Verbose "Checking for installed module $RequiredModuleName v$RequiredModuleVersionSemVer... (prerelease)"
                        $RequiredModuleInstalledMatches | ForEach { 
                            If ($_.Version -eq $RequiredModuleVersion -and $_.PrivateData.PSData.Prerelease -eq $RequiredModulePrerelease) 
                            { 
                                Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer already installed !"
                                $MatchVersion = $true
                                $RequiredModule = $_ 
                                $IsInstalled = $true
                            } 
                        }
                    }
                    Else
                    {
                        Write-Verbose "Checking for installed module $RequiredModuleName v$RequiredModuleVersionSemVer... (not prerelease)"
                        $RequiredModuleInstalledMatches | ForEach { 
                            If ($_.Version -eq $RequiredModuleVersion) 
                            { 
                                Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer already installed !"
                                $MatchVersion = $true
                                $RequiredModule = $_ 
                                $IsInstalled = $true
                            } 
                        } 
                    }

                    # Module installed, importing ...
                    If ($MatchVersion -eq $true) 
                    {
                        Write-Verbose "Importing module $RequiredModuleName v$RequiredModuleVersionSemVer..."
                        Import-Module $RequiredModule -Global
                        $IsImported = $true
                        $Action = 'AutoImport'
                        If (Get-Module $RequiredModuleName) { $State = 'OK' } Else { $State = 'KO' }

                    }
                    # Module not installed, installing and importing ...
                    Else 
                    {
                        Write-Verbose "Module $RequiredModuleName v$RequiredModuleVersionSemVer not found."

                        If (Find-Module -Name $RequiredModuleName -RequiredVersion $RequiredModuleVersionSemVer -Repository PSGallery -AllowPrerelease -ea SilentlyContinue) { $IsDownloadable = $true } Else { $IsDownloadable = $false }

                        If ($AutoInstall)
                        {
                            If ($IsDownloadable)
                            {
                                $Action = 'AutoInstall'
                                Install-Module -Name $RequiredModuleName -RequiredVersion $RequiredModuleVersionSemVer -Repository PSGallery -AllowPrerelease

                                If (Get-InstalledModule -Name $RequiredModuleName -RequiredVersion $RequiredModuleVersionSemVer -AllowPrerelease -ea SilentlyContinue) 
                                { 
                                    $IsInstalled = $true
                                    Import-Module -Name $RequiredModuleName -RequiredVersion $RequiredModuleVersion -Global
                                } 
                            }
                            Else { Write-Verbose "$RequiredModuleName can't be downloaded." }
                        
                            If (Get-Module $RequiredModuleName) { $State = 'OK' } Else { $State = 'KO' }
                        }
                        Else { Write-Verbose "Auto-install module disabled. Please installl required module manually prior using this module : Isntall-Module -Name $ModuleName -RequiredVersion $RequiredModuleVersionSemVer" ; $State = 'KO' }
                    } 
                }
                Else { Write-Verbose "" }

                $ModuleObject += [PSCustomObject]@{Name = $RequiredModuleName; Version = $RequiredModuleVersionSemVer; IsPrerelease = $PreReleaseVersion ; IsImported = $IsImported ; IsInstalled = $IsInstalled ; IsDownloadable = $IsDownloadable ; Action = $Action ; State = $State }
            }

            If ($Details) { $ModuleObject } Else { $ModuleObject.State -notcontains 'KO' }
        }
        Else
        {
            Write-Warning "Please specify a valid module file path (ending with .psm1/.psd1)"
        }
    }
    Catch { Write-Output "[ERROR] $($_)`n[ERROR] [$($_.InvocationInfo.ScriptLineNumber)] $($_.InvocationInfo.ScriptName) >> $($_.InvocationInfo.Line.TrimStart())" }
}