Functions/Copy-DryADModulesToRemoteTarget.ps1

Using NameSpace System.Management.Automation
Using NameSpace System.Management.Automation.Runspaces
# DryActiveDirectory is an AD config module for use with DryDeploy, or by itself.
#
# Copyright (C) 2021 Bjørn Henrik Formo (bjornhenrikformo@gmail.com)
# LICENSE: https://raw.githubusercontent.com/bjoernf73/DryActiveDirectory/main/LICENSE
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Function Copy-DryADModulesToRemoteTarget {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [PSSession]
        $PSSession,

        [Parameter(Mandatory)]
        [String]
        $RemoteRootPath,

        [Parameter(Mandatory)]
        [Array]
        $Modules,

        [Parameter(HelpMessage='Forcefully import the modules, so any previously imported versions are replaced')]
        [Switch]
        $Import
    )

    Try {
        # While copying multiple tiny files, the progress bar is flickering and not informative at all, so suppress it
        $OriginalProgressPreference = $ProgressPreference
        $ProgressPreference = 'SilentlyContinue'
       
        $InvokeDirParams = @{
            ScriptBlock = $DryAD_SB_RemoveAndReCreateDir
            Session     = $PSSession
            ArgumentList = @($RemoteRootPath)
        }
        $DirResult = Invoke-Command @InvokeDirParams
        
        Switch ($DirResult) {
            $True {
                ol d 'Created remote directory',"$RemoteRootPath"
            }
            {$DirResult -is [ErrorRecord]} {
                ol w 'Unable to create remote directory',"$RemoteRootPath"
                $PSCmdlet.ThrowTerminatingError($DirResult)
            }
            Default {
                Throw "Unable to create remote directory: $($DirResult.ToString())"
            }
        }

        ForEach ($Module in $Modules) {
            $ModuleObj = Get-Module -Name $Module -ListAvailable -ErrorAction Stop
            $ModuleFolder = Split-Path -Path $ModuleObj.Path
            $CopyItemsParams = @{
                Path        = $ModuleFolder
                Destination = $RemoteRootPath 
                ToSession   = $PSSession 
                Recurse     = $True
                Force       = $True
            }
            ol d @('Copying folder',"'$ModuleFolder'")
            Copy-Item @CopyItemsParams
        }
        
        # Add RemoteRootPath to $env:PSModulePath on the remote system, so functions are
        # available without explicit import. Prepare $RemoteRootPath and a $RemoteRootPathRegex
        # that allows us to test if the path is already added or not.
        
        # Change double backslash to single, remove trailing backslash, and lastly make all
        # single backslashes double in the regex
        $RemoteRootPath = ($RemoteRootPath.Replace('\\','\')).TrimEnd('\')         
        $RemoteRootPathRegEx = $RemoteRootPath.Replace('\','\\')

        $InvokePSModPathParams = @{
            ScriptBlock  = $DryAD_SB_PSModPath
            Session      = $PSSession 
            ArgumentList = @($RemoteRootPath,$RemoteRootPathRegEx)
        }
        $RemotePSModulePaths = Invoke-Command @InvokePSModPathParams

        ol d @('The PSModulePath on remote system',"'$RemotePSModulePaths'")
        Switch ($RemotePSModulePaths) {
            {$RemotePSModulePaths -Match $RemoteRootPathRegEx} {
                ol v @('Successfully added to remote PSModulePath',"'$RemoteRootPath'")
            }
            Default {
                ol w @('Failed to add path to remote PSModulePath',"'$RemoteRootPath'")
                Throw "The RemoteRootPath '$RemoteRootPath' was not added to the PSModulePath in the remote session"
            }
        }

        If ($Import) {
            $ImportModsParams = @{
                Session      = $PSSession 
                ScriptBlock  = $DryAD_SB_ImportMods 
                ArgumentList = @($Modules)
                ErrorAction  = 'Stop' 
            }   
            $ImportResult = Invoke-Command @ImportModsParams
    
            Switch ($ImportResult) {
                $True {
                    ol s "Modules were imported into the session"
                    ol v "The modules '$Modules' were imported into PSSession to $($PSSession.ComputerName)"
                }
                Default {
                    ol f "Modules were not imported into the session"
                    ol w "The modules '$Modules' were not imported into PSSession to $($PSSession.ComputerName)"
                    Throw "The modules '$Modules' were not imported into PSSession to $($PSSession.ComputerName)"
                }
            }
        }
    }
    Catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
    Finally {
        $ProgressPreference = $OriginalProgressPreference
    }
}