Get-RemoteFile.ps1

<#PSScriptInfo
 
.VERSION 1.0.1
.AUTHOR kalichuza
.GUID abc12345-6789-0123-4567-abcdef123456
.COMPANYNAME
.COPYRIGHT (c) 2024 kalichuza. All rights reserved.
.TAGS Download, File, Hash
.LICENSEURI
.PROJECTURI
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.REQUIREDSCRIPTS
.ATTACHEDFILES
.RELEASENOTES Initial release
.DESCRIPTION Downloads a file from a provided URL to a path. Can compare the hash and automatically remove the failed download. UserName and Password can be used for URLs that require basic authentication.
#>


#Requires -Version 2.0

<#
.SYNOPSIS
    Downloads a file from a provided URL to a path.
 
.EXAMPLE
    -Url "https://www.google.com/" -FilePath "C:\temp\index.html"
    Downloads the index html page from google.com to C:\temp\index.html
.EXAMPLE
    -Url "https://www.nirsoft.net/utils/advancedrun.zip" -FilePath "C:\temp\advancedrun.zip" -Hash "b2c65aa6e71b0f154c5f3a8b884582779d716ff2c03d6cdca9e157f0fe397c9c" -Algorithm SHA256
    Downloads the advancedrun.zip file from nirsoft to C:\temp\advancedrun.zip and validates that the SHA256 hash matches.
.EXAMPLE
    -Url "https://www.nirsoft.net/utils/advancedrun.zip" -FilePath "C:\temp\advancedrun.zip" -Hash "b2c65aa6e71b0f154c5f3a8b884582779d716ff2c03d6cdca9e157f0fe397c9c" -AutoRemove
    Downloads the advancedrun.zip file from nirsoft to C:\temp\advancedrun.zip and validates that the SHA256 hash matches. If the hash does not match, the file will be removed.
    Not specifying Algorithm defaults to SHA256.
.OUTPUTS
    String[]
.NOTES
    Minimum OS Architecture Supported: Windows 7, Windows Server 2008 R2
    Release Notes:
    Initial Release
.COMPONENT
    Download
#>


[CmdletBinding()]
param (
    # Url that will be used to download a file
    [Parameter(Mandatory = $true)]
    [string]$Url,
    # File path that the file will be placed
    [Parameter(Mandatory = $true)]
    [string]$FilePath,
    # Expected hash of downloaded file
    [Parameter()]
    [string]$Hash,
    # Hashing Algorithm to use for hash comparison
    [Parameter()][ValidateSet("SHA1", "SHA256", "SHA384", "SHA512", "MD5")]
    [string]$Algorithm = "SHA256",
    # Removes failed download automatically
    [Parameter()]
    [switch]$AutoRemove,
    # UserName
    [Parameter()]
    [string]$UserName,
    # Password
    [Parameter()]
    [string]$Password
)

# For PowerShell 2.0 and 3.0 compatibility
if ($PSVersionTable.PSVersion.Major -lt 4) {
    function Get-FileHash {
        param (
            [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
            [string[]]$Path,
            [Parameter(Mandatory = $false)]
            [ValidateSet("SHA1", "SHA256", "SHA384", "SHA512", "MD5")]
            [string]$Algorithm = "SHA256"
        )
        $Path | ForEach-Object {
            # Only hash files that exist
            $CurrentPath = $_
            if ($(Test-Path -Path $CurrentPath -ErrorAction SilentlyContinue)) {
                $HashAlgorithm = New-Object -TypeName System.Security.Cryptography.$($Algorithm)CryptoServiceProvider
                $Hash = [System.BitConverter]::ToString($hashAlgorithm.ComputeHash([System.IO.File]::ReadAllBytes($CurrentPath)))
                @{
                    Algorithm = $Algorithm
                    Path      = $Path
                    Hash      = $Hash.Replace('-', '')
                }
            }
        }
    }
}

# For PowerShell 2.0 compatibility
if ($PSVersionTable.PSVersion.Major -le 2) {
    $client = New-Object System.Net.WebClient
    if ($PSBoundParameters.ContainsKey("UserName") -and $PSBoundParameters.ContainsKey("Password")) {
        $client.Credentials = New-Object System.Net.NetworkCredential($UserName, $(ConvertTo-SecureString -String $Password -AsPlainText -Force))
    }
    elseif (
        ($PSBoundParameters.ContainsKey("UserName") -and -not $PSBoundParameters.ContainsKey("Password")) -or
        (-not $PSBoundParameters.ContainsKey("UserName") -and $PSBoundParameters.ContainsKey("Password"))
    ) {
        Write-Error "UserName and Password parameters have to be used together."
        exit 1
    }
    try {
        if ($null -ne $FilePath) {
            $client.DownloadFile($Url, $FilePath)
        }
        else {
            # Similar functionality to Invoke-WebRequest if -OutFile was not used, null, or an empty string
            # Defaults to the current directory
            $client.DownloadFile($Url, ".\")
        }
    }
    catch {
        Write-Error $_
        Write-Host "System.Net.WebClient Failed to download from $Url to $FilePath"
        exit 1
    }
}
else {
    $Splat = @{
        Uri        = $Url
        OutFile    = $FilePath
        Credential = if ($PSBoundParameters.ContainsKey("UserName") -and $PSBoundParameters.ContainsKey("Password")) {
            New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $UserName, $SecureString
        }
        else { $null }
    }
    try {
        Invoke-WebRequest @Splat
    }
    catch {
        Write-Error $_
        Write-Host "Invoke-WebRequest failed to download from $Url to $FilePath"
        exit 1
    }
    
}
Write-Host "Computing hash using $Algorithm"
$ComputedHash = $(Get-FileHash -Path $FilePath -Algorithm $Algorithm).Hash
Write-Host "Computed hash for $FilePath is $ComputedHash"
if ($PSBoundParameters.ContainsKey("Hash")) {
    if ($ComputedHash -like $Hash) {
        Write-Host "$FilePath hash matched!"
    }
    else {
        Write-Error "$FilePath hash did not match!"
        Write-Host "Computed hash of $FilePath was:"
        Write-Host "$ComputedHash"
        Write-Host "Expected hash was:"
        Write-Host "$Hash"
        if ($AutoRemove) {
            Write-Host "Removing failed download."
            try {
                # This should always remove the file
                Remove-Item $Path -Force -Confirm:$false -ErrorAction SilentlyContinue
                Write-Host "Removed failed download."
            }
            catch {
                if ($(Test-Path -Path $Path -ErrorAction SilentlyContinue)) {
                    Write-Host "Failed to remove failed download."
                }
                else {
                    Write-Host "Removed failed download."
                }
            }
        }
        exit 1
    }
}