Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagepowershell
titleList, read and write blobs with Shared Key
linenumberstrue
collapsetrue
# inspired by:
#   https://stackoverflow.com/questions/53653473/generating-an-sas-token-in-java-to-download-a-file-in-an-azure-data-storage-cont?rq=1
#   https://purple.telstra.com.au/blog/loading-and-querying-data-in-azure-table-storage-using-powershell
#   https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key

function Create-Signature( [string] $Operation, [string] $Container, [string] $Blob, [string] $Account, [string] $AccessKey, [string] $Version, [DateTime] $Now, [int] $ContentLength=-1 )
{
    [Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null

    if ( $ContentLength -gt -1 )
    {
        $contentLengthValue = $ContentLength
    } else {
        $contentLengthValue = ''
    }

    $partToSign =      "`n" `
                     + "`n" `
                     + "`n" `
                     + (($ContentLength -gt -1) ? $ContentLength : '') $contentLengthValue + "`n" `
                     + "`n" `
                     + "`n" `
                     + "`n" `
                     + "`n" `
                     + "`n" `
                     + "`n" `
                     + "`n" `
                     + "`n" `
                     + "x-ms-blob-type:BlockBlob" + "`n" `
                     + "x-ms-date:$(Get-Date (Get-Date $Now).ToUniversalTime() -Format 'R')" + "`n" `
                     + "x-ms-version:$Version" + "`n"

    switch ( $Operation )                 
    {                 
        'GET'       {
                        $stringToSign  = 'GET' `
                                       + $partToSign `
                                       + "/$Account/$Container/$Blob"
                        break
                    }
                    
        'PUT'       {
                        $stringToSign  = 'PUT' `
                                       + $partToSign `
                                       + "/$Account/$Container/$Blob"
                        break
                    }
                    
        'LIST'      { 
                        $stringToSign  = "GET" `
                                       + $partToSign `
                                       + "/$Account/$Container" + "`n" `
                                       + "comp:list" + "`n" `
                                       + "restype:container"
                        break
                    }
                    
        default     {
                        throw "invalid operation: $Operation"
                    }
    }

    $hmac = New-Object System.Security.Cryptography.HMACSHA256
    $hmac.key = [Convert]::FromBase64String( $AccessKey )
    
    $signature = $hmac.ComputeHash( [Text.Encoding]::UTF8.GetBytes( $stringToSign ) )
    $signature = [Convert]::ToBase64String( $signature )

    Write-Debug "container: $Container"
    Write-Debug "Accoount: $Account"
    Write-Debug "AccessKey: $AccessKey"
    Write-Debug "stringToSign: $stringToSign"

    $signature
}

function Invoke-BlobRequest( [Uri] $Uri, [string] $Account, [string] $Signature, [DateTime] $Now, [string] $Version, [string] $Method='GET', [string] $FilePath='' )
{
    $requestParams = @{}
    $requestParams.Add( 'Uri', $Uri )
    $requestParams.Add( 'Method', $Method )
    
    if ( $FilePath )
    {
        $requestParams.Add( 'Infile', $FilePath )
    }
    
    $requestParams.Add( 'Headers', @{ `
                                      'x-ms-blob-type' = 'BlockBlob'; `
                                      'x-ms-date' = "$(Get-Date (Get-Date $Now).ToUniversalTime() -Format 'R')"; `
                                      'x-ms-version' = $Version; `
                                      'Authorization' = "SharedKey $($Account):$($Signature)" `
                                    } `
                      )

    Write-Debug ".... Invoke-WebRequest: $Uri"
    
    $requestParams.Keys | % {
        if ( $_ -eq 'Headers' )
        {
            $item = $_
            $requestParams.Item($_).Keys | % {
                Write-Debug "...... Headers $_ : $($requestParams.Item($item).Item($_))"
            }
        } else {
            Write-Debug "...... $_  $($requestParams.Item($_))"
        }
    }

    # run the web service request
    $response = Invoke-WebRequest @requestParams
    $response
}


# ---------- ---------- ----------
# Main
# ---------- ---------- ----------

$ownerAccount     = '<storage account>'
$ownerAccessKey   = '<storage account access key>'
$requesterAccount = '<requester account>'
$version          = '2019-10-10'

# consider badly synchronized server time and start 3 minutes in the past
$now = (Get-Date).ToUniversalTime().AddMinutes(-3)


Write-Host "`n"
Write-Host "# ++++++++++++++++++++++++++++++"
Write-Host "# List Blobs in Container"
Write-Host "# ++++++++++++++++++++++++++++++"

# step 1: create signature
$container = 'yade'
$signature = Create-Signature -Operation 'LIST' -Container $container -Account $ownerAccount -AccessKey $ownerAccessKey -Version $version -Now $now

    Write-Debug "Signature: $signature"

# step 2: send web service request
$uri      = "https://$($ownerAccount).blob.core.windows.net/$($container)?restype=container&comp=list"
$response = Invoke-BlobRequest -Uri $uri -Account $requesterAccount -Signature $signature -Now $now -Version $version -Method 'GET'

    Write-Host "Status Code: $($response.StatusCode)"
    Write-Host $response.Content


Write-Host "`n"
Write-Host "# ++++++++++++++++++++++++++++++"
Write-Host "# Get Blob from Container"
Write-Host "# ++++++++++++++++++++++++++++++"

# step 1: create signature
$container = 'yade'
$blob      = 'test.txt'
$signature = Create-Signature -Operation 'GET' -Container $container -Blob $blob -Account $ownerAccount -AccessKey $ownerAccessKey -Version $version -Now $now

    Write-Debug "Signature: $signature"

# step 2: send web service request
$uri      = "https://$($ownerAccount).blob.core.windows.net/$($container)/$($blob)"
$response = Invoke-BlobRequest -Uri $uri -Account $requesterAccount -Signature $signature -Now $now -Version $version -Method 'GET'

    Write-Host "Status Code: $($response.StatusCode)"
    Write-Host $response.Content

Write-Host "`n"
Write-Host "# ++++++++++++++++++++++++++++++"
Write-Host "# Write Blob to Container"
Write-Host "# ++++++++++++++++++++++++++++++"

# step 1: create signature
$container = 'yade'
$filePath  = "c:/tmp/some_blob.txt"
$blob      = (Get-Item $filePath).Name
$signature = Create-Signature -Operation 'PUT' -Container $container -Blob $blob -Account $ownerAccount -AccessKey $ownerAccessKey -Version $version -Now $now -ContentLength (Get-Content $filePath -Raw).length

    Write-Debug "Signature: $signature"

# step 2: send web service request
$uri      = "https://$($ownerAccount).blob.core.windows.net/$($container)/$($blob)"
$response = Invoke-BlobRequest -Uri $uri -Account $requesterAccount -Signature $signature -Now $now -Version $version -Method 'PUT' -FilePath $filePath

    Write-Host "Status Code: $($response.StatusCode)"
    Write-Host $response.Content


Explanations

The Shared Key authentication enable unlimited access i.e it does not have any expiry. Refer to the Azure article Authorize with Shared Key for reference. The above PowerShell script creates the Shared Key for three operation LIST, GET, PUT. The three operation support three different process:

...