简体   繁体   中英

Azure Storage Rest-API via Powershell to list container content

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!

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

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:

  • Since you're listing blobs, you really don't need x-ms-blob-type header.
  • Listing blob containers is a GET operation so you really don't need Content-Length header.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM