Add a Document to CosmosDB via the REST API using PowerShell

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

,

7 Responses to Add a Document to CosmosDB via the REST API using PowerShell

  1. Billy York March 1, 2019 at 2:10 pm #

    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. šŸ™‚

  2. jbmurphy March 6, 2019 at 9:28 am #

    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.

  3. nick October 25, 2019 at 11:50 am #

    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

  4. Tim November 5, 2019 at 1:20 pm #

    Awesome, thank you for saving me time.

  5. Kumar December 20, 2019 at 3:47 am #

    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.

  6. jbmurphy March 21, 2020 at 9:38 am #

    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.

  7. www.mahakita.org October 21, 2020 at 4:07 pm #

    Hurrah, that’s what I was looking for, what a stuff!
    present here at this website, thanks admin of this website.

Leave a Reply