Public/Import-OriAzBopPsModule.ps1

<#
.SYNOPSIS
    Install PowerShell module from online gallery.
    It returns dependency tree
.DESCRIPTION
    This cmdlet will try to install PowerShell module from online gallery.
    It does the following:
    1. Checks if required module is already loaded with a required (or higher) version.
    2. If not, tryes to import required version from installed modules.
    3. If the loaded module version is still lower or missing (it is not installed) it tries to install it
    4. Checks for prerequisities like PS version, PackageManager version etc
    5. Installs necessary tools if needed
    6. Registers repository if not yet
    7. Installs the module
    8. Loads required version of the module
    It returns dependency tree
 
.PARAMETER DevOpsAccount
    The name of the dev ops account. Default value is 'oriflame'
 
.PARAMETER RegisterPsRepoFeedList
    Powershell Repository feed to register if needed
     
.PARAMETER RegisterNugetRepoFeedList
    Nuget Repository feed to register if needed
 
.PARAMETER Name
    Specifies the exact names of modules to install from the online gallery. The module name must match the module name in the repository.
 
.PARAMETER Repository
    Repository feed to register if needed for getting powershell modules.
     
#####
 
.PARAMETER Guid
    Exact GUID of module.
 
.PARAMETER MaximumVersion
    Maximum module version
 
.PARAMETER RequiredVersion
    Exact required module version. When is not set the lastet version will be installed and loaded.
     
.PARAMETER Version
    Most likely minimum module versionloaded.
     
.PARAMETER Credential
    Repository Credential if needed
 
.PARAMETER SkipImport
    When is set Import-Module will be skipped
 
.PARAMETER Prefix
    When is set the Prefix is used while the Import-Module function as switch parameter
 
.PARAMETER RegisterViaPSRepository
    When is set register via Register-PSRepository, otherwise use Register-PackageSource.
 
.PARAMETER SkipInstallCredProvider
    When is set Installation of Credential provider will be skipped.
 
.PARAMETER SleepInSec
    Sleep time in sec between test if the module is ready to import.
 
.PARAMETER MaxRetry
    Max retry while waiting to import module
 
.EXAMPLE
$password = ConvertTo-SecureString 'xbchuuuuhaaaatest' -AsPlainText -Force
$RepositoryCredential = New-Object System.Management.Automation.PSCredential 'feafeafae@mydomain.net',$password
 
