internal/functions/Get/Save-StagingModule.ps1
function Save-StagingModule { <# .SYNOPSIS Downloads modules from a repository into a specified, local path. .DESCRIPTION Downloads modules from a repository into a specified, local path. This is used internally by Save-PSFModule to cache modules to deploy in one central location that is computer-local. .PARAMETER InstallData The specifics of the module to download. The result of the Resolve-ModuleTarget command, it contains V2/V3 specific targeting information. .PARAMETER Path The path where to save them to. .PARAMETER Repositories The repositories to contact. Must be repository objects as returned by Get-PSFRepository. Repository priority will be adhered. .PARAMETER Credential The Credentials to use for accessing the repositories. .PARAMETER SkipDependency Do not include any dependencies. Works with PowerShellGet V1/V2 as well. .PARAMETER AuthenticodeCheck Whether modules must be correctly signed by a trusted source. Uses "Get-PSFModuleSignature" for validation. Defaults to: $false Default can be configured under the 'PSFramework.NuGet.Install.AuthenticodeSignature.Check' setting. .PARAMETER TrustRepository Whether we should trust the repository installed from and NOT ask users for confirmation. .PARAMETER Cmdlet The $PSCmdlet variable of the calling command, used to ensure errors happen within the scope of the caller, hiding this internal helper command from the user. .EXAMPLE PS C:\> Save-StagingModule -InstallData $installData -Path $tempDirectory -Repositories $repositories -Cmdlet $PSCmdlet -Credential $Credential -SkipDependency:$SkipDependency -AuthenticodeCheck:$AuthenticodeCheck Downloads modules from a repository into a specified, local path. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")] [CmdletBinding()] param ( [object[]] $InstallData, [string] $Path, [object[]] $Repositories, [AllowNull()] [PSCredential] $Credential, [switch] $SkipDependency, [switch] $AuthenticodeCheck, [switch] $TrustRepository, $Cmdlet = $PSCmdlet ) begin { #region Implementing Functions function Save-StagingModuleV2 { [CmdletBinding()] param ( $Repository, $Item, [string] $Path, [AllowNull()] [PSCredential] $Credential, [switch] $SkipDependency, [switch] $AuthenticodeCheck ) Write-PSFMessage -String 'Save-StagingModule.SavingV2.Start' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item $callSpecifics = @{ AcceptLicense = $true ErrorAction = 'Stop' Repository = $Repository.Name } if ($Credential) { $callSpecifics.Credential = $Credential } if ($Repository.Credential) { $callSpecifics.Credential = $Repository.Credential } $result = [PSCustomObject]@{ Success = $false Error = $null ModuleName = $Item.Name ModuleVersion = $item.Version RepositoryName = $Repository.Name RepositoryType = $Repository.Type } $tempDirectory = New-PSFTempDirectory -Name StagingSub -ModuleName PSFramework.NuGet $param = $Item.v2Param # 1) Save to temp folder try { Save-Module @param -Path $tempDirectory @callSpecifics } catch { Write-PSFMessage -String 'Save-StagingModule.SavingV2.Error.Download' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item -Tag fail, save -ErrorRecord $_ $result.Error = $_ Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet Write-PSFMessage -String 'Save-StagingModule.SavingV2.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item return $result } # 2) Remove redundant modules if ($SkipDependency) { # V2 Does not support saving without its dependencies coming along, so we cleanup in pre-staging try { Get-ChildItem -Path $tempDirectory | Where-Object Name -NE $Item.Name | Remove-Item -Force -Recurse -ErrorAction Stop } catch { Write-PSFMessage -String 'Save-StagingModule.SavingV2.Error.DependencyCleanup' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item -Tag fail, cleanup -ErrorRecord $_ $result.Error = $_ Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet Write-PSFMessage -String 'Save-StagingModule.SavingV2.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item return $result } } # 3) Verify Signature if ($AuthenticodeCheck) { $signatures = foreach ($moduleBase in Get-ChildItem -Path $tempDirectory) { Get-PSFModuleSignature -Path (Get-Item -Path "$moduleBase\*").FullName } foreach ($signature in $signatures) { Write-PSFMessage -String 'Save-StagingModule.SavingV2.SignatureCheck' -StringValues $signature.Name, $signature.Version, $signature.IsSigned -Target $signature } if ($unsigned = @($signatures).Where{ -not $_.IsSigned }) { $result.Error = [System.Management.Automation.ErrorRecord]::new( [System.Exception]::new("Modules are not signed by a trusted code signer: $($unsigned.Name -join ', ')"), 'NotTrusted', [System.Management.Automation.ErrorCategory]::SecurityError, $unsigned ) Write-PSFMessage -String 'Save-StagingModule.SavingV2.Error.Unsigned' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item -Tag fail, signed -ErrorRecord $result.Error Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet Write-PSFMessage -String 'Save-StagingModule.SavingV2.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item return $result } } # 4) Move to Staging try { Get-ChildItem -Path $tempDirectory | Copy-Item -Destination $Path -Recurse -Force -ErrorAction Stop } catch { Write-PSFMessage -String 'Save-StagingModule.SavingV2.Error.Transfer' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item -Tag fail, save -ErrorRecord $_ $result.Error = $_ Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet Write-PSFMessage -String 'Save-StagingModule.SavingV2.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item return $result } Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet $result.Success = $true Write-PSFMessage -String 'Save-StagingModule.SavingV2.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item $result } function Save-StagingModuleV3 { [CmdletBinding()] param ( $Repository, $Item, [string] $Path, [AllowNull()] [PSCredential] $Credential, [switch] $SkipDependency, [switch] $AuthenticodeCheck, [switch] $TrustRepository ) Write-PSFMessage -String 'Save-StagingModule.SavingV3.Start' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item $callSpecifics = @{ ErrorAction = 'Stop' Repository = $Repository.Name } if ((Get-Command Save-PSResource).Parameters.Keys -contains 'AcceptLicense') { $callSpecifics.AcceptLicense = $true } if ($Credential) { $callSpecifics.Credential = $Credential } if ($Repository.Credential) { $callSpecifics.Credential = $Repository.Credential } if ($SkipDependency) { $callSpecifics.SkipDependencyCheck = $true } $result = [PSCustomObject]@{ Success = $false Error = $null ModuleName = $Item.Name ModuleVersion = $item.Version RepositoryName = $Repository.Name RepositoryType = $Repository.Type } $tempDirectory = New-PSFTempDirectory -Name StagingSub -ModuleName PSFramework.NuGet $param = $Item.v3Param # 1) Save to temp folder try { Save-PSResource @param -Path $tempDirectory @callSpecifics } catch { Write-PSFMessage -String 'Save-StagingModule.SavingV3.Error.Download' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item -Tag fail, save -ErrorRecord $_ $result.Error = $_ Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet Write-PSFMessage -String 'Save-StagingModule.SavingV3.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item return $result } # 2) Verify Signature if ($AuthenticodeCheck) { $signatures = foreach ($moduleBase in Get-ChildItem -Path $tempDirectory) { Get-PSFModuleSignature -Path (Get-Item -Path "$moduleBase\*").FullName } foreach ($signature in $signatures) { Write-PSFMessage -String 'Save-StagingModule.SavingV3.SignatureCheck' -StringValues $signature.Name, $signature.Version, $signature.IsSigned -Target $signature } if ($unsigned = @($signatures).Where{ -not $_.IsSigned }) { $result.Error = [System.Management.Automation.ErrorRecord]::new( [System.Exception]::new("Modules are not signed by a trusted code signer: $($unsigned.Name -join ', ')"), 'NotTrusted', [System.Management.Automation.ErrorCategory]::SecurityError, $unsigned ) Write-PSFMessage -String 'Save-StagingModule.SavingV3.Error.Unsigned' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item -Tag fail, signed -ErrorRecord $result.Error Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet Write-PSFMessage -String 'Save-StagingModule.SavingV3.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item return $result } } # 3) Move to Staging try { Get-ChildItem -Path $tempDirectory | Copy-Item -Destination $Path -Recurse -Force -ErrorAction Stop } catch { Write-PSFMessage -String 'Save-StagingModule.SavingV3.Error.Transfer' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item -Tag fail, save -ErrorRecord $_ $result.Error = $_ Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet Write-PSFMessage -String 'Save-StagingModule.SavingV3.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item return $result } Remove-PSFTempItem -Name StagingSub -ModuleName PSFramework.NuGet $result.Success = $true Write-PSFMessage -String 'Save-StagingModule.SavingV3.Done' -StringValues $result.Success, $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item $result } #endregion Implementing Functions $common = @{ SkipDependency = $SkipDependency AuthenticodeCheck = $AuthenticodeCheck Path = $Path Credential = $Credential } } process { :item foreach ($installItem in $InstallData) { $saveResults = foreach ($repository in $Repositories | Set-PSFObjectOrder -Property Priority, '>Type') { $saveResult = switch ($repository.Type) { V2 { Save-StagingModuleV2 -Repository $repository -Item $installItem @common } V3 { Save-StagingModuleV3 -Repository $repository -Item $installItem -TrustRepository:$TrustRepository @common } default { Stop-PSFFunction -String 'Save-StagingModule.Error.UnknownRepoType' -StringValues $repository.Type, $repository.Name -Target $repository -Cmdlet $Cmdlet -EnableException $true } } if ($saveResult.Success) { continue item } $saveResult } # Only reached if no repository was successful foreach ($result in $saveResults) { $Cmdlet.WriteError($result.Error) } Stop-PSFFunction -String 'Save-StagingModule.Error.SaveFailed' -StringValues $installItem.Name, $installItem.Version, (@($repository).ForEach{ '{0} ({1})' -f $_.Name, $_.Type } -join ', ') -Target $installItem -Cmdlet $Cmdlet -EnableException $true } } } |