1
votes

I am trying to access azure cosmos db account collection documents and also each document in collection. I had referred below link and changed all necessary cosmos db values like databaseid,container,itemid master key etc.,

Link:

https://github.com/Azure/azure-cosmos-dotnet-v3/blob/master/Microsoft.Azure.Cosmos.Samples/Usage/PowerShellRestApi/PowerShellScripts/ReadItem.ps1

But i am getting below error while running in Powershell.

Error:

  StatusCode: 401
  Exception Message: The remote server returned an error: (401) Unauthorized.
  System.Net.WebException: The remote server returned an error: (401) Unauthorized.
   at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
  at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()

NOTE: When i tried same in postman, i am getting list of documents but when i tried to get specific document . I am getting below error.

Error:

  The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign:

FYI: I am contributor role for Cosmodb account in azure portal

Parameters:

   $endpoint = "https://testcosmos.documents.azure.com:443/"
  $MasterKey = "<Key from Cosmos db account>"
  $KeyType = "master"
 $TokenVersion = "1.0"
 $date = Get-Date
 $utcDate = $date.ToUniversalTime()
 $xDate = $utcDate.ToString('r', 
  [System.Globalization.CultureInfo]::InvariantCulture)
 $databaseId = "testdb"
  $containerId = "containercollection"
 $itemResourceType = "docs"
$ItemId="1"
 $itemResourceId = "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"
  $itemResourceLink = "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"
  $verbMethod = "GET"

  $header = @{

    "authorization"         = "$authKey";

    "x-ms-version"          = "2018-12-31";

   "Cache-Control"         = "no-cache";

    "x-ms-date"             = "$xDate"

    "Accept"                = "application/json";

    "User-Agent"            = "PowerShell-RestApi-Samples";

    "x-ms-documentdb-partitionkey" = '["testPK"]'
}

I had tried commenting "Accept","User-Agent", Cache-Control header options but in vain. I also tried just getting only list of /docs without itemID but that also went in vain.

$result = Invoke-RestMethod -Uri $requestUri -Headers $header -Method $verbMethod -ContentType "application/json"
Write-Host "Read item response = "$result

Updated Code: I finally able to understand why issue is coming. its neither authentication issue nor resourceID. I am passing partition key in headers which is not hard coded as per sample . When i am passing value to partition key, it was not taking correctly hence causing issue. Below is my dynamic Code passing partition key

"x-ms-documentdb-partitionkey" = '["$Partitionkey"]' -- Displaying as 
[$Partitionkey] but it must print in headers like ["partitionkeyValue"]

Am trying how to fix that. Many many thanks for your suggestions.

2
Please edit your question and include the parameters you are passing to this PS function. - Gaurav Mantri
Aren't you missing $ItemId in your $itemResourceId and $itemResourceLink? Both of them should be "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"+$ItemId. - Gaurav Mantri
Adding $itemId also gives same error. So i I removed specifically to know whether i get entire document list but not getting - PavanKumar GVVS
I just copied the code from github.com/Azure/azure-cosmos-dotnet-v3/blob/master/… (link you're referencing) and ran the code and I was able to fetch the document details. I just changed the endpoint, master key, database and container id as per my account setting. - Gaurav Mantri
Team, I had updated my code - PavanKumar GVVS

2 Answers

2
votes

Firstly, i could query my item successfully with github code as same as @Gaurav Mantri. Your error code is 401 auth issue,so I assume that you make some mistakes with generation of MasterKeyAuthorizationSignature,especially the value of $itemResourceId.Please refer to the REST API document.

2 parts:

1.Query a specific item:

$itemId = "1"
$itemResourceType = "docs"
$itemResourceId = "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"+$ItemId
$itemResourceLink = "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"+$ItemId

enter image description here

2.List items in a specific colletion:

No #itemId

$itemResourceId = "dbs/"+$databaseId+"/colls/"+$containerId
$itemResourceLink = "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"

enter image description here

1
votes

I tried the following code and it worked well for me. I was able to fetch the document details:

Add-Type -AssemblyName System.Web

Function Generate-MasterKeyAuthorizationSignature{

    [CmdletBinding()]

    param (

        [string] $Verb,
        [string] $ResourceId,
        [string] $ResourceType,
        [string] $Date,
        [string] $MasterKey,
        [String] $KeyType,
        [String] $TokenVersion
    )

    $keyBytes = [System.Convert]::FromBase64String($MasterKey)

    $sigCleartext = @($Verb.ToLower() + "`n" + $ResourceType.ToLower() + "`n" + $ResourceId + "`n" + $Date.ToString().ToLower() + "`n" + "" + "`n")
    Write-Host "sigCleartext = " $sigCleartext

    $bytesSigClear = [Text.Encoding]::UTF8.GetBytes($sigCleartext)

    $hmacsha = new-object -TypeName System.Security.Cryptography.HMACSHA256 -ArgumentList (, $keyBytes)

    $hash = $hmacsha.ComputeHash($bytesSigClear) 

    $signature = [System.Convert]::ToBase64String($hash)

    $key = [System.Web.HttpUtility]::UrlEncode('type='+$KeyType+'&ver='+$TokenVersion+'&sig=' + $signature)

    return $key
}

$endpoint = "https://account-name.documents.azure.com:443/"
$MasterKey = "account-key"

$KeyType = "master"
$TokenVersion = "1.0"
$date = Get-Date
$utcDate = $date.ToUniversalTime()
$xDate = $utcDate.ToString('r', [System.Globalization.CultureInfo]::InvariantCulture)
$databaseId = "MyDatabaseId"
$containerId = "MyContainerId"
$itemId = "TestItem"
$itemResourceType = "docs"
$itemResourceId = "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"+$ItemId
$itemResourceLink = "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"+$ItemId
$verbMethod = "GET"

$requestUri = "$endpoint$itemResourceLink"

$authKey = Generate-MasterKeyAuthorizationSignature -Verb $verbMethod -ResourceId $itemResourceId -ResourceType $itemResourceType -Date $xDate -MasterKey $MasterKey -KeyType $KeyType -TokenVersion $TokenVersion

$header = @{

        "authorization"         = "$authKey";

        "x-ms-version"          = "2018-12-31";

        "Cache-Control"         = "no-cache";

        "x-ms-date"             = "$xDate";

        "Accept"                = "application/json";

        "User-Agent"            = "PowerShell-RestApi-Samples";

        "x-ms-documentdb-partitionkey" = '["testPk"]'
    }

try {
    $result = Invoke-RestMethod -Uri $requestUri -Headers $header -Method $verbMethod -ContentType "application/json"
    Write-Host "Read item response = "$result
    return "ReadItemSuccess";
}
catch {
    # Dig into the exception to get the Response details.
    # Note that value__ is not a typo.
    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ 
    Write-Host "Exception Message:" $_.Exception.Message
    echo $_.Exception|format-list -force
}

UPDATE

Regarding your comment about dynamically specifying the partition key value, try something like the following:

"x-ms-documentdb-partitionkey" = '["' + $Partitionkey + '"]'