Skip to content

Add readonly key support #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 49 additions & 49 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,61 @@ name: CI

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]

workflow_dispatch:

jobs:
run-tests:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v1

- name: test module
id: test_module
uses: zyborg/pester-tests-report@v1
with:
include_paths: tests
report_name: cosmos-db tests
report_title: cosmos-db tests
gist_name: pester-tests-report.md
github_token: ${{ secrets.GITHUB_TOKEN }}
gist_token: ${{ secrets.GIST_TOKEN }}
gist_badge_label: Tests %ExecutedAt%
- name: checkout
uses: actions/checkout@v1

- name: dump results
shell: pwsh
run: |
ipmo GitHubActions
$test_results_path = '${{ steps.test_module.outputs.test_results_path }}'
$error_message = '${{ steps.test_module.outputs.error_message }}'
$error_clixml_path = '${{ steps.test_module.outputs.error_clixml_path }}'
$result_clixml_path = '${{ steps.test_module.outputs.result_clixml_path }}'
$result_value = '${{ steps.test_module.outputs.result_value }}'
$total_count = '${{ steps.test_module.outputs.total_count }}'
$passed_count = '${{ steps.test_module.outputs.passed_count }}'
$failed_count = '${{ steps.test_module.outputs.failed_count }}'
Write-ActionInfo "Found these outputs from [test_module]:"
Write-ActionInfo " * test_results_path = $test_results_path"
Write-ActionInfo " * error_message = $error_message"
Write-ActionInfo " * error_clixml_path = $error_clixml_path"
Write-ActionInfo " * result_clixml_path = $result_clixml_path"
Write-ActionInfo " * result_value = $result_value"
Write-ActionInfo " * total_count = $total_count"
Write-ActionInfo " * passed_count = $passed_count"
Write-ActionInfo " * failed_count = $failed_count"
if ($error_clixml_path) {
$er = Import-Clixml -Path $error_clixml_path
Write-ActionInfo "Loaded up the ErrorRecord:"
$er
$er.Exception
}
if ($result_clixml_path) {
$pr = Import-Clixml -Path $result_clixml_path
Write-ActionInfo "Loaded up the Pester Result:"
$pr
}
exit $failed_count
- name: test module
id: test_module
uses: cmbrose/pester-tests-report@v1
with:
include_paths: tests
report_name: cosmos-db tests
report_title: cosmos-db tests
gist_name: pester-tests-report.md
github_token: ${{ secrets.GITHUB_TOKEN }}
gist_token: ${{ secrets.GIST_TOKEN }}
gist_badge_label: Tests %ExecutedAt%

- name: dump results
shell: pwsh
run: |
ipmo GitHubActions
$test_results_path = '${{ steps.test_module.outputs.test_results_path }}'
$error_message = '${{ steps.test_module.outputs.error_message }}'
$error_clixml_path = '${{ steps.test_module.outputs.error_clixml_path }}'
$result_clixml_path = '${{ steps.test_module.outputs.result_clixml_path }}'
$result_value = '${{ steps.test_module.outputs.result_value }}'
$total_count = '${{ steps.test_module.outputs.total_count }}'
$passed_count = '${{ steps.test_module.outputs.passed_count }}'
$failed_count = '${{ steps.test_module.outputs.failed_count }}'
Write-ActionInfo "Found these outputs from [test_module]:"
Write-ActionInfo " * test_results_path = $test_results_path"
Write-ActionInfo " * error_message = $error_message"
Write-ActionInfo " * error_clixml_path = $error_clixml_path"
Write-ActionInfo " * result_clixml_path = $result_clixml_path"
Write-ActionInfo " * result_value = $result_value"
Write-ActionInfo " * total_count = $total_count"
Write-ActionInfo " * passed_count = $passed_count"
Write-ActionInfo " * failed_count = $failed_count"
if ($error_clixml_path) {
$er = Import-Clixml -Path $error_clixml_path
Write-ActionInfo "Loaded up the ErrorRecord:"
$er
$er.Exception
}
if ($result_clixml_path) {
$pr = Import-Clixml -Path $result_clixml_path
Write-ActionInfo "Loaded up the Pester Result:"
$pr
}
exit $failed_count
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "PowerShell: Run Pester Tests",
"type": "PowerShell",
"request": "launch",
"script": "Invoke-Pester",
"createTemporaryIntegratedConsole": true,
"attachDotnetDebugger": true
}
]
}
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,26 @@ Remove-CosmosDbRecord ...
| PartitionKey | The partition key of the resource | No - defaults to `Id`<br/>Must be set if the collection uses a different parition scheme |
| GetPartitionKeyBlock | Callback to get the `PartitionKey` from `Object` - useful in pipelines | No - used only if `PartitionKey` is not set |

