There are a lot of examples out there on how to POST a document to Cosmos DB, but they weren’t working for me. I kept getting a 400 Bad Request. After far to long, I finally got it to work. I need the “x-ms-documentdb-partitionkey” header to make it work.
Code for anyone who needs it (I hacked the original code here that wasn’t working for me):
Function Generate-MasterKeyAuthorizationSignature{ [CmdletBinding()] Param( [Parameter(Mandatory=$true)][String]$verb, [Parameter(Mandatory=$true)][String]$resourceLink, [Parameter(Mandatory=$true)][String]$resourceType, [Parameter(Mandatory=$true)][String]$dateTime, [Parameter(Mandatory=$true)][String]$key, [Parameter(Mandatory=$true)][String]$keyType, [Parameter(Mandatory=$true)][String]$tokenVersion ) $hmacSha256 = New-Object System.Security.Cryptography.HMACSHA256 $hmacSha256.Key = [System.Convert]::FromBase64String($key) If ($resourceLink -eq $resourceType) { $resourceLink = "" } $payLoad = "$($verb.ToLowerInvariant())`n$($resourceType.ToLowerInvariant())`n$resourceLink`n$($dateTime.ToLowerInvariant())`n`n" $hashPayLoad = $hmacSha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($payLoad)) $signature = [System.Convert]::ToBase64String($hashPayLoad) [System.Web.HttpUtility]::UrlEncode("type=$keyType&ver=$tokenVersion&sig=$signature") }
Code above just creates the auth header and is called below:
Function Post-CosmosDocuments{ [CmdletBinding()] Param( [Parameter(Mandatory=$true)][String]$EndPoint, [Parameter(Mandatory=$true)][String]$DBName, [Parameter(Mandatory=$true)][String]$CollectionName, [Parameter(Mandatory=$true)][String]$MasterKey, [String]$Verb="POST", [Parameter(Mandatory=$true)][String]$JSON ) $ResourceType = "docs"; $ResourceLink = "dbs/$DBName/colls/$CollectionName" $partitionkey = "[""$(($JSON |ConvertFrom-Json).id)""]" $dateTime = [DateTime]::UtcNow.ToString("r") $authHeader = Generate-MasterKeyAuthorizationSignature -verb $Verb -resourceLink $ResourceLink -resourceType $ResourceType -key $MasterKey -keyType "master" -tokenVersion "1.0" -dateTime $dateTime $header = @{authorization=$authHeader;"x-ms-version"="2015-12-16";"x-ms-documentdb-partitionkey"=$partitionkey;"x-ms-date"=$dateTime} $contentType= "application/json" $queryUri = "$EndPoint$ResourceLink/docs" #$header #$queryUri [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $result = Invoke-RestMethod -Method $Verb -ContentType $contentType -Uri $queryUri -Headers $header -Body $JSON }
And to run the functions above:
$CosmosDBEndPoint = "https://YourDBAccount.documents.azure.com:443/" $DBName = "yourDB" $CollectionName = "YourCollection" $MasterKey = "YourPrimaryKey" Post-CosmosDocuments -EndPoint $CosmosDBEndPoint -MasterKey $MasterKey -DBName $DBName -CollectionName $CollectionName -JSON ($SomeObject | ConvertTo-Json)
The key was to set the correct contentType and add “x-ms-documentdb-partitionkey” to the headers. This needs to match what your set your DB up with. I am useing “id”
As a bonus, here is the code to query a DB. Leveraging the same first function to create the auth header:
Function Query-CosmosDocuments{ [CmdletBinding()] Param( [Parameter(Mandatory=$true)][String]$EndPoint, [Parameter(Mandatory=$true)][String]$DBName, [Parameter(Mandatory=$true)][String]$CollectionName, [Parameter(Mandatory=$true)][String]$MasterKey, [Parameter(Mandatory=$true)][String]$JSON, [String]$Verb="POST" ) $ResourceType = "docs"; $ResourceLink = "dbs/$DBName/colls/$CollectionName" $query=@" { "query": "SELECT * FROM contacts c WHERE c.id = @id", "parameters": [ { "name": "@id", "value": "$(($JSON |ConvertFrom-Json).id)" } ] } "@ $dateTime = [DateTime]::UtcNow.ToString("r") $authHeader = Generate-MasterKeyAuthorizationSignature -verb $Verb -resourceLink $ResourceLink -resourceType $ResourceType -key $MasterKey -keyType "master" -tokenVersion "1.0" -dateTime $dateTime $header = @{authorization=$authHeader;"x-ms-version"="2015-12-16";"x-ms-documentdb-isquery"="True";"x-ms-date"=$dateTime} $contentType= "application/query+json" $queryUri = "$EndPoint$ResourceLink/docs" #$header #$queryUri [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $result = Invoke-RestMethod -Method $Verb -ContentType $contentType -Uri $queryUri -Headers $header -Body $query return $result }
Hope that helps someone
Hey, just curious what wasn’t working for you? I looked briefly through what you posted and what i had and couldnt find the exact difference. I’m using this code to post both my Weather data and BBQ data to cosmosdb.
also this is why i put it on github, for people to fork or pull request or whatever with it. š
I think it was because I selected “SQL” as the API and it requires a “Partition Key”. Therefore I needed x-ms-documentdb-partitionkey in the header.
I am still getting a 400 everytime I post
https://stackoverflow.com/questions/58561826/using-invoke-restmethod-to-post-to-a-cosmosdb-yields-a-400
Awesome, thank you for saving me time.
Hi
In your “Query-CosmosDocuments” there is no x-ms-documentdb-partitionkey is this expected? I am trying to run a simple
$query=@”
{
“query”: “SELECT VALUE COUNT(1) FROM c”,
“parameters”: [ ]
}
“@
I get 400 Bad Request.
If I remember, it was not necessary. I think a lot of other examples showed using it, but I don’t think I needed it in my tests.
Hurrah, that’s what I was looking for, what a stuff!
present here at this website, thanks admin of this website.