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.
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
2.List items in a specific colletion:
No #itemId
$itemResourceId = "dbs/"+$databaseId+"/colls/"+$containerId
$itemResourceLink = "dbs/"+$databaseId+"/colls/"+$containerId+"/docs/"
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 + '"]'
Related
I have been trying to GET Blob using the script below but it gives an error highlighted below. I am using the script from this link and the $signatureString is used according to the this link. In a way, what i understand from technet that the authorization header should have the following
Convert the PS data to JSON
$json = $authHeader | ConvertTo-Json
$method = "GET"
$headerDate = (get-date -format r).ToString()
$headers = #{"x-ms-version"="$headerDate"}
$StorageAccountName = ""
$StorageContainerName = "users"
$StorageAccountKey = ""
$Url = "https://$StorageAccountName.blob.core.windows.net/$StorageContainerName/******/*****.rdp"
$body = "Hello world"
$xmsdate = (get-date -format r).ToString()
$headers.Add("x-ms-date",$xmsdate)
$contentLength = $body.Length
$headers.Add("Content-Length","$contentLength")
$headers.Add("x-ms-blob-type","BlockBlob")
$contentType = "application/json"
#$headers.Add("Content-Type","$contentType")
$signatureString = "$method$([char]10)$([char]10)$([char]10)$contentLength$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)"
#Add CanonicalizedHeaders
$signatureString += "x-ms-blob-type:" + $headers["x-ms-blob-type"] + "$([char]10)"
$signatureString += "x-ms-date:" + $headers["x-ms-date"] + "$([char]10)"
$signatureString += "x-ms-version:" + $headers["x-ms-version"] + "$([char]10)"
#Add CanonicalizedResource
$uri = New-Object System.Uri -ArgumentList $url
$signatureString += "/" + $StorageAccountName + $uri.AbsolutePath + $([char]10) + "restype:container"
$dataToMac = [System.Text.Encoding]::UTF8.GetBytes($signatureString)
$accountKeyBytes = [System.Convert]::FromBase64String($StorageAccountKey)
$hmac = new-object System.Security.Cryptography.HMACSHA256((,$accountKeyBytes))
$signature = [System.Convert]::ToBase64String($hmac.ComputeHash($dataToMac))
$headers.Add("Authorization", "SharedKey " + $StorageAccountName + ":" + $signature);
write-host -fore green $signatureString
write-host -fore green $headers
Invoke-RestMethod -Uri $Url -Method $method -headers $headers
Please someone assist me on this.
I am sorry! yes it was not that descriptive. here is the screenshot
InvalidHeaderValue-Incorrect format
I am actually trying to access azure blob storage using powerShell. today, i tried other script to do the same but I get a different error, please see the script and screenshot of that error message below.
$storageAccount = ""
$accesskey = ""
$version = "2017-04-17"
$resource = "tets"
$table_url = "https://$storageAccount.blob.core.windows.net/$resource"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$stringToSign = "$GMTTime`n/$storageAccount/$resource"
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Convert]::FromBase64String($accesskey)
$signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
$signature = [Convert]::ToBase64String($signature)
$headers = #{
'x-ms-date' = $GMTTime
Authorization = "SharedKeyLite " + $storageAccount + ":" + $signature
"x-ms-version" = $version
Accept = "application/json;odata=fullmetadata"
}
$item = Invoke-RestMethod -Method GET -Uri $table_url -Headers $headers -ContentType application/json
return $item.value
Here is the error message I get.
AuthenticationFailedServer
Consider using the Azure PowerShell modules to access blobs rather than raw API requests. All of the authentication details should be handled for you already.
i am trying to get login status of any website using passed parameter, My below script is returning always status code = 200 irrespective of wrong username/password.
Can anyone guide me pls?
$url = "{url here}"
$username = "{username here}"
$password = "{password here}"
$b = [System.Text.Encoding]::UTF8.GetBytes($username + ":" + $password)
$p = [System.Convert]::ToBase64String($b)
$creds = "Basic " + $p
$req1 = Invoke-WebRequest -Uri $url -Headers #{"Authorization" = $creds }
$StatusCode = [int] $req1.StatusCode;
$StatusDescription = $req1.StatusDescription;
write-host $StatusCode
write-host $StatusDescription
$Auth = "admin:password"
$JenkinsURL = "http://$Auth#172.24.235.27:8080/"
$JobName = "TestItem1"
$JobToken = "token"
$FullURL = "$JenkinsURL/job/$JobName/build?token=$JobToken"
Invoke-WebRequest -UseBasicParsing $FullURL
Above is the PowerShell code used for triggering Jenkins job. But while executing this I am facing "Authentication required" error. But the same command from curl is working fine.
I am not sure whether I am missing something in URL or missing some Jenkins plugin to provide access from PowerShell.
The reason you are getting an authentication error is that you will need to convert the authentication to base 64 string. Below is the script that you can use if you have not enabled the CSRF in Jenkins.
$UserName = "admin"
$Password = "password"
$API_URL = "jenkinsservername"
$JobName = "TestItem1"
$JobToken = "token"
$header = #{}
$Params = #{}
$header.Add('Authorization', 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(${UserName}):$(${Password})")))
$Params['uri'] = "http://jenkinsservername/$JobName/build?token=$JobToken"
$Params['Method'] = 'Post'
$Params['Headers'] = $header
Invoke-RestMethod #Params
But If you have CSRF Enabled in Jenkins then use below script
$UserName = "admin"
$Password = "password"
$API_URL = "jenkinsservername"
$JobName = "TestItem1"
$JobToken = "token"
$header = #{}
$header.Add('Authorization', 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(${UserName}):$(${Password})")))
$Params = #{uri = "http://${API_URL}:${API_Port}/crumbIssuer/api/json";
Method = 'Get';
Headers = $header;}
$API_Crumb = Invoke-RestMethod #Params
write-host $API_Crumb
$h.Add('Jenkins-Crumb', $API_Crumb.crumb)
$Params['uri'] = "http://jenkinsservername/$JobName/build?token=$JobToken"
$Params['Method'] = 'Post'
$Params['Headers'] = $header
Invoke-RestMethod #Params
#Mike correctly described the instructions for working with the jenkins api when using CSRF. But I would like to add that when creating crumbs, the session and cookies are also taken into account.
The code that works for me is as follows:
$UserName = "admin"
$Password = "password"
$API_URL = "jenkinsservername"
$JobName = "TestItem1"
$JobToken = "token"
$header = #{}
$header.Add('Authorization', 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(${UserName}):$(${Password})")))
$Params1 = #{uri = "http://${API_URL}:${API_Port}/crumbIssuer/api/json";
Method = 'Get';
SessionVariable = 'Session';
Headers = $header;}
$API_Crumb = Invoke-RestMethod #Params1
write-host $API_Crumb
$header.Add('Jenkins-Crumb', $API_Crumb.crumb)
$Params2 = #{}
$Params2['uri'] = "http://jenkinsservername/$JobName/build?token=$JobToken"
$Params2['Method'] = 'Post'
$Params2['Headers'] = $header
Invoke-RestMethod #Params2 -WebSession $Session
Also consider if the job is in a folder, then the uri will be different.
For example, if MyJob is in MyFolder then the uri will be:
http://jenkinsservername/job/MyFolder/job/MyJob/build?token=JobToken
You can see this path in the place where you assigned the token for the job
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.
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.