Versions Compared

Key

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

...

  • Access to private Azure Blob Storage requires authentication.
  • Shared Key authentication

Example

Download: azure_authentication_shared_key_sample.ps1


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

$partToSign = "`n" `
+ "`n" `
+ "`n" `
+ (($ContentLength -gt -1) ? $ContentLength : '') + "`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