Public/Save-LatestUpdate.ps1

Function Save-LatestUpdate {
    <#
    .SYNOPSIS
        Downloads the latest cumulative update passed from Get-LatestUpdate.

    .DESCRIPTION
        Downloads the latest cumulative update passed from Get-LatestUpdate to a local folder.

        Then do one or more of the following:
        - Import the update into an MDT share with Import-LatestUpdate to speed up deployment of Windows (reference images etc.)
        - Apply the update to an offline WIM using DISM
        - Deploy the update with ConfigMgr (if not using WSUS)

    .NOTES
        Author: Aaron Parker
        Twitter: @stealthpuppy

    .PARAMETER Updates
        The array of latest cumulative updates retreived by Get-LatestUpdate.

    .PARAMETER Path
        A destination path for downloading the cumulative updates to. This path must exist. Uses the current diretory by default.

    .EXAMPLE
        Get-LatestUpdate | Save-LatestUpdate

        Description:
        Retreives the latest Windows 10 Cumulative Update with Get-LatestUpdate and passes the array of updates to Save-LatestUpdate on the pipeline.
        Save-LatestUpdate then downloads the latest updates to the current directory.

    .EXAMPLE
        $Updates = Get-LatestUpdate -WindowsVersion Windows10 -Build 14393
        Save-LatestUpdate -Updates $Updates -Path C:\Temp\Update

        Description:
        Retreives the latest Windows 10 build 14393 (1607) Cumulative Update with Get-LatestUpdate, saved to the variable $Updates.
        Save-LatestUpdate then downloads the latest updates to C:\Temp\Update.
    #>

    [CmdletBinding(SupportsShouldProcess = $True)]
    [OutputType([Array])]
    Param(
        [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, `
                HelpMessage = "The array of updates from Get-LatestUpdate.")]
        [ValidateNotNullOrEmpty()]
        [Array] $Updates,
    
        [Parameter(Mandatory = $False, Position = 1, ValueFromPipeline = $False, `
                HelpMessage = "Specify a target path to download the update(s) to.")]
        [ValidateScript( { If (Test-Path $_ -PathType 'Container') { $True } Else { Throw "Cannot find path $_" } })]
        [String] $Path = $PWD
    )
    Begin {
        $Path = Get-ValidPath $Path
    } 
    Process {
        # $Urls = $Updates | Select-UniqueUrl
        ForEach ($update in $Updates) {
            $filename = Split-Path $update.url -Leaf
            $target = "$($Path)\$($filename)"
            Write-Verbose "Download target will be $target"
            # $displayName = $Updates | Where-Object { $_.Url -eq $url } | Select-Object -ExpandProperty Note | Select-Object -First 1
            $displayName = $update.Note
            
            # If the update is not already downloaded, download it.
            If (!(Test-Path -Path $target)) {
                If (Get-Command Start-BitsTransfer -ErrorAction SilentlyContinue) {
                    If ($pscmdlet.ShouldProcess($(Split-Path $update.url -Leaf), "BitsDownload")) {
                        Start-BitsTransfer -Source $update.url -Destination $target `
                            -Priority High -ErrorAction Continue -ErrorVariable $ErrorBits `
                            -DisplayName $displayName -Description "Downloading $($update.url)"
                    }
                }
                Else {
                    # BITS isn't available (likely PowerShell Core)
                    If ($pscmdlet.ShouldProcess($update.url, "WebDownload")) {
                        Invoke-WebRequest -Uri $update.url -OutFile $target -UseBasicParsing
                    }
                }
            }
            Else {
                Write-Verbose "File exists: $target. Skipping download."
            }
        }
    }
    End {
        # Write-Output $urls
    }
}