Public/Save-VcRedist.ps1
Function Save-VcRedist { <# .SYNOPSIS Downloads the Visual C++ Redistributables from an manifest returned by Get-VcList. .DESCRIPTION Downloads the Visual C++ Redistributables from an manifest returned by Get-VcList into a folder structure that represents release, version and processor architecture. If the redistributable exists in the specified path, it will not be re-downloaded. For example, the following folder structure will be created when downloading the 2010, 2012, 2013 and 2019 Redistributables to C:\VcRedist: C:\VcRedist\2010\10.0.40219.325\x64 C:\VcRedist\2010\10.0.40219.325\x86 C:\VcRedist\2012\11.0.61030.0\x64 C:\VcRedist\2012\11.0.61030.0\x86 C:\VcRedist\2013\12.0.40664.0\x64 C:\VcRedist\2013\12.0.40664.0\x86 C:\VcRedist\2019\14.28.29913.0\x64 C:\VcRedist\2019\14.28.29913.0\x86 .NOTES Author: Aaron Parker Twitter: @stealthpuppy .LINK https://stealthpuppy.com/VcRedist/save-vcredist.html .PARAMETER VcList Specifies the array that lists the Visual C++ Redistributables to download .PARAMETER Path Specify a target folder to download the Redistributables to, otherwise use the current folder. .PARAMETER Proxy Specifies a proxy server for the request, rather than connecting directly to the internet resource. Enter the URI of a network proxy server. .PARAMETER ProxyCredential Specifies a user account that has permission to use the proxy server that is specified by the Proxy parameter. The default is the current user. .PARAMETER NoProgress Specify this switch with -Verbose to show verbose output but also suppress Invoke-WebRequest progress to speed downloads. .EXAMPLE Save-VcRedist -VcList (Get-VcList) -Path C:\Redist Description: Downloads the supported Visual C++ Redistributables to C:\Redist. .EXAMPLE Get-VcList | Save-VcRedist -Path C:\Redist Description: Passes the list of supported Visual C++ Redistributables to Save-VcRedist and downloads the Redistributables to C:\Redist. .EXAMPLE $VcList = Get-VcList -Release 2013, 2019 -Architecture x86 Save-VcRedist -VcList $VcList -Path C:\Redist Description: Passes the list of 2013 and 2019 x86 supported Visual C++ Redistributables to Save-VcRedist and downloads the Redistributables to C:\Redist. .EXAMPLE Save-VcRedist -VcList (Get-VcList -Release 2010, 2012, 2013, 2019) -Path C:\Redist Description: Downloads the 2010, 2012, 2013, and 2019 Visual C++ Redistributables to C:\Redist. .EXAMPLE Save-VcRedist -VcList (Get-VcList -Release 2010, 2012, 2013, 2019) -Path C:\Redist -Proxy proxy.domain.local Description: Downloads the 2010, 2012, 2013, and 2019 Visual C++ Redistributables to C:\Redist using the proxy server 'proxy.domain.local' #> [Alias("Get-VcRedist")] [CmdletBinding(SupportsShouldProcess = $True, HelpURI = "https://stealthpuppy.com/VcRedist/save-vcredist.html")] [OutputType([System.Management.Automation.PSObject])] Param ( [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline)] [ValidateNotNull()] [System.Management.Automation.PSObject] $VcList, [Parameter(Mandatory = $False, Position = 1)] [ValidateScript( { If (Test-Path -Path $_ -PathType 'Container') { $True } Else { Throw "Cannot find path $_" } })] [System.String] $Path = (Resolve-Path -Path $PWD), [Parameter(Mandatory = $False)] [System.ObsoleteAttribute("This parameter should no longer be used. Invoke-WebRequest is used for all download operations.")] [System.Management.Automation.SwitchParameter] $ForceWebRequest, [Parameter(Mandatory = $False)] [System.ObsoleteAttribute("This parameter should no longer be used. Invoke-WebRequest is used for all download operations.")] [ValidateSet('Foreground', 'High', 'Normal', 'Low')] [System.String] $Priority = "Foreground", [Parameter(Mandatory = $False, Position = 3)] [System.String] $Proxy, [Parameter(Mandatory = $False, Position = 4)] [System.Management.Automation.PSCredential] $ProxyCredential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $False)] [System.Management.Automation.SwitchParameter] $NoProgress ) Begin { # Disable the Invoke-WebRequest progress bar for faster downloads If ($PSBoundParameters.ContainsKey("Verbose") -and !($PSBoundParameters.ContainsKey("NoProgress"))) { $ProgressPreference = "Continue" } Else { $ProgressPreference = "SilentlyContinue" } } Process { # Loop through each Redistributable and download to the target path ForEach ($VcRedist in $VcList) { # Build the path to save the VcRedist into # Target folder structure $folder = [System.IO.Path]::Combine((Resolve-Path -Path $Path), $VcRedist.Release, $VcRedist.Version, $VcRedist.Architecture) # Create the folder to store the downloaded file. Skip if it exists Write-Verbose -Message "$($MyInvocation.MyCommand): Test folder: [$folder]." If (Test-Path -Path $folder -ErrorAction "SilentlyContinue") { Write-Verbose -Message "$($MyInvocation.MyCommand): Folder [$folder] exists. Skipping." } Else { If ($PSCmdlet.ShouldProcess($folder, "Create")) { try { $params = @{ Path = $folder Type = "Directory" Force = $True ErrorAction = "SilentlyContinue" } New-Item @params > $Null } catch [System.Exception] { Write-Warning -Message "$($MyInvocation.MyCommand): Failed to create folder: [$folder]." Throw $_.Exception.Message Continue } } } # Test whether the VcRedist is already on disk $target = Join-Path -Path $folder -ChildPath $(Split-Path -Path $VcRedist.Download -Leaf) Write-Verbose -Message "$($MyInvocation.MyCommand): Testing target: $($target)" If (Test-Path -Path $target -PathType "Leaf" -ErrorAction "SilentlyContinue") { $ProductVersion = $(Get-FileMetadata -Path $target).ProductVersion # If the target Redistributable is already downloaded, compare the version If (($VcRedist.Version -gt $ProductVersion) -or ($Null -eq $ProductVersion)) { # Download the newer version Write-Verbose -Message "$($MyInvocation.MyCommand): Manifest version: [$($VcRedist.Version)] > file version: [$ProductVersion]." $download = $True } Else { Write-Verbose -Message "$($MyInvocation.MyCommand): Manifest version: [$($VcRedist.Version)] matches file version: [$ProductVersion]." $download = $False } } Else { $download = $True } # The VcRedist needs to be downloaded If ($download) { If ($PSCmdlet.ShouldProcess($VcRedist.Download, "Invoke-WebRequest")) { try { # Enable TLS 1.2 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Download the file Write-Verbose -Message "$($MyInvocation.MyCommand): Download VcRedist: [$($VcRedist.Release), $($VcRedist.Architecture), $($VcRedist.Version)]" $iwrParams = @{ Uri = $VcRedist.Download OutFile = $target UseBasicParsing = $True ErrorAction = "SilentlyContinue" } If ($PSBoundParameters.ContainsKey("Proxy")) { $iwrParams.Proxy = $Proxy } If ($PSBoundParameters.ContainsKey("ProxyCredential")) { $iwrParams.ProxyCredential = $ProxyCredential } Invoke-WebRequest @iwrParams } catch [System.Exception] { Write-Warning -Message "$($MyInvocation.MyCommand): Failed to download: [$($VcRedist.Name)]." Write-Warning -Message "$($MyInvocation.MyCommand): URL: [$($VcRedist.Download)]." Write-Warning -Message "$($MyInvocation.MyCommand): Download failed with: [$($_.Exception.Message)]" } finally { # Return the $VcList array on the pipeline so that we can act on what was downloaded If (Test-Path -Path $target -PathType "Leaf" -ErrorAction "SilentlyContinue") { Write-Output -InputObject $VcRedist } } } } Else { Write-Verbose -Message "$($MyInvocation.MyCommand): [$($target)] exists." } } } End { } } |