I've been struggling with what appears to be a common problem: formatting my authorization header for the Azure Table Service REST API. I have been unable to find an example using PowerShell and SharedKey, and am worried that I am making some dumb mistake in working backwards from other examples.
The specific (though unspecific) error is: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
I have been referencing these articles, as well as other examples:
I've confirmed that my key is correct, the table exists, has at least one row, and a number of other suggestions without luck.
If there is another approach I should take to accomplish the same goal, I'm all ears. I would like to use the REST API to allow for maximum compatibility on the clients.
$tableEndpoint = 'https://STORAGEACCOUNTNAME.table.core.windows.net/'
$tableName = 'TABLENAME'
$StorageAccountName = 'STORAGEACCOUNTNAME'
$Key = "STORAGEACCOUNTKEY"
Function New-AuthorizationHeader
{
param ($canonicalizedString)
[byte[]]$Bytes = [system.convert]::FromBase64String($Key)
$HMACSHA256 = [System.Security.Cryptography.HMACSHA256]::new($Bytes)
$dataToHmac = [System.Text.Encoding]::UTF8.GetBytes($canonicalizedString)
$Signature = [System.Convert]::ToBase64String($HMACSHA256.ComputeHash($dataToHmac))
[string]$AuthorizationHeader = "{0} {1}:{2}" -f "SharedKey",$StorageAccountName,$Signature
$AuthorizationHeader
}
Function New-Entity
{
param ($jsonContent)
$requestMethod = "POST"
$contentMD5 = [string]::Empty
$storageServiceVersion = '2016-05-31'
$reqDate = (Get-Date -Format r)
$contentType = "application/json"
$canonicalizedResource = "/{0}/{1}" -f $StorageAccountName,($tableEndpoint + $tableName)
$stringToSign = "{0}`n{1}`n{2}`n{3}`n{4}" -f $requestMethod,$contentMD5,$contentType,$reqDate,$canonicalizedResource
$authorizationHeader = New-AuthorizationHeader -canonicalizedString $stringToSign
$content = [System.Text.Encoding]::UTF8.GetBytes($jsonContent)
$fullURI = New-Object -TypeName System.Uri -ArgumentList ($tableEndpoint + $tableName)
$httpWebRequest = [System.Net.WebRequest]::Create($fullURI)
$httpWebRequest.Accept = 'application/json;odata=fullmetadata'
$httpWebRequest.ContentLength = $content.length
$httpWebRequest.ContentType = $contentType
$httpWebRequest.Method = $requestMethod
$httpWebRequest.Headers.Add("x-ms-date", $reqDate)
$httpWebRequest.Headers.Add("x-ms-version", $storageServiceVersion)
$httpWebRequest.Headers.Add("Authorization", $authorizationHeader)
$httpWebRequest.Headers.Add("Accept-Charset", "UTF-8")
$httpWebRequest.Headers.Add("DataServiceVersion", "3.0;NetFx")
$httpWebRequest.Headers.Add("MaxDataServiceVersion", "3.0;NetFx")
$requestStream = $httpWebRequest.GetRequestStream()
$requestStream.Write($content, 0, $content.length)
$requestStream.Close()
$response = $httpWebRequest.GetResponse()
$dataStream = $response.GetResponseStream()
$reader = New-Object -TypeName System.IO.StreamReader($dataStream)
$responseFromServer = $reader.ReadToEnd()
}
$jsonContent = @"
{
"ExecutionStatus"="smapledata",
"PartitionKey"="$ENV:Username",
"RowKey"="PrinterScript"
}
"@
New-Entity -jsonContent $jsonContent
Please make 2 changes above:
Canonical resource should not have table endpoint. So it should be:
$canonicalizedResource = "/{0}/{1}" -f $StorageAccountName,$tableName
JSON body should be properly formatted. So it should be:
$jsonContent = @" { "ExecutionStatus":"smapledata", "PartitionKey":"$ENV:Username", "RowKey":"PrinterScript" } "@
Once you make these changes, the code should work just fine.
Thank you again for your responses, Gaurav.
I verified that my clock is not skewed. In the end, I switched to using a Shared Access Signature, which is probably a better practice anyway.
Using a SAS obviates the need for an Authorization header (and getting that correctly formatted).
Here's the relevant updated PowerShell:
$tableEndpoint = 'https://STORAGEACCOUNT.table.core.windows.net/'
$tableName = 'TABLENAME'
$SAS = "?sv=2016-05-31&ss=t&srt=o&sp=wa&se=2017-09-01T04:08:11Z&st=2017-03-14T20:08:11Z&spr=https&sig=SIGNATURE"
$URI = $tableEndpoint + $tableName + $SAS
If (-NOT $script:RunLogKeyTime)
{
$script:RunLogKeyTime = (Get-Date -Format 'yyyyMMdd-HHmmss')
}
$RequestBody = ConvertTo-Json -InputObject @{
"TagetName"= $TargetName;
"Message"= $Message;
"ComputerName"= $ENV:ComputerName;
"Username"= $ENV:Username;
"EntryType"= $EntryType;
"PartitionKey"= "$Username`_$ScriptIdentifier";
"RowKey"= "$EntryType"}
$EncodedRequestBody = [System.Text.Encoding]::UTF8.GetBytes($RequestBody)
$RequestHeaders = @{
"x-ms-date"=(Get-Date -Format r);
"x-ms-version"="2016-05-31";
"Accept-Charset"="UTF-8";
"DataServiceVersion"="3.0;NetFx";
"MaxDataServiceVersion"="3.0;NetFx";
"Accept"="application/json;odata=nometadata";
"ContentLength"=$EncodedRequestBody.Length}
Invoke-WebRequest -Method POST -Uri $URI -Headers $RequestHeaders -Body $EncodedRequestBody -ContentType "application/json"
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.