Public/Get-VcRedist.ps1

Function Get-VcRedist {
    <#
        .SYNOPSIS
            Downloads the Visual C++ Redistributables from an array returned by Get-VcXml.

        .DESCRIPTION
            Downloads the Visual C++ Redistributables from an array returned by Get-VcXml into a folder structure that represents release and processor architecture.
            If the redistributable exists in the specified path, it will not be re-downloaded.

        .OUTPUTS
            System.Array

        .NOTES
            Author: Aaron Parker
            Twitter: @stealthpuppy

        .LINK
            https://github.com/aaronparker/Install-VisualCRedistributables

        .PARAMETER VcList
            Sepcifies 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 Release
            Specifies the release (or version) of the redistributables to download or install.

        .PARAMETER Architecture
            Specifies the processor architecture to download or install.

        .EXAMPLE
            Get-VcXml | Get-VcRedist -Path C:\Redist

            Description:
            Downloads the supported Visual C++ Redistributables to C:\Redist.
            
        .EXAMPLE
            Get-VcRedist -VcXml $VcRedists -Release "2012","2013",2017"

            Description:
            Downloads only the 2012, 2013 & 2017 releases of the Visual C++ Redistributables listed in $VcRedists

        .EXAMPLE
            Get-VcList | Get-VcRedist -Path C:\Temp\VcRedist -Architecture x64

            Description:
            Downloads only the 64-bit versions of the Visual C++ Redistributables listed in $VcRedists.
    #>

    [Alias("Save-VcRedist")]
    [CmdletBinding(SupportsShouldProcess = $True)]
    [OutputType([Array])]
    Param (
        [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $False, `
                HelpMessage = ".")]
        [ValidateNotNull()]
        [array] $VcList,

        [Parameter(Mandatory = $False, Position = 1, HelpMessage = "Specify a target path to download the Redistributables to.")]
        [ValidateScript( { If (Test-Path $_ -PathType 'Container') { $True } Else { Throw "Cannot find path $_" } })]
        [string] $Path,

        [Parameter(Mandatory = $False, HelpMessage = "Specify the version of the Redistributables to download.")]
        [ValidateSet('2005', '2008', '2010', '2012', '2013', '2015', '2017')]
        [string[]] $Release = @("2008", "2010", "2012", "2013", "2015", "2017"),

        [Parameter(Mandatory = $False, HelpMessage = "Specify the processor architecture/s to download.")]
        [ValidateSet('x86', 'x64')]
        [string[]] $Architecture = @("x86", "x64")
    )
    Begin {
        $output = @()
    }
    Process {
        # Filter release and architecture if specified
        If ( $PSBoundParameters.ContainsKey('Release') ) {
            Write-Verbose "Filtering releases for platform."
            $VcList = $VcList | Where-Object { $_.Release -eq $Release }
        }
        If ( $PSBoundParameters.ContainsKey('Architecture') ) {
            Write-Verbose "Filtering releases for architecture."
            $VcList = $VcList | Where-Object { $_.Architecture -eq $Architecture }
        }

        # Loop through each Redistributable and download to the target path
        ForEach ($Vc in $VcList) {
            Write-Verbose "Downloading: [$($Vc.Name)][$($Vc.Release)][$($Vc.Architecture)]"
            $output += $Vc

            # Create the folder to store the downloaded file. Skip if it exists
            # $folder = "$($(Get-Item -Path $Path).FullName)\$($Vc.Release)\$($Vc.Architecture)\$($Vc.ShortName)"
            $folder = Join-Path (Join-Path (Join-Path $(Resolve-Path -Path $Path) $Vc.Release) $Vc.Architecture) $Vc.ShortName

            If (Test-Path -Path $folder) {
                Write-Verbose "Folder '$folder' exists. Skipping."
            }
            Else {
                If ($pscmdlet.ShouldProcess($folder, "Create")) {
                    New-Item -Path $folder -Type Directory -Force -ErrorAction SilentlyContinue | Out-Null
                }
            }
            
            $target = Join-Path $folder $(Split-Path -Path $Vc.Download -Leaf)
            Write-Verbose "Testing target: $($target)"
            If (Test-Path -Path $target -PathType Leaf) {
                $ProductVersion = $(Get-FileMetadata -Path $target).ProductVersion
                
                # If the target Redistributable is already downloaded, compare the version
                If (($Vc.Version -gt $ProductVersion) -or ($Null -eq $ProductVersion)) {
                    # Download the newer version
                    Write-Verbose "$($Vc.Version) > $ProductVersion."
                    $download = $True
                }
                Else {
                    Write-Verbose "Manifest version: $($Vc.Version) matches file version: $ProductVersion."
                    $download = $False
                }
            }
            Else {
                $download = $True
            }

            If ($download) {
                # If running on Windows PowerShell use Start-BitsTransfer, otherwise use Invoke-WebRequest
                If ( Get-Command Start-BitsTransfer -ErrorAction SilentlyContinue ) {
                    If ( $pscmdlet.ShouldProcess($Vc.Download, "BitsDownload") ) {
                        Start-BitsTransfer -Source $Vc.Download -Destination $target `
                            -Priority High -ErrorAction Continue -ErrorVariable $ErrorBits `
                            -DisplayName "Visual C++ Redistributable Download" -Description $Vc.Name
                    }
                }
                Else {
                    If ( $pscmdlet.ShouldProcess($Vc.Download, "WebDownload") ) {
                        Invoke-WebRequest -Uri $Vc.Download -OutFile $target
                    }
                }
            }
            Else {
                Write-Verbose "$($target) exists."
            }
        }
    }
    End {
        # Return the $VcList array on the pipeline so that we can act on what was downloaded
        Write-Output $output
    }
}