Category: Azure

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
}