Download blob from Azure in PowerShell without AzureRM

This is an edge case, but not edge enough where people aren’t posting about it. Sometimes you don’t want to take an additional dependency and jump through hoops to get AzureRM installed on a machine and functional. This could entail getting the right version of PowerShell, setting permissions to access the PowerShell Gallery, etc. In my scenario, I’m using Packer to create images for Azure and needed to get a third-party installer on my image. So, I uploaded my assets to Azure and only needed to pull them down. I could have hosted my own web server and accessed the files over HTTP/HTTPS, but this was the path of least resistance. When I started down this path, I came across this page:

https://docs.microsoft.com/en-us/rest/api/storageservices/authentication-for-the-azure-storage-services

This page is more specification than how-to document, so there are plenty of comments saying they didn’t understand and other helpful visitors posted their examples. I also came across this page, that does a great job of outlining the process in various language but not PowerShell:

https://tsmatz.wordpress.com/2016/07/06/how-to-get-azure-storage-rest-api-authorization-header/

Taking their lead, I decided to put everything together in a nice and clean PowerShell function that you can use in your own scripts or modules. Enjoy.

function Get-BlobFromAzure {
    [CmdLetBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$StorageAccountName,

        [Parameter(Mandatory)]
        [string]$StorageAccountKey,

        [Parameter(Mandatory)]
        [string]$ContainerName,

        [Parameter(Mandatory)]
        [string]$BlobName,

        [Parameter(Mandatory)]
        [string]$TargetFolderPath
    )

    $verb = "GET"
    $url = "https://$($StorageAccountName).blob.core.windows.net/$($ContainerName)/$($BlobName)"
    $xMsVersion = "2015-02-21"
    $xMsDate = [DateTime]::UtcNow.ToString('r')
    $targetFilePath = Join-Path -Path $TargetFolderPath -ChildPath $BlobName

    $canonicalizedHeaders = "x-ms-date:$($xMsDate)`n" + `
        "x-ms-version:$($xMsVersion)"

    $canonicalizedResource = "/$($StorageAccountName)/$($ContainerName)/$($BlobName)"

    $stringToSign = $verb + "`n" + `
        $contentEncoding + "`n" + `
        $contentLanguage + "`n" + `
        $contentLength + "`n" + `
        $contentMD5 + "`n" + `
        $contentType + "`n" + `
        $date + "`n" + `
        $ifModifiedSince + "`n" + `
        $ifMatch + "`n" + `
        $ifNoneMatch + "`n" + `
        $ifUnmodifiedSince + "`n" + `
        $range + "`n" + `
        $canonicalizedHeaders + "`n" + `
        $canonicalizedResource

    $hmac = new-object System.Security.Cryptography.HMACSHA256
    $hmac.Key = [System.Convert]::FromBase64String($storageAccountKey)
    $dataToMac = [System.Text.Encoding]::UTF8.GetBytes($stringToSign)
    $sigByte = $hmac.ComputeHash($dataToMac)
    $signature = [System.Convert]::ToBase64String($sigByte)

    $headers = @{
        "x-ms-version" = $xMsVersion
        "x-ms-date" = $xMsDate
        "Authorization" = "SharedKey $($storageAccountName):$($signature)"
    }

    Invoke-RestMethod -Uri $url -Method $verb -Headers $headers -OutFile $targetFilePath
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s