Install-DTXModule.ps1

<#PSScriptInfo

.VERSION 0.0.1

.GUID 2fdc79e7-5369-49b4-9adf-29629023bfd1

.AUTHOR DTX Platform Team

.COMPANYNAME

.COPYRIGHT

.TAGS

.LICENSEURI

.PROJECTURI

.ICONURI

.EXTERNALMODULEDEPENDENCIES

.REQUIREDSCRIPTS

.EXTERNALSCRIPTDEPENDENCIES

.RELEASENOTES

.PRIVATEDATA

#>


<#

.DESCRIPTION
    
        Installs a PowerShell module from S3.

#>


param(
    [Parameter(Mandatory = $true)]
    [string]$Name,

    [Parameter(Mandatory = $true)]
    [string]$Version,

    [Parameter(Mandatory = $true)]
    [string]$BucketName,

    [Parameter(Mandatory = $true)]
    [string]$BucketRegion,

    [Parameter(Mandatory = $true)]
    [string]$ObjectKey,

    [switch]$SkipTestImport
)

Begin {
    function Install-ModuleForWindows {
        param (
            [string]$Name,
            [string]$Version,
            [string]$BucketName,
            [string]$BucketRegion,
            [string]$ObjectKey
        )

        # The command is ran inside another PowerShell core process to avoid issues with the current process
        # More specifically the AWS PowerShell module needed to install the module might not be compatible
        # with the requirements of the module we are trying to install.
        pwsh -Command {
            try {
                $WarningPreference = "SilentlyContinue"
                $ProgressPreference = "SilentlyContinue"

                # Access the passed in arguments
                $receivedBucketName = $args[0]
                $receivedBucketRegion = $args[1]
                $receivedObjectKey = $args[2]
                $receivedModuleName = $args[3]
                $receivedModuleVersion = $args[4]

                # Check if the module is already installed
                if (Get-Module -Name $receivedModuleName -ListAvailable) {
                    # Check if the candidate version is already installed
                    if (Get-Module -Name $receivedModuleName -ListAvailable | Where-Object { $_.Version -eq $receivedModuleVersion }) {
                        # If the module version is already installed, skip the installation
                        Write-Host "Module $($receivedModuleName) version $($receivedModuleVersion) is already installed."
                        Write-Host "No action required."
                        exit 0
                    }
                }

                Write-Host "Installing $($receivedModuleName) version $($receivedModuleVersion)..."

                # Import the pre-requisites
                $preReqModule = "AWSPowerShell.NetCore"
                if (-not (Get-Module -Name $preReqModule -ListAvailable)) {
                    Install-Module -Name $preReqModule -Force
                }
                Import-Module -Name $preReqModule -Force

                # Download the module from S3
                Write-Host "Downloading $($receivedModuleName) version $($receivedModuleVersion) from S3 object $($receivedObjectKey) in bucket $($receivedBucketName)."
                $tempFile = Read-S3Object -BucketName $receivedBucketName -Region $receivedBucketRegion -Key $receivedObjectKey -File ([System.IO.Path]::GetTempFileName())

                $tempPath = [System.IO.Path]::GetTempPath()
                Expand-Archive -Path $tempFile.FullName -DestinationPath $tempPath -Force

                # Get the path to the module version
                $tempModuleVersionPath = Join-Path -Path $tempPath -ChildPath $receivedModuleName
                $newTempModuleVersionPath = Join-Path -Path $tempPath -ChildPath $receivedModuleVersion

                # Rename the folder to the module version so dtx.cloud.management > 0.6.1
                if (Test-Path -Path $newTempModuleVersionPath) {
                    Remove-Item -Path $newTempModuleVersionPath -Recurse -Force
                }
                Rename-Item -Path $tempModuleVersionPath -NewName $newTempModuleVersionPath -Force

                $modulePath = Join-Path -Path $env:ProgramFiles -ChildPath "PowerShell" -AdditionalChildPath "Modules", $receivedModuleName
                $modulePathInclVersion = Join-Path -Path $modulePath -ChildPath $receivedModuleVersion

                # Check if the module exist
                if (Test-Path -Path $modulePath) {
                    # Check if the module version is already installed
                    Write-Host "Checking if $($modulePathInclVersion) exists..."
                    if (Test-Path -Path $modulePathInclVersion) {
                        # If we reached this point, we do not expect the version dir for this module to exist.
                        # We need to remove it before proceeding
                        Remove-Item -Path $modulePathInclVersion -Recurse -Force
                    }
                }
                else {
                    # If the module doesn't exist, create its path
                    Write-Host "Creating $($modulePath)..."
                    [void]$(New-Item -ItemType Directory -Path $modulePath -Force)
                }

                # Move the module to the modules folder
                Write-Host "Installing $($receivedModuleName) version $($receivedModuleVersion)..."
                Move-Item -Path $newTempModuleVersionPath -Destination $modulePath -Force

                Write-Host "Module installed successfully."
            }
            catch {
                Write-Error $_.Exception.Message
                exit 1 
            }
        } -Args $BucketName, $BucketRegion, $ObjectKey, $Name, $Version

        if ($LASTEXITCODE -ne 0) {
            Write-Error $_.Exception.Message
            throw "Failed to install module."
        }
    }
    function Install-ModuleForNonWindows {
        param (
            [string]$Name,
            [string]$Version,
            [string]$BucketName,
            [string]$BucketRegion,
            [string]$ObjectKey
        )

        # The command is ran inside another PowerShell core process to avoid issues with the current process
        # More specifically the AWS PowerShell module needed to install the module might not be compatible
        # with the requirements of the module we are trying to install.
        try {
            $WarningPreference = "SilentlyContinue"
            $ProgressPreference = "SilentlyContinue"

            # Check if the module is already installed
            if (Get-Module -Name $Name -ListAvailable) {
                # Check if the candidate version is already installed
                if (Get-Module -Name $Name -ListAvailable | Where-Object { $_.Version -eq $Version }) {
                    # If the module version is already installed, skip the installation
                    Write-Host "Module $($Name) version $($Version) is already installed."
                    Write-Host "No action required."
                    exit 0
                }
            }

            Write-Host "Installing $($Name) version $($Version)..."

            # Import the pre-requisites
            $preReqModule = "AWSPowerShell.NetCore"
            if (-not (Get-Module -Name $preReqModule -ListAvailable)) {
                Install-Module -Name $preReqModule -Force
            }
            Import-Module -Name $preReqModule -Force

            # Download the module from S3
            Write-Host "Downloading $($Name) version $($Version) from S3 object $($ObjectKey) in bucket $($BucketName)."
            $tempFile = Read-S3Object -BucketName $BucketName -Region $BucketRegion -Key $ObjectKey -File ([System.IO.Path]::GetTempFileName())

            $tempPath = [System.IO.Path]::GetTempPath()
            Expand-Archive -Path $tempFile.FullName -DestinationPath $tempPath -Force

            # Get the path to the module version
            $tempModuleVersionPath = Join-Path -Path $tempPath -ChildPath $Name
            $newTempModuleVersionPath = Join-Path -Path $tempPath -ChildPath $Version

            # Rename the folder to the module version so dtx.cloud.management > 0.6.1
            if (Test-Path -Path $newTempModuleVersionPath) {
                Remove-Item -Path $newTempModuleVersionPath -Recurse -Force
            }
            Rename-Item -Path $tempModuleVersionPath -NewName $newTempModuleVersionPath -Force

            $modulePath = Join-Path -Path "/usr/local/share" -ChildPath "powershell" -AdditionalChildPath "Modules", $Name
            $modulePathInclVersion = Join-Path -Path $modulePath -ChildPath $Version

            # Check if the module exist
            if (Test-Path -Path $modulePath) {
                # Check if the module version is already installed
                Write-Host "Checking if $($modulePathInclVersion) exists..."
                if (Test-Path -Path $modulePathInclVersion) {
                    # If we reached this point, we do not expect the version dir for this module to exist.
                    # We need to remove it before proceeding
                    Remove-Item -Path $modulePathInclVersion -Recurse -Force
                }
            }
            else {
                # If the module doesn't exist, create its path
                Write-Host "Creating $($modulePath)..."
                [void]$(New-Item -ItemType Directory -Path $modulePath -Force)
            }

            # Move the module to the modules folder
            Write-Host "Installing $($Name) version $($Version)..."
            Move-Item -Path $newTempModuleVersionPath -Destination $modulePath -Force
                
            Write-Host "Module installed successfully."
        }
        catch {
            Write-Error $_.Exception.Message
            throw "Failed to install module."
        }
    }
    function Test-ModuleIsInstalled {
        param (
            [string]$Name,
            [string]$Version
        )
        try {
            Write-Host "Testing if $($Name) version $($Version) is installed successfully..."
            Import-Module -Name $Name -RequiredVersion $Version -Force
            Write-Host "Test passed."
        }
        catch {
            Write-Error "Test failed."
            Write-Error $_.Exception.Message
        }
    }
}

Process {
    try {
        $params = @{
            Name         = $Name
            Version      = $Version
            BucketName   = $BucketName
            BucketRegion = $BucketRegion
            ObjectKey    = $ObjectKey
        }
        if ($IsWindows) {
            Install-ModuleForWindows @params
        }
        elseif ($IsLinux -or $MacOS) {
            Install-ModuleForNonWindows @params
        }
        else {
            throw "Unsupported operating system."
        }

        if (-not $SkipTestImport) {
            Test-ModuleIsInstalled @params
        }
    }
    catch {
        Write-Error $_.Exception.Message
        throw "Failed to install module."
    }
}