### Use-CosmosDbReadonlyKeys

Enables or disables using readonly DB keys in commands. By default this is disabled (i.e. writable keys are used).

#### Examples

```powershell
# Enable using readonly keys
Use-CosmosDbReadonlyKeys

# Disable using readonly keys (use writable keys)
Use-CosmosDbReadonlyKeys -Disable
```

#### Parameters

| Name | Usage | Required |
| - | - | - |
| Disable | Disables readonly keys if set (enables writable keys) | No - default is `$false` (which will enable readonly keys) |

### Use-CosmosDbInternalFlag

Enables or disables internal flags in the module, normally should only be used for debugging or dogfooding
Expand Down
4 changes: 2 additions & 2 deletions cosmos-db/cosmos-db.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# RootModule = ''

# Version number of this module.
ModuleVersion = '1.15'
ModuleVersion = '1.16'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down Expand Up @@ -71,7 +71,7 @@
FunctionsToExport = 'Get-CosmosDbRecord', 'Get-AllCosmosDbRecords',
'Search-CosmosDbRecords', 'New-CosmosDbRecord',
'Update-CosmosDbRecord', 'Remove-CosmosDbRecord',
'Get-CosmosDbRecordContent', 'Use-CosmosDbInternalFlag'
'Get-CosmosDbRecordContent', 'Use-CosmosDbReadonlyKeys', 'Use-CosmosDbInternalFlag'

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = '*'
Expand Down
75 changes: 56 additions & 19 deletions cosmos-db/cosmos-db.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Function Get-DocumentsUrl([string]$Container, [string]$Collection, [string]$Reco
$encodedRecordId = [uri]::EscapeDataString($RecordId)

return @{
ApiUrl = "$collectionsUrl/$DOCS_TYPE/$encodedRecordId";
ApiUrl = "$collectionsUrl/$DOCS_TYPE/$encodedRecordId";
ResourceUrl = "$collectionsUrl/$DOCS_TYPE/$RecordId";
}
}
Expand Down Expand Up @@ -61,20 +61,36 @@ Function Set-CacheValue([string]$key, $value, [hashtable]$cache, [int]$expiratio
}

Function Get-Base64Masterkey([string]$ResourceGroup, [string]$Database, [string]$SubscriptionId) {
$cacheKey = "$SubscriptionId/$ResourceGroup/$Database"
$readonly = $env:COSMOS_DB_FLAG_ENABLE_READONLY_KEYS -eq 1

$cacheKey = "$SubscriptionId/$ResourceGroup/$Database/$readonly"
$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
$masterKey = Get-Base64MasterkeyWithoutCaching -ResourceGroup $ResourceGroup -Database $Database -SubscriptionId $SubscriptionId -Readonly $readonly

Set-CacheValue -Key $cacheKey -Value $masterKey -Cache $MASTER_KEY_CACHE -ExpirationHours 6

$masterKey
}

