Skip to main content

Fetch and store a remote HTTP file PowerShell function.

function Get-HttpResource {
<#
.SYNOPSIS
Downloads the contents from a URL

.DESCRIPTION
Get-HttpResource downloads the contents of an HTTP url.
When -PassThru is specified it returns the string content.

.PARAMETER Url
The url containing the content to download.

.PARAMETER OutputPath
If provided, the content will be saved to this path.

.PARAMETER PassThru
If provided, the string will be output to the pipeline.

.EXAMPLE
$content = Get-HttpResource -Url 'http://my/url' `
                            -OutputPath 'c:\myfile.txt' `
                            -PassThru

This downloads the content located at http://my/url and
saves it to a file at c:\myfile.txt and also returns
the downloaded string.

.LINK
https://boxstarter.org

#>
    param (
        [string]$Url,
        [string]$OutputPath = $null,
        [switch]$PassThru
    )
    Write-Verbose "Downloading $url"
    $str = Invoke-RetriableScript -RetryScript {
        $downloader=new-object net.webclient
        $wp=[system.net.WebProxy]::GetDefaultProxy()
        $wp.UseDefaultCredentials=$true
        $downloader.Proxy=$wp
        $downloader.UseDefaultCredentials=$true
        # Fixes: "Powershell Invoke-WebRequest Fails with SSL/TLS Secure Channel"
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        try {
            if ($args[1]) {
                Write-Verbose "Saving $($args[0]) to $($args[1])"
                $downloader.DownloadFile($args[0], $args[1])
            }
            else {
                $downloader.DownloadString($args[0])
            }
        }
        catch{
            if ($VerbosePreference -eq "Continue"){
                Write-Error $($_.Exception | fl * -Force | Out-String)
            }
            throw $_
        }
    } $Url $OutputPath

    if ($PassThru) {
        if ($str) {
            Write-Output $str
        }
        elseif ($OutputPath) {
            Get-Content -Path $OutputPath
        }
    }
}

function Invoke-RetriableScript{
<#
.SYNOPSIS
Retries a script 5 times or until it completes without terminating errors.
All Unnamed arguments will be passed as arguments to the script
#>
    param([ScriptBlock]$RetryScript)
    $currentErrorAction=$ErrorActionPreference
    try{
        $ErrorActionPreference = "Stop"
        for ($count = 1; $count -le 5; $count++) {
            try {
                Write-Verbose "Attempt #$count..."
                $ret = Invoke-Command -ScriptBlock $RetryScript -ArgumentList $args
                return $ret
                break
            }
            catch {
                if ($global:Error.Count -gt 0){$global:Error.RemoveAt(0)}
                if ($count -eq 5) { throw $_ }
                else { Sleep 10 }
            }
        }
    }
    finally{
        $ErrorActionPreference = $currentErrorAction
    }
}

# Get-HttpResource -Url "https://github.com" -PassThru