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.

        .PARAMETER ForceWebRequest
            Forces the use of Invoke-WebRequest over Start-BitsTransfer

        .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)]
        [ValidateNotNull()]
        [PSCustomObject] $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"),

        [Parameter(Mandatory = $False)]
        [switch] $ForceWebRequest
    )
    Begin {
        # Output variable
        $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")) {
                    try {
                        New-Item -Path $folder -Type Directory -Force -ErrorAction SilentlyContinue | Out-Null
                    }
                    catch {
                        Throw "Failed to create folder $folder."
                    }
                }
            }
            
            # Test whether the VcRedist is already on disk
            $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
            }

            # The VcRedist needs to be downloaded
            If ($download) {

                # If -ForceWebRequest or running on PowerShell Core (or Start-BitsTransfer is unavailable) download with Invoke-WebRequest
                If ($ForceWebRequest -or (!(Get-Command -Name Start-BitsTransfer -ErrorAction SilentlyContinue))) {
                    If ($pscmdlet.ShouldProcess($Vc.Download, "WebDownload")) {

                        # Use Invoke-WebRequest in instances where Start-BitsTransfer isn't supported or won't work
                        try {
                            Invoke-WebRequest -Uri $Vc.Download -OutFile $target
                        }
                        catch {
                            Throw "Failed to download VcRedist from $Vc.Download."
                        }
                    }
                }
                Else {
                    If ($pscmdlet.ShouldProcess($Vc.Download, "BitsDownload")) {
                        
                        # Use Start-BitsTransfer
                        try {
                            Start-BitsTransfer -Source $Vc.Download -Destination $target `
                                -Priority High -ErrorAction Continue -ErrorVariable $ErrorBits `
                                -DisplayName "Visual C++ Redistributable Download" -Description $Vc.Name
                        }
                        catch {
                            Throw "Failed to download VcRedist from $Vc.Download."
                        }
                    }
                }
            }
            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
    }
}