Lib/Module.ps1
function GetModule { <# .SYNOPSIS Tests whether an exising PowerShell module meets the minimum or required version #> [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Name ) process { WriteVerbose -Message ($localized.LocatingModule -f $Name); ## Only return modules in the %ProgramFiles%\WindowsPowerShell\Modules location, ignore other $env:PSModulePaths $programFiles = [System.Environment]::GetFolderPath('ProgramFiles'); $modulesPath = ('{0}\WindowsPowerShell\Modules' -f $programFiles).Replace('\','\\'); $module = Get-Module -Name $Name -ListAvailable -Verbose:$false | Where-Object Path -match $modulesPath; if (-not $module) { WriteVerbose -Message ($localized.ModuleNotFound -f $Name); } else { WriteVerbose -Message ($localized.ModuleFoundInPath -f $module.Path); } return $module; } #end process } #end function GetModule function TestModule { <# .SYNOPSIS Tests whether an exising PowerShell module meets the minimum or required version #> [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Name, ## The minimum version of the module required [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'MinimumVersion')] [ValidateNotNullOrEmpty()] [System.Version] $MinimumVersion, ## The exact version of the module required [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'RequiredVersion')] [ValidateNotNullOrEmpty()] [System.Version] $RequiredVersion, ## Catch all to be able to pass parameters via $PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) process { $module = GetModule -Name $Name; if ($module) { $testLabModuleVersionParams = @{ ModulePath = $module.Path; } if ($MinimumVersion) { $testLabModuleVersionParams['MinimumVersion'] = $MinimumVersion; } if ($RequiredVersion) { $testLabModuleVersionParams['RequiredVersion'] = $RequiredVersion; } return (Test-LabModuleVersion @testLabModuleVersionParams); } else { return $false; } } #end process } #end function TestModule function ResolveModule { <# .SYNOPSIS Resolves a lab module definition by its name from Lability configuration data. #> [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( ## Specifies a PowerShell DSC configuration document (.psd1) containing the lab configuration. [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] $ConfigurationData, [Parameter(Mandatory)] [ValidateSet('Module','DscResource')] [System.String] $ModuleType, ## Lab module name/ID [Parameter(ValueFromPipelineByPropertyName)] [System.String[]] $Name, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $ThrowIfNotFound ) process { $modules = $ConfigurationData.NonNodeData.($labDefaults.ModuleName).$ModuleType; if (($PSBoundParameters.ContainsKey('Name')) -and ($Name -notcontains '*')) { ## Check we have them all first.. foreach ($moduleName in $Name) { if ($modules.Name -notcontains $moduleName) { if ($ThrowIfNotFound) { throw ($localized.CannotResolveModuleNameError -f $ModuleType, $moduleName); } else { WriteWarning -Message ($localized.CannotResolveModuleNameError -f $ModuleType, $moduleName); } } } $modules = $modules | Where-Object { $_.Name -in $Name }; } return $modules; } } #end function ResolveLabResource function TestModuleCache { <# .SYNOPSIS Tests whether the requested PowerShell module is cached. #> [CmdletBinding(DefaultParameterSetName = 'Name')] [OutputType([System.Boolean])] param ( ## PowerShell module/DSC resource module name [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Name, ## The minimum version of the module required [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [ValidateNotNullOrEmpty()] [System.Version] $MinimumVersion, ## The exact version of the module required [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.Version] $RequiredVersion, ## GitHub repository owner [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Owner, ## GitHub repository branch [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Branch, ## Source Filesystem module path [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Path, ## Provider used to download the module [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateSet('PSGallery','GitHub','FileSystem')] [System.String] $Provider, ## Lability PowerShell module info hashtable [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Module')] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable] $Module, ## Catch all to be able to pass parameter via $PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) begin { ## Remove -RemainingArguments to stop it being passed on. [ref] $null = $PSBoundParameters.Remove('RemainingArguments'); } process { $moduleFileInfo = Get-LabModuleCache @PSBoundParameters; return ($null -ne $moduleFileInfo); } #end process } #end function TestModuleCache function GetModuleCacheManifest { <# .SYNOPSIS Returns a zipped module's manifest. #> [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( ## File path to the zipped module [Parameter(Mandatory)] [System.String] $Path, [ValidateSet('PSGallery','GitHub')] [System.String] $Provider = 'PSGallery' ) begin { if (-not (Test-Path -Path $Path -PathType Leaf)) { throw ($localized.InvalidPathError -f 'Module', $Path); } } process { Write-Debug -Message 'Loading ''System.IO.Compression'' .NET binaries.'; [ref] $null = [System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression"); [ref] $null = [System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem"); $moduleFileInfo = Get-Item -Path $Path; if ($Provider -eq 'PSGallery') { $moduleName = $moduleFileInfo.Name -replace '\.zip', ''; } elseif ($Provider -eq 'GitHub') { ## If we have a GitHub module, trim the _Owner_Branch.zip; if we have a PSGallery module, trim the .zip $moduleName = $moduleFileInfo.Name -replace '_\S+_\S+\.zip', ''; } $moduleManifestName = '{0}.psd1' -f $moduleName; $temporaryArchivePath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "$moduleName.psd1"; try { ### Open the ZipArchive with read access WriteVerbose -Message ($localized.OpeningArchive -f $moduleFileInfo.FullName); $archive = New-Object System.IO.Compression.ZipArchive(New-Object System.IO.FileStream($moduleFileInfo.FullName, [System.IO.FileMode]::Open)); ## Zip archive entries are case-sensitive, therefore, we need to search for a match and can't use ::GetEntry() foreach ($archiveEntry in $archive.Entries) { if ($archiveEntry.Name -eq $moduleManifestName) { $moduleManifestArchiveEntry = $archiveEntry; } } [System.IO.Compression.ZipFileExtensions]::ExtractToFile($moduleManifestArchiveEntry, $temporaryArchivePath, $true); $moduleManifest = ConvertTo-ConfigurationData -ConfigurationData $temporaryArchivePath; } catch { Write-Error ($localized.ReadingArchiveItemError -f $moduleManifestName); } finally { if ($null -ne $archive) { WriteVerbose -Message ($localized.ClosingArchive -f $moduleFileInfo.FullName); $archive.Dispose(); } Remove-Item -Path $temporaryArchivePath -Force; } return $moduleManifest; } #end process } #end function GetModuleCacheVersion function RenameModuleCacheVersion { <# .SYNOPSIS Renames a cached module zip file with its version number. #> [CmdletBinding(DefaultParameterSetName = 'PSGallery')] [OutputType([System.IO.FileInfo])] param ( ## PowerShell module/DSC resource module name [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Name, ## Destination directory path to download the PowerShell module/DSC resource module to [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Path, ## GitHub module repository owner [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'GitHub')] [ValidateNotNullOrEmpty()] [System.String] $Owner, ## GitHub module branch [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'GitHub')] [ValidateNotNullOrEmpty()] [System.String] $Branch ) process { if ($PSCmdlet.ParameterSetName -eq 'GitHub') { $moduleManifest = GetModuleCacheManifest -Path $Path -Provider 'GitHub'; $versionedModuleFilename = '{0}-v{1}_{2}_{3}.zip' -f $Name, $moduleManifest.ModuleVersion, $Owner, $Branch; } else { $moduleManifest = GetModuleCacheManifest -Path $Path; $versionedModuleFilename = '{0}-v{1}.zip' -f $Name, $moduleManifest.ModuleVersion; } $versionedModulePath = Join-Path -Path (Split-Path -Path $Path -Parent) -ChildPath $versionedModuleFilename; if (Test-Path -Path $versionedModulePath -PathType Leaf) { ## Remove existing version module Remove-Item -Path $versionedModulePath -Force -Confirm:$false; } Rename-Item -Path $Path -NewName $versionedModuleFilename; return (Get-Item -Path $versionedModulePath); } #end process } #end function RenameModuleCacheVersion function InvokeModuleDownloadFromPSGallery { <# .SYNOPSIS Downloads a PowerShell module/DSC resource from the PowerShell gallery to the host's module cache. #> [CmdletBinding(DefaultParameterSetName = 'LatestAvailable')] [OutputType([System.IO.FileInfo])] param ( ## PowerShell module/DSC resource module name [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Name, ## Destination directory path to download the PowerShell module/DSC resource module to [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath = (Get-ConfigurationData -Configuration Host).ModuleCachePath, ## The minimum version of the module required [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'MinimumVersion')] [ValidateNotNullOrEmpty()] [System.Version] $MinimumVersion, ## The exact version of the module required [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'RequiredVersion')] [ValidateNotNullOrEmpty()] [System.Version] $RequiredVersion, ## Catch all, for splatting parameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) process { $destinationModuleName = '{0}.zip' -f $Name; $moduleCacheDestinationPath = Join-Path -Path $DestinationPath -ChildPath $destinationModuleName; $setResourceDownloadParams = @{ DestinationPath = $moduleCacheDestinationPath; Uri = Resolve-PSGalleryModuleUri @PSBoundParameters; NoCheckSum = $true; } $moduleDestinationPath = SetResourceDownload @setResourceDownloadParams; return (RenameModuleCacheVersion -Name $Name -Path $moduleDestinationPath); } #end process } #end function InvokeModuleDownloadFromPSGallery function InvokeModuleDownloadFromGitHub { <# .SYNOPSIS Downloads a DSC resource if it has not already been downloaded from Github. .NOTES Uses the GitHubRepository module! #> [CmdletBinding()] [OutputType([System.IO.DirectoryInfo])] param ( ## PowerShell DSC resource module name [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Name, ## Destination directory path to download the PowerShell module/DSC resource module to [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath = (Get-ConfigurationData -Configuration Host).ModuleCachePath, ## The GitHub repository owner, typically 'PowerShell' [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Owner, ## The GitHub repository name, normally the DSC module's name [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Repository = $Name, ## The GitHub branch to download, defaults to the 'master' branch [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Branch = 'master', ## Override the local directory name. Only used if the repository name does not ## match the DSC module name [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $OverrideRepositoryName = $Name, ## Force a download, overwriting any existing resources [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, ## Catch all, for splatting parameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) begin { if (-not $PSBoundParameters.ContainsKey('Owner')) { throw ($localized.MissingParameterError -f 'Owner'); } if (-not $PSBoundParameters.ContainsKey('Branch')) { WriteWarning -Message ($localized.NoModuleBranchSpecified -f $Name); } ## Remove -RemainingArguments to stop it being passed on. [ref] $null = $PSBoundParameters.Remove('RemainingArguments'); ## Add Repository and Branch as they might not have been explicitly passed. $PSBoundParameters['Repository'] = $Repository; $PSBoundParameters['Branch'] = $Branch; } process { ## GitHub modules are suffixed with .Owner_Branch.zip $destinationModuleName = '{0}_{1}_{2}.zip' -f $Name, $Owner, $Branch; $moduleCacheDestinationPath = Join-Path -Path $DestinationPath -ChildPath $destinationModuleName; $setResourceDownloadParams = @{ DestinationPath = $moduleCacheDestinationPath; Uri = ResolveGitHubModuleUri @PSBoundParameters; NoCheckSum = $true; } $moduleDestinationPath = SetResourceDownload @setResourceDownloadParams; return (RenameModuleCacheVersion -Name $Name -Path $moduleDestinationPath -Owner $Owner -Branch $Branch); } #end process } #end function InvokeModuleDownloadFromGitHub function InvokeModuleCacheDownload { <# .SYNOPSIS Downloads a PowerShell module (DSC resource) into the module cache. #> [CmdletBinding(DefaultParameterSetName = 'Name')] [OutputType([System.IO.FileInfo])] param ( ## PowerShell module/DSC resource module name [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Name, ## The minimum version of the module required [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [ValidateNotNullOrEmpty()] [System.Version] $MinimumVersion, ## The exact version of the module required [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.Version] $RequiredVersion, ## GitHub repository owner [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Owner, ## The GitHub repository name, normally the DSC module's name [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Repository = $Name, ## GitHub repository branch [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Branch, ## Source Filesystem module path [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateNotNullOrEmpty()] [System.String] $Path, ## Provider used to download the module [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameMinimum')] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'NameRequired')] [ValidateSet('PSGallery','GitHub','FileSystem')] [System.String] $Provider, ## Lability PowerShell module info hashtable [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Module')] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable[]] $Module, ## Destination directory path to download the PowerShell module/DSC resource module to [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath = (Get-ConfigurationData -Configuration Host).ModuleCachePath, ## Force a download of the module(s) even if they already exist in the cache. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, ## Catch all to be able to pass parameter via $PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) begin { ## Remove -RemainingArguments to stop it being passed on. [ref] $null = $PSBoundParameters.Remove('RemainingArguments'); if ($PSCmdlet.ParameterSetName -ne 'Module') { ## Create a module hashtable $newModule = @{ Name = $Name; Repository = $Repository; } if ($PSBoundParameters.ContainsKey('MinimumVersion')) { $newModule['MinimumVersion'] = $MinimumVersion; } if ($PSBoundParameters.ContainsKey('RequiredVersion')) { $newModule['RequiredVersion'] = $RequiredVersion; } if ($PSBoundParameters.ContainsKey('Owner')) { $newModule['Owner'] = $Owner; } if ($PSBoundParameters.ContainsKey('Branch')) { $newModule['Branch'] = $Branch; } if ($PSBoundParameters.ContainsKey('Path')) { $newModule['Path'] = $Path; } if ($PSBoundParameters.ContainsKey('Provider')) { $newModule['Provider'] = $Provider; } $Module = $newModule; } } process { foreach ($moduleInfo in $Module) { if ((-not (TestModuleCache @moduleInfo)) -or ($Force)) { if ((-not $moduleInfo.ContainsKey('Provider')) -or ($moduleInfo['Provider'] -eq 'PSGallery')) { if ($moduleInfo.ContainsKey('RequiredVersion')) { WriteVerbose -Message ($localized.ModuleVersionNotCached -f $moduleInfo.Name, $moduleInfo.RequiredVersion); } elseif ($moduleInfo.ContainsKey('MinimumVersion')) { WriteVerbose -Message ($localized.ModuleMinmumVersionNotCached -f $moduleInfo.Name, $moduleInfo.MinimumVersion); } else { WriteVerbose -Message ($localized.ModuleNotCached -f $moduleInfo.Name); } InvokeModuleDownloadFromPSGallery @moduleInfo; } elseif ($moduleInfo['Provider'] -eq 'GitHub') { WriteVerbose -Message ($localized.ModuleNotCached -f $moduleInfo.Name); InvokeModuleDownloadFromGitHub @moduleInfo; } elseif ($moduleInfo['Provider'] -eq 'FileSystem') { ## We should never get here as filesystem modules are not cached. ## If the test doesn't throw, it should return $true. } } else { Get-LabModuleCache @moduleInfo; } } #end foreach module } #end process } #end function InvokeModuleDownload function ExpandModuleCache { <# .SYNOPSIS Extracts a cached PowerShell module to the specified destination module path. #> [CmdletBinding()] [OutputType([System.IO.DirectoryInfo])] param ( ## PowerShell module hashtable [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable[]] $Module, ## Destination directory path to download the PowerShell module/DSC resource module to [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath, ## Removes existing module directory if present [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Clean, ## Catch all to be able to pass parameter via $PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) begin { [ref] $null = $PSBoundParameters.Remove('RemainingArguments'); } process { foreach ($moduleInfo in $Module) { $moduleFileInfo = Get-LabModuleCache @moduleInfo; $moduleSourcePath = $moduleFileInfo.FullName; $moduleDestinationPath = Join-Path -Path $DestinationPath -ChildPath $moduleInfo.Name; if ($Clean -and (Test-Path -Path $moduleDestinationPath -PathType Container)) { WriteVerbose -Message ($localized.CleaningModuleDirectory -f $moduleDestinationPath); Remove-Item -Path $moduleDestinationPath -Recurse -Force -Confirm:$false; } if ((-not $moduleInfo.ContainsKey('Provider')) -or ($moduleInfo.Provider -eq 'PSGallery')) { WriteVerbose -Message ($localized.ExpandingModule -f $moduleDestinationPath); $expandZipArchiveParams = @{ Path = $moduleSourcePath; DestinationPath = $moduleDestinationPath; ExcludeNuSpecFiles = $true; Force = $true; Verbose = $false; WarningAction = 'SilentlyContinue'; Confirm = $false; } [ref] $null = ExpandZipArchive @expandZipArchiveParams; } #end if PSGallery elseif (($moduleInfo.ContainsKey('Provider')) -and ($moduleInfo.Provider -eq 'GitHub')) { WriteVerbose -Message ($localized.ExpandingModule -f $moduleDestinationPath); $expandGitHubZipArchiveParams = @{ Path = $moduleSourcePath; ## GitHub modules include the module directory. Therefore, we need the parent root directory DestinationPath = Split-Path -Path $moduleDestinationPath -Parent;; Repository = $moduleInfo.Name; Force = $true; Verbose = $false; WarningAction = 'SilentlyContinue'; Confirm = $false; } if ($moduleInfo.ContainsKey('OverrideRepository')) { $expandGitHubZipArchiveParams['OverrideRepository'] = $moduleInfo.OverrideRepository; } [ref] $null = ExpandGitHubZipArchive @expandGitHubZipArchiveParams; } #end if GitHub elseif (($moduleInfo.ContainsKey('Provider')) -and ($moduleInfo.Provider -eq 'FileSystem')) { if ($null -ne $moduleFileInfo) { if ($moduleFileInfo -is [System.IO.FileInfo]) { WriteVerbose -Message ($localized.ExpandingModule -f $moduleDestinationPath); $expandZipArchiveParams = @{ Path = $moduleSourcePath; DestinationPath = $moduleDestinationPath; ExcludeNuSpecFiles = $true; Force = $true; Verbose = $false; WarningAction = 'SilentlyContinue'; Confirm = $false; } [ref] $null = ExpandZipArchive @expandZipArchiveParams; } elseif ($moduleFileInfo -is [System.IO.DirectoryInfo]) { WriteVerbose -Message ($localized.CopyingModuleDirectory -f $moduleFileInfo.Name, $moduleDestinationPath); ## If the target doesn't exist create it. We may be copying a versioned ## module, i.e. \xJea\0.2.16.6 to \xJea.. if (-not (Test-Path -Path $moduleDestinationPath -PathType Container)) { New-Item -Path $moduleDestinationPath -ItemType Directory -Force; } $copyItemParams = @{ Path = "$moduleSourcePath\*"; Destination = $moduleDestinationPath; Recurse = $true; Force = $true; Verbose = $false; Confirm = $false; } Copy-Item @copyItemParams; } } } #end if FileSystem ## Only output if we found a module during this pass if ($null -ne $moduleFileInfo) { Write-Output -InputObject (Get-Item -Path $moduleDestinationPath); } } #end foreach module } #end process } #end function ExpandModule # SIG # Begin signature block # MIIXtwYJKoZIhvcNAQcCoIIXqDCCF6QCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU0P/4dp55XvK2oZ57K/3//X8k # A+CgghLqMIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B # AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG # A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh # d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg # Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV # UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu # dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q # WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC # i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4 # ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3 # +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI # fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd # BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG # CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB # Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro # YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV # HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y # MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf # plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y # 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq # IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3 # DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh # dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD # QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE # BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT # eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow # mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0 # jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu # ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh # d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz # C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB # o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO # BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw # Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90 # cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx # oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy # bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV # HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa # 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH # bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73 # BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR # EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW # yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu # e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw # ggUZMIIEAaADAgECAhADViTO4HBjoJNSwH9//cwJMA0GCSqGSIb3DQEBCwUAMHIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJ # RCBDb2RlIFNpZ25pbmcgQ0EwHhcNMTUwNTE5MDAwMDAwWhcNMTcwODIzMTIwMDAw # WjBgMQswCQYDVQQGEwJHQjEPMA0GA1UEBxMGT3hmb3JkMR8wHQYDVQQKExZWaXJ0 # dWFsIEVuZ2luZSBMaW1pdGVkMR8wHQYDVQQDExZWaXJ0dWFsIEVuZ2luZSBMaW1p # dGVkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqLQmabdimcQtYPTQ # 9RSjv3ThEmFTRJt/MzseYYtZpBTcR6BnSfj8RfkC4aGZvspFgH0cGP/SNJh1w67b # iX9oT5NFL9sUJHUsVdyPBA1LhpWcF09PP28mGGKO3oQHI4hTLD8etiIlF9qFantd # 1Pmo0jdqT4uErSmx0m4kYGUUTa5ZPAK0UZSuAiNX6iNIL+rj/BPbI3nuPJzzx438 # oHYkZGRtsx11+pLA6hIKyUzRuIDoI7JQ0nZ0MkCziVyc6xGfS54JVLaVCEteTKPz # Gc4yyvCqp6Tfe9gs8UuxJiEMdH5fvllTU4aoXbm+W8tonkE7i/19rv8S1A2VPiVV # xNLbpwIDAQABo4IBuzCCAbcwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1 # DlgwHQYDVR0OBBYEFP2RNOWYipdNCSRVb5jIcyRp9tUDMA4GA1UdDwEB/wQEAwIH # gDATBgNVHSUEDDAKBggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8v # Y3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYv # aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmww # QgYDVR0gBDswOTA3BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93 # d3cuZGlnaWNlcnQuY29tL0NQUzCBhAYIKwYBBQUHAQEEeDB2MCQGCCsGAQUFBzAB # hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9j # YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURDb2RlU2ln # bmluZ0NBLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCclXHR # DhDyJr81eiD0x+AL04ryDwdKT+PooKYgOxc7EhRn59ogxNO7jApQPSVo0I11Zfm6 # zQ6K6RPWhxDenflf2vMx7a0tIZlpHhq2F8praAMykK7THA9F3AUxIb/lWHGZCock # yD/GQvJek3LSC5NjkwQbnubWYF/XZTDzX/mJGU2DcG1OGameffR1V3xODHcUE/K3 # PWy1bzixwbQCQA96GKNCWow4/mEW31cupHHSo+XVxmjTAoC93yllE9f4Kdv6F29H # bRk0Go8Yn8WjWeLE/htxW/8ruIj0KnWkG+YwmZD+nTegYU6RvAV9HbJJYUEIfhVy # 3DeK5OlY9ima2sdtMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkq # hkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j # MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBB # c3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAw # WjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL # ExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3Vy # ZWQgSUQgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB # CgKCAQEA+NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6 # kkPApfmJ1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQj # ZhJUM1B0sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5w # MWYzcT6scKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp # 6moKq4TzrGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH # 5DiLanMg0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgw # BgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYI # KwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j # b20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6 # Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmww # OqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ # RFJvb3RDQS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUH # AgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYD # VR0OBBYEFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuC # MS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2 # qB1dHC06GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4Q # pO4/cY5jDhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEp # KBo6cSgCPC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/Dm # ZAwlCEIysjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9 # CBoYs4GbT8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHv # MYIENzCCBDMCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0 # IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNl # cnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQA1YkzuBwY6CTUsB/ # f/3MCTAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkq # hkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGC # NwIBFTAjBgkqhkiG9w0BCQQxFgQUr5jbDNeIvnIBW2umKgMRZpeNzl8wDQYJKoZI # hvcNAQEBBQAEggEAPbT9ZC+BKlmyXkoqxCqt+H5FgZQ+WzExkdoZmY+u765OxNez # 1RTSjPyZwSO4qSNg3fWjKmgECmHO6tc2i0dNqV6DwjPOOT44tt+13S1Ywa7JBGWy # ezys8QF5BNoZvjw9u7oOMTU3AicGTVbIOzBPkATSYyx5UIyLHwR0uSHNBU9/kPl1 # FVbi7rE+QxAoJqBBMPFzyM3wkh1ONT+zhBxeMpxtq+rDiiccNIrJREht4DvwNDD4 # 4MbKwrj8ebhGlNnTFhGuzAqdKbLvROE4MPoncXhLN5fT5OzcJc/2oqc9+cmycWfy # mH58vR5RZM8htov1b6yT/mKDXZAx4OFdgSas9KGCAgswggIHBgkqhkiG9w0BCQYx # ggH4MIIB9AIBATByMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBD # b3Jwb3JhdGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2 # aWNlcyBDQSAtIEcyAhAOz/Q4yP6/NW4E2GqYGxpQMAkGBSsOAwIaBQCgXTAYBgkq # hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNzAzMTIyMTAz # MzdaMCMGCSqGSIb3DQEJBDEWBBQQfuv/GM6UUxHhtQvqYk0AjMcw0zANBgkqhkiG # 9w0BAQEFAASCAQCKZ+jdFkNBVgQ7xlXW0tBhy3ubdKI+aTYnNoTRCLb9ryz3Rsjb # KCAM9bNpcyev+L1mb0RwMwQ1BMY5pUY4Dj7x30Onc7lUcsMFhe3OZp0nAaFFPa5d # gWQ1SOp0xR1Vy/WedoLDFY2WXTNyAuD6pbLESQYugKV116mK3g63o1UyaXENxJLH # kcNUVNgKJD9c6Xa3Gq2jNsDx81m/xl4oIsz89j8BnuZcYC6dW9X2XxoqQfKlgdY0 # Ek6+ghGH5GmibHLnatEUlUl56EJ83onZD40JXpz+D07WO6BZJnXCoYkw0ZP54BWf # SZIArF9hR1mqkzNfU77B3PHn4cr7elnx4dLb # SIG # End signature block |