# <copyright file="Assert-Module.ps1" company="Endjin Limited">
# Copyright (c) Endjin Limited. All rights reserved.
# </copyright>

Ensures that the specfied module is available.

Checks the presence of the specfied module version, installing it if necessary.

The module name.

The module version.

.PARAMETER AdditionalArgs
Any additional 'Install-Module' arguments required by the module (e.g. -AcceptLicense)

Suppresses the auto-install behaviour

Sets the installation scope of the module.

Sets the PowerShell repository used as the source (e.g. PSGallery)

Returns the 'PSModuleInfo' object for the asserted module - this can be used by the caller to easily import the module.


function Assert-Module
    param (
        [string] $Name,

        [string] $Version = "Latest",

        [hashtable] $AdditionalArgs,

        [switch] $DoNotInstall,

        [string] $Scope = "CurrentUser",

        [string] $PSRepository = "PSGallery"

    $existingLoaded = Get-Module $Name
    $existingInstalled = Get-Module -ListAvailable $Name

    # TODO: a version unconstrained check

    if ($null -ne $existingLoaded -and $existingLoaded.Version -eq $Version) {
        return $existingLoaded
    elseif ($existingLoaded) {
        Write-Verbose ("Unloading incorrect version of module {0} - (actual={1}, required={2})" -f $Name, $existingLoaded.Version, $Version)
        $existingLoaded | Remove-Module -Verbose:$false

    if ($DoNotInstall) {
        throw "The required module {0} v{1} is not available and was not installed due to DoNotInstall=true"

    if ($null -eq $existingInstalled -or ($Version -notin $existingInstalled.Version)) {
        Write-Verbose ("Installing required module: {0} v{1}" -f $Name, $Version)
        $installArgs= @{
            Name = $Name
            Scope = $Scope
            Force = $true
            RequiredVersion = $Version
            Repository = $PSRepository
        if ($AdditionalArgs) {
            $installArgs += $AdditionalArgs
        Install-Module @installArgs

    $assertedModule = Get-Module -ListAvailable $Name | Where-Object { $_.Version -eq $Version }
    if (!$assertedModule) {
        throw "The module {0} v{1} was unexpectedly not available"
    return $assertedModule