Import-OriAzBopPsModule `
-Name OriAzEncEnvironmentConfiguration `
-RequiredVersion 1.0.48 `
-Credential $RepositoryCredential
#>

function Import-OriAzBopPsModule {

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = "There's required use the recomanded code.")]
    [CmdLetBinding()]
    [OutputType([PSCustomObject])]
    param (
        [Parameter(Mandatory = $false, HelpMessage = "The name of the dev ops account")]
        [String] $DevOpsAccount = $Script:VstsAccount,

        [Parameter(Mandatory = $true, HelpMessage = "Exact name of the module")]
        [String] $Name,

        [Parameter(Mandatory = $false, HelpMessage = "Powershell Repository feed to register if needed")]
        [String[]] $RegisterPsRepoFeedList = @('PackageManagementFeed'),

        [Parameter(Mandatory = $false, HelpMessage = "Nuget Repository feed to register if needed")]
        [String[]] $RegisterNugetRepoFeedList = @('DeploymentPackages'),

        [Parameter(Mandatory = $false, HelpMessage = "Repository feed to register if needed for getting powershell modules")]
        [String] $Repository = 'PackageManagementFeed',

        [Parameter(Mandatory = $false, HelpMessage = "GUID of the module")]
        [string] $Guid,

        [Parameter(Mandatory = $false, HelpMessage = "Maximum module version")]
        [Version] $MaximumVersion,

        [Parameter(Mandatory = $false, HelpMessage = "Required module version")]
        [Version] $RequiredVersion,

        [Alias("Version")]
        [Parameter(Mandatory = $false, HelpMessage = "Most likely minimum module version")]
        [Version] $MinimumVersion,

        [Parameter(Mandatory = $false, HelpMessage = "Repository Credential if needed")]
        [PSCredential] $Credential = $null,

        [Parameter(Mandatory = $false, HelpMessage = "When is set import-module will be skipped.")]
        [switch] $SkipImport,

        [Parameter(Mandatory = $False, HelpMessage = "When is set the Prefix is used while the Import-Module function as switch parameter")]
        [String] $Prefix,

        [Parameter(Mandatory = $false, HelpMessage = "When is set register via Register-PSRepository, otherwise use Register-PackageSource.")]
        [switch] $RegisterViaPSRepository,

        [Parameter(Mandatory = $false, HelpMessage = "When is set Installation of Credential provider will be skipped.")]
        [switch] $SkipInstallCredProvider,

        [Parameter(Mandatory = $False, HelpMessage = "Sleep time in sec between test if the module is ready to import.")]
        [int] $SleepInSec = 10,

        [Parameter(Mandatory = $False, HelpMessage = "Max retry")]
        [int] $MaxRetry = 20
    )
    $ErrorActionPreference = 'Stop'
    Write-Verbose "-- Import-OriAzBopPsModule --"
    Write-Verbose "Name: $Name"
    Write-Verbose "Guid: $Guid"
    Write-Verbose "MaximumVersion: $MaximumVersion"
    Write-Verbose "RequiredVersion: $RequiredVersion"
    Write-Verbose "Credential: $(ConvertTo-Json $Credential)"
    Write-Verbose "SkipImport: $($SkipImport.IsPresent)"
    Write-Verbose "SkipInstallCredProvider: $($SkipInstallCredProvider.IsPresent)"
    Write-Verbose "Prefix: $Prefix"
    Write-Verbose "SleepInSec: $SleepInSec"
    Write-Verbose "MaxRetry: $MaxRetry"

    # Required module is already imported
    if (-not($SkipImport.IsPresent) -and (Test-GetModule -Name $Name -RequiredVersion $RequiredVersion)) {
        Write-Verbose "Module $Name is already imported."
        return
    }

    # Fix credential provider
    if (-not($SkipInstallCredProvider.IsPresent)) {
        Invoke-OriAzExrExceptionRetry `
            -ListedExceptions @('*') `
            -MaxRetry 60 `
            -SleepTimeInSec 60 `
            -ScriptBlockToRun {
            Invoke-WebRequest -Uri "https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1" -UseBasicParsing | Invoke-Expression 6> $null
        }
    }

    
    Install-OriAzBopPrerequisity -Name PowerShellGet -MinimumVersion 2.2.5 -SkipImport:$SkipImport -AllowClobber -SkipPublisherCheck
    Install-OriAzBopPrerequisity -Name PackageManagement -MinimumVersion 1.4.7 -SkipImport:$SkipImport -AllowClobber -SkipPublisherCheck

    # Required module needs to be installed and imported
    Register-OriAzBopRepository `
        -DevOpsAccount $DevOpsAccount `
        -RepositoryCredential $Credential `
        -PsProjectName $RegisterPsRepoFeedList `
        -NugetProjectName $RegisterNugetRepoFeedList `
        -RegisterViaPSRepository:$RegisterViaPSRepository `
        -SkipPrompt:$true `
        -Verbose:$VerbosePreference `
        -Debug:$DebugPreference

    [PSCustomObject] $DependencyMap = Invoke-ModuleInstall `
        -Name $Name `
        -Guid $Guid `
        -MaximumVersion $MaximumVersion `
        -RequiredVersion $RequiredVersion `
        -Version $MinimumVersion `
        -Credential $Credential `
        -Repository $Repository `
        -SkipImport:$SkipImport `
        -Verbose:$VerbosePreference `
        -Debug:$DebugPreference
        
    Write-Verbose "Re-Import Module InstalledLocation: $($DependencyMap.InstalledLocation)"
    # Note: Following import does NOT work.
    # Import-Module -Name $Name -RequiredVersion $RequiredVersion -Verbose
    # Any using of -RequiredVersion skip execution of init.ps1
    # This problem we're bpassing via using Import-Module on installed path of module.
    if ($SkipImport.IsPresent) {
        Write-Debug "Skip of Import-Module"
    }
    else {
        Wait-OriAzBopModuleComplete `
            -Path $DependencyMap.InstalledLocation `
            -SleepInSec $SleepInSec `
            -MaxRetry $MaxRetry `
            -Verbose:$VerbosePreference `
            -Debug:$DebugPreference

        $ImportParameter = @{ Name = $DependencyMap.InstalledLocation }

        if (![string]::IsNullOrEmpty($Prefix)) {
            $ImportParameter += @{ Prefix = $Prefix }
        }

        # remove -Force while import-module (it can be already loaded via depnedency on the same module.)
        Import-Module @ImportParameter `
            -Verbose:$VerbosePreference `
            -Debug:$DebugPreference 
    }

    Write-Verbose "-- End of Import-OriAzBopPsModule --"
    return $DependencyMap
}