# This is just to support testing caching with Get-Base64Masterkey and isn't meant to be used directly
Function Get-Base64MasterkeyWithoutCaching([string]$ResourceGroup, [string]$Database, [string]$SubscriptionId, [bool]$Readonly) {
$query = if ($readonly) {
"primaryReadonlyMasterKey"
}
else {
$masterKey = az cosmosdb keys list --name $Database --query primaryMasterKey --output tsv --resource-group $ResourceGroup
"primaryMasterKey"
}

Set-CacheValue -Key $cacheKey -Value $masterKey -Cache $MASTER_KEY_CACHE -ExpirationHours 6
if ($SubscriptionId) {
$masterKey = az cosmosdb keys list --name $Database --query $query --output tsv --resource-group $ResourceGroup --subscription $SubscriptionId
}
else {
$masterKey = az cosmosdb keys list --name $Database --query $query --output tsv --resource-group $ResourceGroup
}

$masterKey
}
Expand Down Expand Up @@ -140,9 +156,9 @@ Function Get-CommonHeaders([string]$now, [string]$encodedAuthString, [string]$co
$headers["x-ms-documentdb-partitionkey"] = "[`"$PartitionKey`"]"
}

if ($Etag) {
$headers["If-Match"] = $Etag
}
if ($Etag) {
$headers["If-Match"] = $Etag
}

$headers
}
Expand Down Expand Up @@ -172,15 +188,16 @@ Function Get-ExceptionResponseOrThrow($err) {

if ($err.Exception.Response) {
$msg = @{
StatusCode = $err.Exception.Response.StatusCode;
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 {
}
else {
# In Desktop we can re-read the content stream
$result = $err.Exception.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($result)
Expand All @@ -191,7 +208,8 @@ Function Get-ExceptionResponseOrThrow($err) {
}

return [PSCustomObject]$msg
} else {
}
else {
throw $err.Exception
}
}
Expand Down Expand Up @@ -802,7 +820,8 @@ Function Update-CosmosDbRecord {

if ($EnforceOptimisticConcurrency) {
$headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -PartitionKey $requestPartitionKey -Etag $Object._etag
} else {
}
else {
$headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -PartitionKey $requestPartitionKey
}

Expand Down Expand Up @@ -911,11 +930,12 @@ Function Get-CosmosDbRecordContent([parameter(ValueFromPipeline)]$RecordResponse
process {
$code = [int]$RecordResponse.StatusCode
$content =
if ($RecordResponse.Content) {
$RecordResponse.Content | ConvertFrom-Json
} else {
$null
}
if ($RecordResponse.Content) {
$RecordResponse.Content | ConvertFrom-Json
}
else {
$null
}

if ($code -lt 300) {
if ($RecordResponse.Content) {
Expand All @@ -925,6 +945,12 @@ Function Get-CosmosDbRecordContent([parameter(ValueFromPipeline)]$RecordResponse
$null
}
}
elseif ($code -eq 401) {
if ($env:COSMOS_DB_FLAG_ENABLE_READONLY_KEYS -eq 1) {
throw "Unauthorized (used a readonly key)"
}
throw "Unauthorized"
}
elseif ($code -eq 404) {
if ($content.Message -like "*Owner resource does not exist*") {
throw "Database does not exist"
Expand All @@ -935,6 +961,7 @@ Function Get-CosmosDbRecordContent([parameter(ValueFromPipeline)]$RecordResponse
elseif ($code -eq 429) {
throw "Request rate limited"
}

else {
$message = $content.Message
throw "Request failed with status code $code with message`n`n$message"
Expand All @@ -961,6 +988,14 @@ Function Use-CosmosDbInternalFlag
}
}

Function Use-CosmosDbReadonlyKeys
(
[switch]$Disable
) {
$env:COSMOS_DB_FLAG_ENABLE_READONLY_KEYS = if ($Disable) { 0 } else { 1 }
}


Export-ModuleMember -Function "Get-CosmosDbRecord"
Export-ModuleMember -Function "Get-AllCosmosDbRecords"

Expand All @@ -974,4 +1009,6 @@ Export-ModuleMember -Function "Remove-CosmosDbRecord"

Export-ModuleMember -Function "Get-CosmosDbRecordContent"

Export-ModuleMember -Function "Use-CosmosDbInternalFlag"
Export-ModuleMember -Function "Use-CosmosDbReadonlyKeys"

Export-ModuleMember -Function "Use-CosmosDbInternalFlag"
Loading
Loading