Reputation: 77
i try to list content of our storage accounts by the example of the "liveworkerstorage". I have created an auth header and it was possible to create a file on a container but when i want to only list the content via Powershell i get an error message which tells me:
Invoke-RestMethod : AuthenticationFailedServer failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:781ec136-101e-0012-0620-f6ebe4000000
Time:2020-03-09T14:40:50.3148026ZThe MAC signature found in the HTTP request '16lBcDgjTWNtqInwWSghnHT0ae7yc5OS/05B72fVS4E=' is not the same as any computed signature. Server used following string to sign: 'GET
x-ms-blob-type:BlockBlob
x-ms-date:Mon, 09 Mar 2020 15:40:52 GMT
x-ms-version:2014-02-14
/liveworkerstorage/curltestdonotdelete/
restype:container'.
In C:\temp\Powershell\StoragePing\StoragePimg3.ps1:40 Zeichen:1
+ Invoke-RestMethod -method $method -Uri $Url -Headers $headers
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Here is my code... You can see that there are commented out values where i made the PUT Method and that worked.
$method = "GET"
#$method = "PUT"
$headerDate = '2014-02-14'
$headers = @{"x-ms-version" = "$headerDate" }
$StorageAccountName = "xxXXxx"
$StorageContainerName = "xxXXxx"
$StorageAccountKey = "xxXXxxXX"
#$Url = "https://$StorageAccountName.blob.core.cloudapi.de/$StorageContainerName/Test.txt"
$Url = "https://$StorageAccountName.blob.core.cloudapi.de/$StorageContainerName/?restype=container"
#$body = "Hello world"
$xmsdate = (get-date -format r).ToString()
$headers.Add("x-ms-date", $xmsdate)
$bytes = ([System.Text.Encoding]::UTF8.GetBytes($body))
$contentLength = $bytes.length
$headers.Add("Content-Length", "$contentLength")
$headers.Add("x-ms-blob-type", "BlockBlob")
$signatureString = "$method$([char]10)$([char]10)$([char]10)$contentLength$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)"
#Add CanonicalizedHeaders
$signatureString += "x-ms-blob-type:" + $headers["x-ms-blob-type"] + "$([char]10)"
$signatureString += "x-ms-date:" + $headers["x-ms-date"] + "$([char]10)"
$signatureString += "x-ms-version:" + $headers["x-ms-version"] + "$([char]10)"
#Add CanonicalizedResource
$uri = New-Object System.Uri -ArgumentList $url
$signatureString += "/" + $StorageAccountName + $uri.AbsolutePath
$dataToMac = [System.Text.Encoding]::UTF8.GetBytes($signatureString)
$accountKeyBytes = [System.Convert]::FromBase64String($StorageAccountKey)
$hmac = new-object System.Security.Cryptography.HMACSHA256((, $accountKeyBytes))
$signature = [System.Convert]::ToBase64String($hmac.ComputeHash($dataToMac))
$headers.Add("Authorization", "SharedKey " + $StorageAccountName + ":" + $signature);
write-host -fore green $signatureString
#Invoke-RestMethod -Uri $Url -Method $method -headers $headers -Body $body
Invoke-RestMethod -method $method -Uri $Url -Headers $headers
Thank you in advance
Best Regards
I have an update. Thanks so far for your answers!! It still doesn't work...
I changed my code for the get query.
But it tells me that the container doesn't exist. How is it possible to only list the root part of the storage?
I found this
$root?restype=container
So here is my code and the error message i get when i execute it...
#[CmdletBinding()]
#Param(
#[Parameter(Mandatory=$true,Position=1)] [string] $StorageAccountName,
#[Parameter(Mandatory=$True,Position=2)] [string] $FilesystemName,
#[Parameter(Mandatory=$True,Position=2)] [string] $AccessKey
#)
$StorageAccountName = "XXX"
#$StorageAccountName = "XXX"
#$FilesystemName = "XXX"
$FilesystemName = "XXX"
#$AccessKey = "XXX"
$AccessKey = "XXX"
$date = [System.DateTime]::UtcNow.ToString("R")
$n = "`n"
$method = "GET"
$stringToSign = "$method$n" #VERB
$stringToSign += "$n" # Content-Encoding + "\n" +
$stringToSign += "$n" # Content-Language + "\n" +
$stringToSign += "$n" # Content-Length + "\n" +
$stringToSign += "$n" # Content-MD5 + "\n" +
$stringToSign += "$n" # Content-Type + "\n" +
$stringToSign += "$n" # Date + "\n" +
$stringToSign += "$n" # If-Modified-Since + "\n" +
$stringToSign += "$n" # If-Match + "\n" +
$stringToSign += "$n" # If-None-Match + "\n" +
$stringToSign += "$n" # If-Unmodified-Since + "\n" +
$stringToSign += "$n" # Range + "\n" +
$stringToSign +=
<# SECTION: CanonicalizedHeaders + "\n" #>
"x-ms-date:$date" + $n +
"x-ms-version:2018-11-09" + $n #
<# SECTION: CanonicalizedHeaders + "\n" #>
$stringToSign +=
<# SECTION: CanonicalizedResource + "\n" #>
"/$StorageAccountName/$FilesystemName" + $n +
"recursive:true" + $n +
"resource:filesystem"#
<# SECTION: CanonicalizedResource + "\n" #>
$sharedKey = [System.Convert]::FromBase64String($AccessKey)
$hasher = New-Object System.Security.Cryptography.HMACSHA256
$hasher.Key = $sharedKey
$signedSignature = [System.Convert]::ToBase64String($hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($stringToSign)))
$authHeader = "SharedKey ${StorageAccountName}:$signedSignature"
$headers = @{"x-ms-date"=$date}
$headers.Add("x-ms-version","2018-11-09")
$headers.Add("Authorization",$authHeader)
$URI = "https://$StorageAccountName.blob.core.cloudapi.de/" + $FilesystemName + "?recursive=true&resource=filesystem"
$result = Invoke-RestMethod -method GET -Uri $URI -Headers $headers
Error Message:
Invoke-RestMethod : ContainerNotFoundThe specified container does not exist.
RequestId:c652069a-301e-0027-5ae1-f645b1000000
Time:2020-03-10T13:41:22.8270890Z
In C:\temp\Powershell\StoragePing\StoragePingfromweb.ps1:60 Zeichen:11
+ $result = Invoke-RestMethod -method GET -Uri $URI -Headers $headers
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Thank you so far!
Upvotes: 3
Views: 4286
Reputation: 41
Simpler if you generate your sas token through Azure, but here's what I've found that works when using a sas token...(no need to send headers)
Here is the reference that helped me put it together: https://blog.zuehlke.cloud/2019/10/access-azure-blob-storage-with-rest-and-sas/
# The sas token used below is the one generated by Azure in the Storage Account settings and censored
# and not one generated and signed using other methods (eg. the method you consistently see on help sites)
# To Note, in order to utilize the sas token with filters, you need to change the preceeding '?' with '&'
# Apologies for the variable names, I just yanked them from a larger test script
# The biggest difference when using a filter for these REST operations WITH A SAS KEY seems to be the need
# to define "&restype=container" whereas the same operation anonymously has no need for "&restype=container"
#
$sasTokenS1 = "?sv=<DATE>&ss=bfqt&srt=sco&sp=rwdlacupx&se=<DATEandTIME>&spr=https&sig=<Signature already in address format>" #included only for comparison
$sasTokenS1v2 = "&sv=<DATE>&ss=bfqt&srt=sco&sp=rwdlacupx&se=<DATEandTIME>&spr=https&sig=<Signature already in address format>"
$storageAccountS1 = "storageaccountname" #Standard StorageV2 (general purpose v2), paired with $sasTokenS1
$containerName2 = "containername"
$filter1 = "?comp=list" #the filter to list/read
$method1 = "GET" #the REST method, must be all CAPS
#
# With a sas token
$blobUri = "https://$storageAccountS1.blob.core.windows.net/$containerName2/$filter1&restype=container$sasTokenS1v2"
# Anonymous
$blobUri = "https://$storageAccountS1.blob.core.windows.net/$containerName2/$filter1"
#
#
Invoke-RestMethod -Method $method1 -Uri $blobUri
Upvotes: 2
Reputation: 136366
One issue that I see in your code is that you're computing canonicalizedResource
string incorrectly. As per the documentation here
, you would need to include query string parameters there.
So basically this line of code:
$signatureString += "/" + $StorageAccountName + $uri.AbsolutePath
Should be:
$signatureString += "/" + $StorageAccountName + $uri.AbsolutePath + $([char]10) + "restype:container"
Also some other comments:
x-ms-blob-type
header.GET
operation so you really don't need Content-Length
header.Upvotes: 0