I'm using this script from the download in this link.
https://gallery.technet.microsoft.com/scriptcenter/How-to-query-Azure-Cosmos-0a9aa517
However for some reason I am getting a 404 response.
I copy-pasted the url of the db directly. Putting in a fake URL gives me a "could not be resolved" error so I know the location exists.
Based on the Azure CosmosDB API documentation here:
https://learn.microsoft.com/en-us/rest/api/documentdb/databases
the $databaseID is user-set and just has to be unique, so I've set it to be the same as the db name and assigned that to the url.
Changing it to be different still gives me the same 404 response message (below).
Edit: Removed original commenting intro for readability
Powershell Script:
# add necessary assembly
#
Add-Type -AssemblyName System.Web
# generate authorization key
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)
$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")
}
# query
Function Query-CosmosDb
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$EndPoint,
[Parameter(Mandatory=$true)][String]$DataBaseId,
[Parameter(Mandatory=$true)][String]$CollectionId,
[Parameter(Mandatory=$true)][String]$MasterKey,
[Parameter(Mandatory=$true)][String]$Query
)
$Verb = "POST"
$ResourceType = "docs";
$ResourceLink = "dbs/$DatabaseId/colls/$CollectionId"
$dateTime = [DateTime]::UtcNow.ToString("r")
$authHeader = Generate-MasterKeyAuthorizationSignature -verb $Verb -resourceLink $ResourceLink -resourceType $ResourceType -key $MasterKey -keyType "master" -tokenVersion "1.0" -dateTime $dateTime
$queryJson = #{query=$Query} | ConvertTo-Json
$header = #{authorization=$authHeader;"x-ms-documentdb-isquery"="True";"x-ms-version"="2017-02-22";"x-ms-date"=$dateTime}
$contentType= "application/json "# The original said "application/query+json", I tried both
$queryUri = "$EndPoint$ResourceLink/docs"
$result = Invoke-RestMethod -Method $Verb -ContentType $contentType -Uri $queryUri -Headers $header -Body $queryJson
$result | ConvertTo-Json -Depth 10
}
# fill the target cosmos database endpoint uri, database id, collection id and masterkey
$DatabaseName = "" # name goes here
$MasterKey = "" #key goes here
$CollectionId = "transientUsers"
$DatabaseId = $DatabaseName
$CosmosDBEndPoint = "https://$DatabaseId.documents.azure.com:443/"
# query string
$Query = "SELECT * FROM transientUsers"
# execute
Query-CosmosDb -EndPoint $CosmosDBEndPoint -DataBaseId $DataBaseId -CollectionId $CollectionId -MasterKey $MasterKey -Query $Query
Error I'm getting:
Invoke-RestMethod : The remote server returned an error: (404) Not Found.
At D:\querycosmos\PowerShell\QueryCosmosDB.ps1:69 char:12
+ ... $result = Invoke-RestMethod -Method $Verb -ContentType $contentType ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I noticed that you used $DatabaseId in two places:
$ResourceLink = "dbs/$DatabaseId/colls/$CollectionId"
and
$CosmosDBEndPoint = "https://$DatabaseId.documents.azure.com:443/"
If $DatabaseId refers to your account name, then you would need to change your $ResourceLink variable and use the name of the database inside your account containing the collection. If however $DatabaseId refers to the database name, then you would need to change $CosmosDBEndPoint and use account name there.
Related
I am trying to write an azure function which posts data to a specific table e.g. (Table_CL) inside log analytics workspace. I am running into errors.
Install-Module -Name Az.OperationalInsights -Scope CurrentUser -Force
$logAnalyticsClient = Get-AzOperationalInsightsWorkspace -ResourceGroupName "rg-name" -Name "log-analytics-wsp"
$WorkspaceId = "/subscriptions/subscriptionID/resourceGroups/rg-name/providers/Microsoft.OperationalInsights/workspaces/log-analytics-wsp"
$SharedKey = "some-value"
$CustomTableName = "Table_CL"
# Obtain an authentication token
$tenantId = 'some value'
$clientId = 'value of Function App identity client IOD'
$Uri = "https://log-analytics-wsp.ods.opinsights.azure.com/api/logs?api-version=2016-04-01"
$Body = #{
"EventId" = $eventGridEvent.id;
"eventType" = $eventGridEvent.eventType;
"subject" = $eventGridEvent.subject;
"TimeGenerated" = [datetime]$eventGridEvent.eventTime;
"data" = (ConvertTo-Json -InputObject $eventGridEvent.data);
"dataVersion" = $eventGridEventvent.dataVersion;
"metadataVersion" = $eventGridEvent.metadataVersion;
}
$signature = $SharedKey + (get-date -uformat '%a, %d %b %Y %H:%M:%S ')
$hex = ""
foreach ($byte in $signature) {
$hex += "{0:x2}" -f $byte
}
$Signature = $hex
$Headers = #{
"Content-Type" = "application/json"
"Log-Type" = $CustomTableName
"Authorization" = $Signature
}
# Use the authentication token to send a request to the Log Analytics API
Invoke-WebRequest -Uri $Uri -Method Post -Body $Body -Headers $Headers
I am getting errors, can someone please guide me on how to post to specific table in log analytics using powershell script.
I am getting the following errors.
2023-01-18T19:56:48Z [Error] ERROR: The format of value 'some-shared-key-value-==Wed, 18 Jan 2023 19:56:47 GMT' is invalid.
Exception :
Type : System.FormatException
TargetSite :
Name : ParseValue
DeclaringType : System.Net.Http.Headers.HttpHeaderParser, System.Net.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
MemberType : Method
Module : System.Net.Http.dll
Message : The format of value 'some-shared-key-value-Wed, 18 Jan 2023 19:56:47 GMT' is invalid.
Source : System.Net.Http
HResult : -2146233033
StackTrace :
at System.Net.Http.Headers.HttpHeaderParser.ParseValue(String value, Object storeValue, Int32& index)
at System.Net.Http.Headers.HttpHeaders.ParseAndAddValue(HeaderDescriptor descriptor, HeaderStoreItemInfo info, String value)
at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetRequest(Uri uri)
at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
at System.Management.Automation.Cmdlet.DoProcessRecord()
at System.Management.Automation.CommandProcessor.ProcessRecord()
CategoryInfo : NotSpecified: (:) [Invoke-WebRequest], FormatException
FullyQualifiedErrorId : System.FormatException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
InvocationInfo :
MyCommand : Invoke-WebRequest
ScriptLineNumber : 55
OffsetInLine : 1
HistoryId : 1
ScriptName : C:\home\site\wwwroot\EventGridTrigger1\run.ps1
Line : Invoke-WebRequest -Uri $Uri -Method Post -Body $Body -Headers $Headers
PositionMessage : At C:\home\site\wwwroot\EventGridTrigger1\run.ps1:55 char:1
+ Invoke-WebRequest -Uri $Uri -Method Post -Body $Body -Headers $Header …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PSScriptRoot : C:\home\site\wwwroot\EventGridTrigger1
PSCommandPath : C:\home\site\wwwroot\EventGridTrigger1\run.ps1
InvocationName : Invoke-WebRequest
CommandOrigin : Internal
ScriptStackTrace : at <ScriptBlock>, C:\home\site\wwwroot\EventGridTrigger1\run.ps1: line 55
Any suggestions? I am looking for way to post to Log Analytics workspace specific table, any help would be great.
Thanks
I had to use the following API from azure docs. This works neatly as it is. Just plug the values and the post command is able to create table in Azure Monitor.
Following is the reference for the docs as well.
# Replace with your Workspace ID
$CustomerId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# Replace with your Primary Key
$SharedKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Specify the name of the record type that you'll be creating
$LogType = "MyRecordType"
# Optional name of a field that includes the timestamp for the data. If the time field is not specified, Azure Monitor assumes the time is the message ingestion time
$TimeStampField = ""
# Create two records with the same set of properties to create
$json = #"
[{ "StringValue": "MyString1",
"NumberValue": 42,
"BooleanValue": true,
"DateValue": "2019-09-12T20:00:00.625Z",
"GUIDValue": "9909ED01-A74C-4874-8ABF-D2678E3AE23D"
},
{ "StringValue": "MyString2",
"NumberValue": 43,
"BooleanValue": false,
"DateValue": "2019-09-12T20:00:00.625Z",
"GUIDValue": "8809ED01-A74C-4874-8ABF-D2678E3AE23D"
}]
"#
# Create the function to create the authorization signature
Function Build-Signature ($customerId, $sharedKey, $date, $contentLength, $method, $contentType, $resource)
{
$xHeaders = "x-ms-date:" + $date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash)
$keyBytes = [Convert]::FromBase64String($sharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.Key = $keyBytes
$calculatedHash = $sha256.ComputeHash($bytesToHash)
$encodedHash = [Convert]::ToBase64String($calculatedHash)
$authorization = 'SharedKey {0}:{1}' -f $customerId,$encodedHash
return $authorization
}
# Create the function to create and post the request
Function Post-LogAnalyticsData($customerId, $sharedKey, $body, $logType)
{
$method = "POST"
$contentType = "application/json"
$resource = "/api/logs"
$rfc1123date = [DateTime]::UtcNow.ToString("r")
$contentLength = $body.Length
$signature = Build-Signature `
-customerId $customerId `
-sharedKey $sharedKey `
-date $rfc1123date `
-contentLength $contentLength `
-method $method `
-contentType $contentType `
-resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = #{
"Authorization" = $signature;
"Log-Type" = $logType;
"x-ms-date" = $rfc1123date;
"time-generated-field" = $TimeStampField;
}
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing
return $response.StatusCode
}
# Submit the data to the API endpoint
Post-LogAnalyticsData -customerId $customerId -sharedKey $sharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType $logType
I've created an Azure Powershell Runbook to scale UP or Down a Datawarehouse dedicated SQL Pool using the Invoke-RestMethod but is failing with the following error:
At line:31 char:11 + $Body = # + ~ Unrecognized token in source text. At line:36 char:6 + "name" = "DW500c" + ~~~~~~ The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.
Here is the code being used
$Url = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Sql/servers/$sqlServerName/databases/$SynapseSqlPoolName?api-version=2020-08-01-preview HTTP/1.1 Content-Type: application/json; charset=UTF-8"
$Body = #
{
location: "West Europe",
"sku":
{
"name" = "DW500c"
}
}
Invoke-RestMethod -Method POST -Uri $url -Body $body -UseBasicParsing
I've tried to change the "=" with ":" but it gives the same error
I've tried with the solution below provided by Mathias but I'm getting a new error
$Body = #{
location = "West Europe"
sku = #{
name = "DW500c"
}
}
Error:
The remote server returned an error ( 400) Bad Request
Invoke-RestMethod : {"error":{"code":"MissingApiVersionParameter","message":"The api-version query parameter (?api-version=) is required for all requests."}} At line:38 char:3 + Invoke-RestMethod -Method POST -Uri $url -Body $body -UseBasi ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Latest code but getting "The specified content type is invalid"
$Url = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Sql/servers/$sqlServerName/databases/$SynapseSqlPoolName?api-version=2020-08-01-preview"
$ContentType = "HTTP/1.1 Content-Type:application/json;charset=UTF-8"
$Body = #{
location = "West Europe"
sku = #{
name = "DW500c"
}
}
Invoke-RestMethod -Method POST -Uri $url -ContentType $ContentType -Body $body -UseBasicParsing
The problem with the Content Type has been fixed but I'm still getting the api version error . Below is the full code
Param
(
# This is the Resource group where Azure Synapse Analytics SQL Pool is located
[Parameter(Mandatory=$True)]
[String] $resourceGroupName
,
# This is the name of the Azure SQL Server hosting the Azure Synapse Analytics SQL Pool
[Parameter(Mandatory=$True)]
[String] $sqlServerName
,
# This is the name of the Azure Synapse Analytics SQL Pool
[Parameter(Mandatory=$True)]
[String] $SynapseSqlPoolName
,
# This is the name of the Azure Synapse Analytics SQL Pool
[Parameter(Mandatory=$True)]
[String] $SubscriptionId
,
# This is the name of the Azure Synapse Analytics SQL Pool
[Parameter(Mandatory=$True)]
[String] $Sku
)
$ConnectionName = 'AzureRunAsConnection'
$ServicePrincipalConnection = Get-AutomationConnection -Name $ConnectionName
'Log in to Azure...'
$null = Connect-AzAccount `
-ServicePrincipal `
-TenantId $ServicePrincipalConnection.TenantId `
-ApplicationId $ServicePrincipalConnection.ApplicationId `
-CertificateThumbprint $ServicePrincipalConnection.CertificateThumbprint
$Url = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Sql/servers/$sqlServerName/databases/$SynapseSqlPoolName?api-version=2020-08-01-preview"
$ContentType = "application/json;charset=UTF-8"
$Body = #{
location = "West Europe"
sku = #{
name = $Sku
}
}
Invoke-RestMethod -Method POST -Uri $url -ContentType $ContentType -Body $body -UseBasicParsing
I've tried to change the "=" with ":" but it gives the same error
That'd be the correct course of action, but make sure you use a hashtable literal (#{ ... }) for the inner sku object too:
$Body = #{
location = "West Europe"
sku = #{
name = "DW500c"
}
}
It appears that the string value you assign to $Url contains a little more than just the URL - remove the trailing HTTP header garbage from the URL, and then pass the Content-Type header value to the -ContentType parameter of Invoke-RestMethod:
$Url = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Sql/servers/$sqlServerName/databases/$SynapseSqlPoolName?api-version=2020-08-01-preview"
$ContentType = 'application/json;charset=UTF-8'
# ...
Invoke-RestMethod -Method POST -Uri $url -ContentType $ContentType -Body $body -UseBasicParsing
Thanks to Mathias R. Jessen for his guidance, thanks to the 100 websites I've reviewed and to the 100 tests or more performed today , I finally found the way to make it work. We need to keep in mind that there is a different URL for a normal Azure database VS a Synapse database ( mine is synapse DB).
There are a couple of more tweaks that can be done to the code like , getting the subscriptionId and apiversion automatically instead of hardcoding it and also using some try , catch and IF condition to do the scale the Synapse DB when is not Paused but the code below accomplish what needs to done to scaleup and down a Snapse DB using a Powershell Runbook in Azure ( calling an API)
Param
(
# This is the Resource group where Azure Synapse Analytics SQL Pool is located
[Parameter(Mandatory=$True)]
[String] $resourceGroupName
,
# This is the name of the Azure SQL Server hosting the Azure Synapse Analytics SQL Pool
[Parameter(Mandatory=$True)]
[String] $sqlServerName
,
# This is the name of the Azure Synapse Analytics SQL Pool
[Parameter(Mandatory=$True)]
[String] $SynapseSqlPoolName
,
# This is the name of the Azure Synapse Analytics SQL Pool
[Parameter(Mandatory=$True)]
[String] $Sku
,
# This is the name of the Azure Synapse Analytics SQL Pool
[Parameter(Mandatory=$True)]
[String] $SubscriptionId
)
$ConnectionName = 'AzureRunAsConnection'
$ServicePrincipalConnection = Get-AutomationConnection -Name $ConnectionName
'Log in to Azure...'
$null = Connect-AzAccount `
-ServicePrincipal `
-TenantId $ServicePrincipalConnection.TenantId `
-ApplicationId $ServicePrincipalConnection.ApplicationId `
-CertificateThumbprint $ServicePrincipalConnection.CertificateThumbprint
$azContext = Get-AzContext
$azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($azProfile)
$token = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId)
$authHeader = #{
'Content-Type'='application/json'
'Authorization'='Bearer '+$token.AccessToken
}
$apiversion = "2021-06-01"
$Url = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Synapse/workspaces/$sqlServerName/sqlPools/$SynapseSqlPoolName"+"?api-version=$apiversion"
$ContentType = "application/json;charset=UTF-8"
$Body = #{
location = "westeurope"
sku = #{
name = $Sku
}
}
Invoke-RestMethod -Uri $Url -Method PUT -Headers $authHeader -Body ($body|ConvertTo-Json) ## -UseBasicParsing
My ultimate goal is to create a MS Teams team with channels and tabs of applications.
But first, I need to properly format my request. I dont know what I'm doing wrong.
Obviously I found this topic (https://learn.microsoft.com/en-us/graph/api/team-post?view=graph-rest-1.0) Example n°3 that looks promising but I dont know how to use it. I started with the code bellow:
$password = "stackexchange"
$login = "stackexchange#stackexchange.onmicrosoft.com"
$ownerEmail = "stackexchange#stackexchange.onmicrosoft.com"
$url = "https://graph.microsoft.com/v1.0/teams"
$securedPassword = convertto-securestring -String $password -AsPlainText -Force
$creds = new-object -typename System.Management.Automation.PSCredential -argumentlist $login, $securedPassword
$GraphAppId = "stackexchange-guid"
$GraphAppSecret = "stackexchange"
$AADDomain = "stackexchange.onmicrosoft.com"
Connect-AzureAD -Credential $creds
$userId = (Get-AzureADUser -ObjectId $ownerEmail).ObjectId
write-output $userId # Here the userId is actually displayed
Connect-PnPOnline -ClientId $GraphAppId -ClientSecret $GraphAppSecret -AADDomain $AADDomain
$accessToken = Get-PnPGraphAccessToken
$header = #{
"Content-Type" = "application/json"
Authorization = "Bearer $accessToken"
}
$body = #{
displayName = "Test"
"owners#odata.bind" = "https://graph.microsoft.com/v1.0/users('$userId')"
"template#odata.bind" = "https://graph.microsoft.com/v1.0/teamsTemplates('standard')"
memberSettings = #{
allowCreateUpdateChannels = $true
}
messagingSettings = #{
allowUserEditMessages = $true
allowUserDeleteMessages = $true
}
funSettings = #{
allowGiphy = $true
giphyContentRating = "strict"
}
}
$Body = ConvertTo-Json -InputObject $body
Invoke-RestMethod -Uri $url -Body $Body -Method 'Post' -Headers $header -UseBasicParsing -Credential $creds
I get the following message in my PowerShell terminal :
Invoke-RestMethod : {
"error": {
"code": "BadRequest",
"message": "Invalid bind property name owners in request.",
"innerError": {
"date": "2020-09-03T15:40:53",
"request-id": "fef8bd7e-3143-4ea9-bcf6-a87702a488b8"
}
}
}
At character Line:36 : 5
+ Invoke-RestMethod -Uri $url -Body $Body -Method 'Post' -Headers $ ...
+ CategoryInfo : InvalidOperation : (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Instead of doing this all "by hand", I'd suggest looking at the Graph SDK for PowerShell. It's still 'officially' in beta, but that's this PowerShell SDK, not the Graph itself of course.
you could also use the PS nuget "MicrosoftTeams"
for example:
# ===========================================
# this Script creates a new project environment containing:
# - a new TEAMs channel
# ===========================================
Install-Module MicrosoftTeams -Force # -AllowClobber
## parameters
$TeamDisplayName='contoso'
$ProjectName='Contoso-Reporting'
$TEAMS_ChannelName=$ProjectName
## connect to TEAMS
Connect-MicrosoftTeams
## Get the Opslogix TEAM
$team = Get-Team | foreach {if ( $_.DisplayName -eq $TeamDisplayName ) { $_ }}
## create a new project channel
$team | new-TeamChannel -DisplayName $TEAMS_ChannelName
#$team | Get-TeamChannel
## disconnect TEAMS
Disconnect-MicrosoftTeams
Try changing:
"owners#odata.bind" = "https://graph.microsoft.com/v1.0/users('$userId')"
to:
members = #(
#{
'#odata.type' = "#microsoft.graph.aadUserConversationMember"
roles = #(
'owner'
)
'user#odata.bind' = "https://graph.microsoft.com/v1.0/users('$userId')"
}
)
I am trying to post a document to cosmosdb using powershell, I have followed the steps in this post https://www.systemcenterautomation.com/2018/06/cosmos-db-rest-api-powershell/ and I am still getting a 400 error everytime
Add-Type -AssemblyName System.Web
# generate authorization key
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)
$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")
}
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
)
$Verb = "POST"
$ResourceType = "docs";
$ResourceLink = "dbs/$DBName/colls/$CollectionName"
$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"="2017-02-22";"x-ms-date"=$dateTime}
$contentType= "application/json"
$queryUri = "$EndPoint$ResourceLink/docs"
$result = Invoke-RestMethod -Method $Verb -ContentType $contentType -Uri $queryUri -Headers $header -Body $JSON
return $result.statuscode
}
$CosmosDBEndPoint = "https://<omitted>.documents.azure.com:443/"
$DBName = "database"
$CollectionName = "container"
$MasterKey = "<omitted>=="
$SomeObject = [PSObject]#{ id = 1 ; Application = "Ops"; Environment = "Dev"; adKey = "555-555-5555"; }
Post-CosmosDocuments -EndPoint $CosmosDBEndPoint -MasterKey $MasterKey -DBName $DBName -CollectionName $CollectionName -JSON ($SomeObject | ConvertTo-Json)
returns a 400 every time, I am not sure what is wrong with the request.
Based on the Powershell script samples, I see you are missing the headers:
x-ms-documentdb-partitionkey
User-Agent
2018-12-31 as x-ms-version
Adding the response body that you are getting would also help.
it is working now
I am not sure why I was getting a 400. perhaps I the timing on when I created the collection was off.
For a testing scenario, I'd like to run a Stored Procedure against each partition in a CosmosDB database, and I'm attempting to get the list of partition ranges from CosmosDB using PowerShell.
I'm getting a 401 - Unauthorized response for this query, but other queries on the same collection work fine - for example executing a procedure works.
The code I'm using to query the ranges is:
Add-Type -AssemblyName System.Web
# Configure as required
$accountName = ""
$connectionKey = ""
$collectionName = ""
$databaseName = ""
function GetKey([System.String]$Verb = '',[System.String]$ResourceId = '',
[System.String]$ResourceType = '',[System.String]$Date = '',
[System.String]$masterKey = '')
{
$keyBytes = [System.Convert]::FromBase64String($masterKey)
$text = #($Verb.ToLowerInvariant() + "`n" +
$ResourceType.ToLowerInvariant() + "`n" + $ResourceId + "`n" +
$Date.ToLowerInvariant() + "`n" + "" + "`n")
$body =[Text.Encoding]::UTF8.GetBytes($text)
$hmacsha = new-object -TypeName System.Security.Cryptography.HMACSHA256 -ArgumentList (,$keyBytes)
$hash = $hmacsha.ComputeHash($body)
$signature = [System.Convert]::ToBase64String($hash)
[System.Web.HttpUtility]::UrlEncode($('type=master&ver=1.0&sig=' + $signature))
}
function GetUTDate() {
$date = get-date
$date = $date.ToUniversalTime();
return $date.ToString("r", [System.Globalization.CultureInfo]::InvariantCulture);
}
function BuildHeaders([string]$action = "get",[string]$resType, [string]$resourceId){
$authz = GetKey -Verb $action -ResourceType $resType -ResourceId $resourceId -Date $apiDate -masterKey $connectionKey
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", $authz)
$headers.Add("x-ms-version", '2017-02-22')
$headers.Add("x-ms-date", $apiDate)
$headers.Add("Cache-Control", 'no-cache')
$headers.Add("Accept", 'application/json')
$headers.Add("Content-Type", 'application/json')
$headers
}
function GetPartitionKeys(){
$pkranges = "dbs/" + $databaseName + "/colls/" + $collectionName + "/pkranges"
$headers = BuildHeaders -action Get -resType colls -resourceId $pkranges
$uri = $rootUri + "/" + $pkranges
write-host "Calling" $uri
write-host($headers|Out-String)
$response = Invoke-RestMethod $uri -Method Get -Headers $headers
}
$rootUri = "https://" + $accountName + ".documents.azure.com"
GetPartitionKeys
I think the problem is to do with the Resource Type when I'm building the Auth header but the CosmosDB REST documentation doesn't have much information about querying this resource. At present I'm getting the following output:
Calling https://my-account-name.documents.azure.com/dbs/my-db-name/colls/my-coll-name/pkranges
Invoke-RestMethod : The remote server returned an error: (401) Unauthorized.
When computing the has for the authorization header the ResourceType value should be "pkranges".
The ResourceLink would be "dbs/my-db-name/colls/my-coll-name" and the URL you request is: https://my-account-name.documents.azure.com/dbs/my-db-name/colls/my-coll-name/pkranges
Paul- Stored procedures can be executed only against a partition key, not against individual partition key ranges.