cosmos-db.psm1
$DB_TYPE = "dbs" # aka Container $COLLS_TYPE = "colls" $DOCS_TYPE = "docs" $PARTITIONKEYRANGE_TYPE = "pkranges" $GET_VERB = "get" $POST_VERB = "post" $PUT_VERB = "put" $DELETE_VERB = "delete" $API_VERSION = "2018-12-31" $MASTER_KEY_CACHE = @{} $SIGNATURE_HASH_CACHE = @{} $PARTITION_KEY_RANGE_CACHE = @{} Function Get-BaseDatabaseUrl([string]$Database) { return "https://$Database.documents.azure.com" } Function Get-CollectionsUrl([string]$Container, [string]$Collection) { return "$DB_TYPE/$Container/$COLLS_TYPE/$Collection" } Function Get-DocumentsUrl([string]$Container, [string]$Collection, [string]$RecordId) { return (Get-CollectionsUrl $Container $Collection) + "/$DOCS_TYPE/$RecordId" } Function Get-Time() { Get-Date ([datetime]::UtcNow) -Format "R" } Function Get-CacheValue([string]$key, [hashtable]$cache) { if ($env:COSMOS_DB_FLAG_ENABLE_CACHING -eq 0) { return $null } $cacheEntry = $cache[$key] if ($cacheEntry -and ($cacheEntry.Expiration -gt [datetime]::UtcNow)) { return $cacheEntry.Value } return $null } Function Set-CacheValue([string]$key, $value, [hashtable]$cache, [int]$expirationHours) { $cache[$key] = @{ Expiration = [datetime]::UtcNow.AddHours($expirationHours); Value = $value } } Function Get-Base64Masterkey([string]$ResourceGroup, [string]$Database, [string]$SubscriptionId) { $cacheKey = "$SubscriptionId/$ResourceGroup/$Database" $cacheResult = Get-CacheValue -Key $cacheKey -Cache $MASTER_KEY_CACHE if ($cacheResult) { return $cacheResult } if ($SubscriptionId) { $masterKey = az cosmosdb keys list --name $Database --query primaryMasterKey --output tsv --resource-group $ResourceGroup --subscription $SubscriptionId } else { $masterKey = az cosmosdb keys list --name $Database --query primaryMasterKey --output tsv --resource-group $ResourceGroup } Set-CacheValue -Key $cacheKey -Value $masterKey -Cache $MASTER_KEY_CACHE -ExpirationHours 6 $masterKey } Function Get-Signature([string]$verb, [string]$resourceType, [string]$resourceUrl, [string]$now) { $parts = @( $verb.ToLower(), $resourceType.ToLower(), $resourceUrl, $now.ToLower(), "" ) (($parts -join "`n") + "`n") } Function Get-Base64EncryptedSignatureHash([string]$masterKey, [string]$signature) { $cacheKey = "$masterKey/$signature" $cacheResult = Get-CacheValue -Key $cacheKey -Cache $SIGNATURE_HASH_CACHE if ($cacheResult) { return $cacheResult } $keyBytes = [System.Convert]::FromBase64String($masterKey) $hasher = New-Object System.Security.Cryptography.HMACSHA256 -Property @{ Key = $keyBytes } $sigBinary = [System.Text.Encoding]::UTF8.GetBytes($signature) $hashBytes = $hasher.ComputeHash($sigBinary) $base64Hash = [System.Convert]::ToBase64String($hashBytes) Set-CacheValue -Key $cacheKey -Value $base64Hash -Cache $SIGNATURE_HASH_CACHE -ExpirationHours 1 $base64Hash } Function Get-EncodedAuthString([string]$signatureHash) { $authString = "type=master&ver=1.0&sig=$signatureHash" [uri]::EscapeDataString($authString) } Function Get-AuthorizationHeader([string]$ResourceGroup, [string]$SubscriptionId, [string]$Database, [string]$verb, [string]$resourceType, [string]$resourceUrl, [string]$now) { $masterKey = Get-Base64Masterkey -ResourceGroup $ResourceGroup -Database $Database -SubscriptionId $SubscriptionId $signature = Get-Signature -verb $verb -resourceType $resourceType -resourceUrl $resourceUrl -now $now $signatureHash = Get-Base64EncryptedSignatureHash -masterKey $masterKey -signature $signature Get-EncodedAuthString -signatureHash $signatureHash } Function Get-CommonHeaders([string]$now, [string]$encodedAuthString, [string]$contentType = "application/json", [bool]$isQuery = $false, [string]$PartitionKey = $null) { $headers = @{ "x-ms-date" = $now; "x-ms-version" = $API_VERSION; "Authorization" = $encodedAuthString; "Cache-Control" = "No-Cache"; "Content-Type" = $contentType; } if ($isQuery) { $headers["x-ms-documentdb-isquery"] = "true" } if ($PartitionKey) { $headers["x-ms-documentdb-partitionkey"] = "[`"$PartitionKey`"]" } $headers } Function Get-QueryParametersAsNameValuePairs($obj) { if (!$obj) { return @() } if ($obj -is [array]) { return $obj } if ($obj -is [hashtable]) { return $obj.Keys | % { $nvs = @() } { $nvs += @{ name = $_; value = $obj.$_ } } { $nvs } } $type = $obj.GetType() throw "Cannot convert type $type to Name-Value pairs" } Function Get-ExceptionResponseOrThrow($err) { # Because PS Desktop and Core use different exception types for HTTP errors, it's hard to extract the content is a consistent # manner. Instead, this is used on exceptions to just pull out the details which are most useful (those needed by Get-CosmosDbRecordContent) # into a consistent wrapper object. The raw HTTP response object is included for debugging, but will be inconsistently typed # based on the platform. As more fields are needed, they can been pulled out into the wrapper. if ($err.Exception.Response) { $msg = @{ StatusCode = $err.Exception.Response.StatusCode; RawResponse = $err.Exception.Response; } if ($PSVersionTable.PSEdition -eq "Core") { # In PS Core, the body is eaten and put into this message # See: https://stackoverflow.com/questions/18771424/how-to-get-powershell-invoke-restmethod-to-return-body-of-http-500-code-response $msg.Content = $err.ErrorDetails.Message } else { # In Desktop we can re-read the content stream $result = $err.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $msg.Content = $reader.ReadToEnd() } return [PSCustomObject]$msg } else { throw $err.Exception } } Function Invoke-CosmosDbApiRequest([string]$verb, [string]$url, $headers, $body = $null) { if ($body) { $body = $body | ConvertTo-Json -Depth 100 } Invoke-WebRequest -Method $verb -Uri $url -Body $body -Headers $headers } Function Get-ContinuationToken($response) { $value = $response.Headers["x-ms-continuation"] if ($PSVersionTable.PSEdition -eq "Core") { if (-not $value) { return $null } # Headers were changed to arrays in version 7 # https://docs.microsoft.com/en-us/powershell/scripting/whats-new/breaking-changes-ps6?view=powershell-7.1#changes-to-web-cmdlets $value[0] } else { $value } } Function Invoke-CosmosDbApiRequestWithContinuation([string]$verb, [string]$url, $headers, $body = $null) { process { $response = Invoke-CosmosDbApiRequest -Verb $verb -Url $url -Body $body -Headers $headers $response $continuationToken = Get-ContinuationToken $response while ($continuationToken) { $headers["x-ms-continuation"] = $continuationToken $response = Invoke-CosmosDbApiRequest -Verb $verb -Url $url -Body $body -Headers $headers $response $continuationToken = Get-ContinuationToken $response } } } Function Get-PartitionKeyRangesOrError ( [parameter(Mandatory = $true)][string]$ResourceGroup, [parameter(Mandatory = $true)][string]$Database, [parameter(Mandatory = $true)][string]$Container, [parameter(Mandatory = $true)][string]$Collection, [parameter(Mandatory = $true)][string]$SubscriptionId ) { try { $baseUrl = Get-BaseDatabaseUrl $Database $collectionsUrl = Get-CollectionsUrl $Container $Collection $pkRangeUrl = "$collectionsUrl/$PARTITIONKEYRANGE_TYPE" $url = "$baseUrl/$pkRangeUrl" $cacheKey = $url $cacheResult = Get-CacheValue -Key $cacheKey -Cache $PARTITION_KEY_RANGE_CACHE if ($cacheResult) { return $cacheResult } $now = Get-Time $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $GET_VERB -resourceType $PARTITIONKEYRANGE_TYPE -resourceUrl $collectionsUrl -now $now $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -PartitionKey $requestPartitionKey $headers["x-ms-documentdb-query-enablecrosspartition"] = "true" $response = Invoke-CosmosDbApiRequest -Verb $GET_VERB -Url $url -Headers $headers | Get-CosmosDbRecordContent $ranges = $response.partitionKeyRanges Set-CacheValue -Key $cacheKey -Value $ranges -Cache $PARTITION_KEY_RANGE_CACHE -ExpirationHours 6 @{ Ranges = $ranges } } catch { @{ ErrorRecord = $_ } } } Function Get-FilteredPartitionKeyRangesForQuery($allRanges, $queryRanges) { $allRanges | where-object { $partitionMin = $_.minInclusive $partitionMax = $_.maxExclusive $queryRanges | where-object { $queryMin = $_.min $queryMax = $_.max !(($partitionMax -le $queryMin) -or ($partitionMin -gt $queryMax)) } } } <# .SYNOPSIS Fetches a single DB record, returns the HTTP response of the lookup .PARAMETER ResourceGroup Azure Resource Group of the database .PARAMETER Database The database name .PARAMETER Container The container name inside the database .PARAMETER Collection The collection name inside the container .PARAMETER RecordId The record's id .PARAMETER SubscriptionId [Optional] The Azure Subscription Id. Default is the same as `az`. .PARAMETER PartitionKey [Optional] The record's partition key. Default is `RecordId`. Required if using a custom partition strategy. .EXAMPLE $> Get-CosmosDbRecord ... StatusCode : 200 StatusDescription : OK Content : { ... } RawContent : ... Forms : {} Headers : { ... } Images : {} InputFields : {} Links : {} ParsedHtml : mshtml.HTMLDocumentClass RawContentLength : 1234 .EXAMPLE $> Get-CosmosDbRecord ... | Get-CosmosDbRecordContent id : 12345 key1 : value1 key2 : value2 .EXAMPLE $> Get-CosmosDbRecord ... | Get-CosmosDbRecordContent | ConvertTo-Json { "id": 12345, "key1", "value1", "key2": "value2" } #> Function Get-CosmosDbRecord( [parameter(Mandatory = $true)][string]$ResourceGroup, [parameter(Mandatory = $true)][string]$Database, [parameter(Mandatory = $true)][string]$Container, [parameter(Mandatory = $true)][string]$Collection, [parameter(Mandatory = $true)][string]$RecordId, [parameter(Mandatory = $false)][string]$SubscriptionId = "", [parameter(Mandatory = $false)][string]$PartitionKey = "") { begin { $baseUrl = Get-BaseDatabaseUrl $Database $documentUrl = Get-DocumentsUrl $Container $Collection $RecordId $url = "$baseUrl/$documentUrl" $now = Get-Time $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $GET_VERB -resourceType $DOCS_TYPE -resourceUrl $documentUrl -now $now $requestPartitionKey = if ($PartitionKey) { $PartitionKey } else { $RecordId } } process { try { $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -PartitionKey $requestPartitionKey -isQuery $true Invoke-CosmosDbApiRequest -Verb $GET_VERB -Url $url -Headers $headers } catch { Get-ExceptionResponseOrThrow $_ } } } <# .SYNOPSIS Fetches all DB record, returns the HTTP response. The records will be within the Documents property of the result. .PARAMETER ResourceGroup Azure Resource Group of the database .PARAMETER Database The database name .PARAMETER Container The container name inside the database .PARAMETER Collection The collection name inside the container .PARAMETER SubscriptionId [Optional] The Azure Subscription Id. Default is the same as `az`. .EXAMPLE $> Get-AllCosmosDbRecords ... StatusCode : 200 StatusDescription : OK Content : { ... } RawContent : ... Forms : {} Headers : { ... } Images : {} InputFields : {} Links : {} ParsedHtml : mshtml.HTMLDocumentClass RawContentLength : 1234 .EXAMPLE $> Get-AllCosmosDbRecords ... | Get-CosmosDbRecordContent id : 1 ... id : 2 ... .EXAMPLE $> Get-AllCosmosDbRecords ... | Get-CosmosDbRecordContent | ConvertTo-Json [ { "id": 1, ... }, { "id": 2, ... }, ] #> Function Get-AllCosmosDbRecords( [parameter(Mandatory = $true)][string]$ResourceGroup, [parameter(Mandatory = $true)][string]$Database, [parameter(Mandatory = $true)][string]$Container, [parameter(Mandatory = $true)][string]$Collection, [parameter(Mandatory = $false)][string]$SubscriptionId = "") { begin { $baseUrl = Get-BaseDatabaseUrl $Database $collectionsUrl = Get-CollectionsUrl $Container $Collection $docsUrl = "$collectionsUrl/$DOCS_TYPE" $url = "$baseUrl/$docsUrl" $now = Get-Time $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $GET_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now } process { $tmp = $ProgressPreference $ProgressPreference = 'SilentlyContinue' try { $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -isQuery $true Invoke-CosmosDbApiRequestWithContinuation -verb $GET_VERB -url $url -Headers $headers } catch { Get-ExceptionResponseOrThrow $_ } $ProgressPreference = $tmp } } <# .SYNOPSIS Queries the DB, returns the HTTP response. The records will be within the Documents property of the result. .PARAMETER ResourceGroup Azure Resource Group of the database .PARAMETER Database The database name .PARAMETER Container The container name inside the database .PARAMETER Collection The collection name inside the container .PARAMETER Query The query as a string with optional parameters .PARAMETER Parameters [Optional] Parameters values used in the query. Accepts an array of name-value pairs or a hashtable. .PARAMETER SubscriptionId [Optional] The Azure Subscription Id. Default is the same as `az`. .PARAMETER DisableExtraFeatures Disables extra query features required to perform operations like aggregates, TOP, or DISTINCT. Should be used in case the support for these operations has a bug 😄. Default is false (extra features enabled). .EXAMPLE $> Search-CosmosDbRecords -Query "select * from c where c.id in (1, 2)" | Get-CosmosDbRecordContent | ConvertTo-Json [ { "id": 1, ... }, { "id": 2, ... }, ] .EXAMPLE $> Search-CosmosDbRecords -Query "select * from c where c.id = @id" -Parameters @(@{ name = "@id"; value = 1 }) | Get-CosmosDbRecordContent | ConvertTo-Json { "id": 1, ... }, .EXAMPLE $> Search-CosmosDbRecords -Query "select * from c where c.id = @id" -Parameters @{ "@id" = 1 } | Get-CosmosDbRecordContent | ConvertTo-Json { "id": 1, ... }, .EXAMPLE $> Search-CosmosDbRecords -Query "select count(1) as cnt, c.key from c group by c.key" | Get-CosmosDbRecordContent | % Payload | ConvertTo-Json [ { "cnt": { "item": 1234 }, "key": "key1" }, { "cnt": { "item": 5678 }, "key": "key2" } ] #> Function Search-CosmosDbRecords( [parameter(Mandatory = $true)][string]$ResourceGroup, [parameter(Mandatory = $true)][string]$Database, [parameter(Mandatory = $true)][string]$Container, [parameter(Mandatory = $true)][string]$Collection, [parameter(Mandatory = $true)][string]$Query, [parameter(Mandatory = $false)]$Parameters = $null, [parameter(Mandatory = $false)][string]$SubscriptionId = "", [parameter(Mandatory = $false)][switch]$DisableExtraFeatures = $false) { begin { $Parameters = @(Get-QueryParametersAsNameValuePairs $Parameters) $baseUrl = Get-BaseDatabaseUrl $Database $collectionsUrl = Get-CollectionsUrl $Container $Collection $docsUrl = "$collectionsUrl/$DOCS_TYPE" $url = "$baseUrl/$docsUrl" $now = Get-Time $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $POST_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now } process { if (!$DisableExtraFeatures) { return Search-CosmosDbRecordsWithExtraFeatures -ResourceGroup $ResourceGroup -Database $Database -Container $Container -Collection $Collection -Query $Query -Parameters $Parameters -SubscriptionId $SubscriptionId } try { $body = @{ query = $Query; parameters = $Parameters; } $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -isQuery $true -contentType "application/Query+json" $headers["x-ms-documentdb-query-enablecrosspartition"] = "true" Invoke-CosmosDbApiRequestWithContinuation -verb $POST_VERB -url $url -Body $body -Headers $headers } catch { Get-ExceptionResponseOrThrow $_ } } } Function Search-CosmosDbRecordsWithExtraFeatures ( [string]$ResourceGroup, [string]$Database, [string]$Container, [string]$Collection, [string]$Query, $Parameters, [string]$SubscriptionId ) { begin { $Parameters = @(Get-QueryParametersAsNameValuePairs $Parameters) $baseUrl = Get-BaseDatabaseUrl $Database $collectionsUrl = Get-CollectionsUrl $Container $Collection $docsUrl = "$collectionsUrl/$DOCS_TYPE" $url = "$baseUrl/$docsUrl" $now = Get-Time $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $POST_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now $allPartitionKeyRangesOrError = Get-PartitionKeyRangesOrError -ResourceGroup $ResourceGroup -Database $Database -Container $Container -Collection $Collection -SubscriptionId $SubscriptionId } process { if ($allPartitionKeyRangesOrError.ErrorRecord) { return Get-ExceptionResponseOrThrow $allPartitionKeyRangesOrError.ErrorRecord } try { $ranges = $allPartitionKeyRangesOrError.Ranges $body = @{ query = $Query; parameters = $Parameters; } $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -isQuery $true -contentType "application/Query+json" $headers += @{ "x-ms-documentdb-query-enablecrosspartition" = "true"; "x-ms-cosmos-supported-query-features" = "NonValueAggregate, Aggregate, Distinct, MultipleOrderBy, OffsetAndLimit, OrderBy, Top, CompositeAggregate, GroupBy, MultipleAggregates"; "x-ms-documentdb-query-enable-scan" = "true"; "x-ms-documentdb-query-parallelizecrosspartitionquery" = "true"; "x-ms-cosmos-is-query-plan-request" = "True"; } $queryPlan = Invoke-CosmosDbApiRequest -verb $POST_VERB -url $url -Body $body -Headers $headers | Get-CosmosDbRecordContent $rewrittenQuery = $queryPlan.QueryInfo.RewrittenQuery $searchQuery = if ($rewrittenQuery) { $rewrittenQuery } else { $Query }; $body = @{ query = $searchQuery; parameters = $Parameters; } $headers.Remove("x-ms-cosmos-is-query-plan-request") $partitionKeyRanges = if ($env:COSMOS_DB_FLAG_ENABLE_PARTITION_KEY_RANGE_SEARCHES -eq 1) { Get-FilteredPartitionKeyRangesForQuery -AllRanges $ranges -QueryRanges $queryPlan.QueryRanges } else { $ranges } foreach ($partitionKeyRange in $partitionKeyRanges) { $headers["x-ms-documentdb-partitionkeyrangeid"] = $partitionKeyRange.id Invoke-CosmosDbApiRequestWithContinuation -verb $POST_VERB -url $url -Body $body -Headers $headers } } catch { Get-ExceptionResponseOrThrow $_ } } } <# .SYNOPSIS Creates a single DB record, returns the HTTP response of the operation .PARAMETER Object Azure Resource Group of the database .PARAMETER ResourceGroup Azure Resource Group of the database .PARAMETER Database The database name .PARAMETER Container The container name inside the database .PARAMETER Collection The collection name inside the container .PARAMETER SubscriptionId [Optional] The Azure Subscription Id. Default is the same as `az`. .PARAMETER PartitionKey [Optional] The record's partition key. Default is the `id` property of `Object`. Required if using a custom partition strategy. .PARAMETER GetPartitionKeyBlock [Optional] Callback to get the partition key from the input object. Default is the `id` property of `Object`. Required if using a custom partition strategy. .EXAMPLE $> New-CosmosDbRecord -Object @{ id = 1234; key = value } ... StatusCode : 201 StatusDescription : Created Content : { ... } RawContent : ... Forms : {} Headers : { ... } Images : {} InputFields : {} Links : {} ParsedHtml : mshtml.HTMLDocumentClass RawContentLength : 257 .EXAMPLE $> New-CosmosDbRecord -Object @{ id = 1234; key = value } ... id : 1234 key : value .EXAMPLE $> $record | New-CosmosDbRecord -PartitionKey $record.PartitionKey ... .EXAMPLE $> $recordList | New-CosmosDbRecord -GetPartitionKeyBlock { param($r) $r.PartitionKey } ... #> Function New-CosmosDbRecord { [CmdletBinding(DefaultParameterSetName = 'ExplicitPartitionKey')] param ( [parameter(ValueFromPipeline = $true, Mandatory = $true)]$Object, [parameter(Mandatory = $true)][string]$ResourceGroup, [parameter(Mandatory = $true)][string]$Database, [parameter(Mandatory = $true)][string]$Container, [parameter(Mandatory = $true)][string]$Collection, [parameter(Mandatory = $false)][string]$SubscriptionId = "", [parameter(Mandatory = $false, ParameterSetName = "ExplicitPartitionKey")][string]$PartitionKey = "", [parameter(Mandatory = $false, ParameterSetName = "ParttionKeyCallback")]$GetPartitionKeyBlock = $null ) begin { $baseUrl = Get-BaseDatabaseUrl $Database $collectionsUrl = Get-CollectionsUrl $Container $Collection $docsUrl = "$collectionsUrl/$DOCS_TYPE" $url = "$baseUrl/$docsUrl" $now = Get-Time $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $POST_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now } process { try { $requestPartitionKey = if ($PartitionKey) { $PartitionKey } elseif ($GetPartitionKeyBlock) { Invoke-Command -ScriptBlock $GetPartitionKeyBlock -ArgumentList $Object } else { $Object.Id } $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -PartitionKey $requestPartitionKey Invoke-CosmosDbApiRequest -Verb $POST_VERB -Url $url -Body $Object -Headers $headers } catch { Get-ExceptionResponseOrThrow $_ } } } <# .SYNOPSIS Updates a single DB record, returns the HTTP response of the operation .PARAMETER Object The input record .PARAMETER ResourceGroup Azure Resource Group of the database .PARAMETER Database The database name .PARAMETER Container The container name inside the database .PARAMETER Collection The collection name inside the container .PARAMETER SubscriptionId [Optional] The Azure Subscription Id. Default is the same as `az`. .PARAMETER PartitionKey [Optional] The record's partition key. Default is the `id` property of `Object`. Required if using a custom partition strategy. .PARAMETER GetPartitionKeyBlock [Optional] Callback to get the partition key from the input object. Default is the `id` property of `Object`. Required if using a custom partition strategy. .EXAMPLE $> Update-CosmosDbRecord -Object @{ id = 1234; key = value } ... StatusCode : 200 StatusDescription : Ok Content : { ... } RawContent : ... Forms : {} Headers : { ... } Images : {} InputFields : {} Links : {} ParsedHtml : mshtml.HTMLDocumentClass RawContentLength : 271 .EXAMPLE $> Update-CosmosDbRecord -Object @{ id = 1234; key = value } ... | Get-CosmosDbRecordContent id : 1234 key : value .EXAMPLE $> $record | Update-CosmosDbRecord -PartitionKey $record.PartitionKey ... .EXAMPLE $> $recordList | Update-CosmosDbRecord -GetPartitionKeyBlock { param($r) $r.PartitionKey } ... #> Function Update-CosmosDbRecord { [CmdletBinding(DefaultParameterSetName = 'ExplicitPartitionKey')] param ( [parameter(ValueFromPipeline = $true, Mandatory = $true)]$Object, [parameter(Mandatory = $true)][string]$ResourceGroup, [parameter(Mandatory = $true)][string]$Database, [parameter(Mandatory = $true)][string]$Container, [parameter(Mandatory = $true)][string]$Collection, [parameter(Mandatory = $false)][string]$SubscriptionId = "", [parameter(Mandatory = $false, ParameterSetName = "ExplicitPartitionKey")][string]$PartitionKey = "", [parameter(Mandatory = $false, ParameterSetName = "ParttionKeyCallback")]$GetPartitionKeyBlock = $null ) begin { $baseUrl = Get-BaseDatabaseUrl $Database } process { try { $documentUrl = Get-DocumentsUrl $Container $Collection $Object.id $url = "$baseUrl/$documentUrl" $now = Get-Time $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $PUT_VERB -resourceType $DOCS_TYPE -resourceUrl $documentUrl -now $now $requestPartitionKey = if ($PartitionKey) { $PartitionKey } elseif ($GetPartitionKeyBlock) { Invoke-Command -ScriptBlock $GetPartitionKeyBlock -ArgumentList $Object } else { $Object.Id } $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -PartitionKey $requestPartitionKey Invoke-CosmosDbApiRequest -Verb $PUT_VERB -Url $url -Body $Object -Headers $headers } catch { Get-ExceptionResponseOrThrow $_ } } } <# .SYNOPSIS Deletes a single DB record, returns the HTTP response of the lookup .PARAMETER ResourceGroup Azure Resource Group of the database .PARAMETER Database The database name .PARAMETER Container The container name inside the database .PARAMETER Collection The collection name inside the container .PARAMETER RecordId The record's id .PARAMETER Object [Optional] The input record to extract the id from if `RecordId` is not set. .PARAMETER SubscriptionId [Optional] The Azure Subscription Id. Default is the same as `az`. .PARAMETER PartitionKey [Optional] The record's partition key. Default is the `id` property of `Object`. Required if using a custom partition strategy. .PARAMETER GetPartitionKeyBlock [Optional] Callback to get the partition key from the input object. Default is the `id` property of `Object`. Required if using a custom partition strategy. .EXAMPLE $> Remove-CosmosDbRecord ... StatusCode : 204 StatusDescription : No Content Content : RawContent : ... Forms : {} Headers : { ... } Images : {} InputFields : {} Links : {} ParsedHtml : mshtml.HTMLDocumentClass RawContentLength : 0 #> Function Remove-CosmosDbRecord { [CmdletBinding(DefaultParameterSetName = 'DefaultPartitionKey_ExplicitRecordId')] param ( [parameter(Mandatory = $true)][string]$ResourceGroup, [parameter(Mandatory = $true)][string]$Database, [parameter(Mandatory = $true)][string]$Container, [parameter(Mandatory = $true)][string]$Collection, [parameter(Mandatory = $false)][string]$SubscriptionId = "", [parameter(Mandatory = $true, ParameterSetName = "DefaultPartitionKey_ExplicitRecordId")] [parameter(Mandatory = $true, ParameterSetName = "ExplicitPartitionKey_ExplicitRecordId")] [parameter(Mandatory = $true, ParameterSetName = "CallbackPartitionKey_ExplicitRecordId")] [string]$RecordId, [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "DefaultPartitionKey_ObjectRecordId")] [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "ExplicitPartitionKey_ObjectRecordId")] [parameter(ValueFromPipeline = $true, Mandatory = $true, ParameterSetName = "CallbackPartitionKey_ObjectRecordId")] $Object, [parameter(Mandatory = $true, ParameterSetName = "ExplicitPartitionKey_ExplicitRecordId")] [parameter(Mandatory = $true, ParameterSetName = "ExplicitPartitionKey_ObjectRecordId")] [string]$PartitionKey = "", [parameter(Mandatory = $true, ParameterSetName = "CallbackPartitionKey_ObjectRecordId")] $GetPartitionKeyBlock = $null ) begin { $baseUrl = Get-BaseDatabaseUrl $Database } process { try { $id = if ($RecordId) { $RecordId } else { $Object.id } $documentUrl = Get-DocumentsUrl $Container $Collection $id $url = "$baseUrl/$documentUrl" $now = Get-Time $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $DELETE_VERB -resourceType $DOCS_TYPE -resourceUrl $documentUrl -now $now $requestPartitionKey = if ($PartitionKey) { $PartitionKey } elseif ($GetPartitionKeyBlock) { Invoke-Command -ScriptBlock $GetPartitionKeyBlock -ArgumentList $Object } else { $id } $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -PartitionKey $requestPartitionKey Invoke-CosmosDbApiRequest -Verb $DELETE_VERB -Url $url -Headers $headers } catch { Get-ExceptionResponseOrThrow $_ } } } Function Get-CosmosDbRecordContent([parameter(ValueFromPipeline)]$RecordResponse) { process { $code = [int]$RecordResponse.StatusCode $content = if ($RecordResponse.Content) { $RecordResponse.Content | ConvertFrom-Json } else { $null } if ($code -lt 300) { if ($RecordResponse.Content) { $content } else { $null } } elseif ($code -eq 404) { if ($content.Message -like "*Owner resource does not exist*") { throw "Database does not exist" } throw "Record not found" } elseif ($code -eq 429) { throw "Request rate limited" } else { $message = $content.Message throw "Request failed with status code $code with message`n`n$message" } } } Function Use-CosmosDbInternalFlag ( $enableFiddlerDebugging = $null, $enableCaching = $null, $enablePartitionKeyRangeSearches = $null ) { if ($null -ne $enableFiddlerDebugging) { $env:AZURE_CLI_DISABLE_CONNECTION_VERIFICATION = if ($enableFiddlerDebugging) { 1 } else { 0 } } if ($null -ne $enableCaching) { $env:COSMOS_DB_FLAG_ENABLE_CACHING = if ($enableCaching) { 1 } else { 0 } } if ($null -ne $enablePartitionKeyRangeSearches) { $env:COSMOS_DB_FLAG_ENABLE_PARTITION_KEY_RANGE_SEARCHES = if ($enablePartitionKeyRangeSearches) { 1 } else { 0 } } } Export-ModuleMember -Function "Get-CosmosDbRecord" Export-ModuleMember -Function "Get-AllCosmosDbRecords" Export-ModuleMember -Function "Search-CosmosDbRecords" Export-ModuleMember -Function "New-CosmosDbRecord" Export-ModuleMember -Function "Update-CosmosDbRecord" Export-ModuleMember -Function "Remove-CosmosDbRecord" Export-ModuleMember -Function "Get-CosmosDbRecordContent" Export-ModuleMember -Function "Use-CosmosDbInternalFlag" |