diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b553ca9..f5fbb000 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated `README.MD` to documentation to reduce focus on collections without partition keys - fixes [Issue #342](https://github.com/PlagueHO/CosmosDB/issues/342). +### Added + +- Added support for `Environment` parameter in `New-CosmosDbContext` to allow + using Azure US Government Cloud - fixes [Issue #322](https://github.com/PlagueHO/CosmosDB/issues/322). + ## [3.6.1] - 2020-03-19 ### Changed diff --git a/README.md b/README.md index 5b7eb6d9..b09e41e0 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ - [Getting Started](#getting-started) - [Working with Contexts](#working-with-contexts) - [Create a Context specifying the Key Manually](#create-a-context-specifying-the-key-manually) + - [Create a Context for a Cosmos DB in Azure US Government Cloud](#create-a-context-for-a-cosmos-db-in-azure-us-government-cloud) - [Use CosmosDB Module to Retrieve Key from Azure Management Portal](#use-cosmosdb-module-to-retrieve-key-from-azure-management-portal) - [Create a Context for a Cosmos DB Emulator](#create-a-context-for-a-cosmos-db-emulator) - [Create a Context from Resource Authorization Tokens](#create-a-context-from-resource-authorization-tokens) @@ -138,6 +139,16 @@ create a context variable: $cosmosDbContext = New-CosmosDbContext -Account 'MyAzureCosmosDB' -Database 'MyDatabase' -Key $primaryKey ``` +#### Create a Context for a Cosmos DB in Azure US Government Cloud + +Use the key secure string, Azure Cosmos DB account name and database to +create a context variable and set the `Environment` parameter to +`AzureUSGovernment`: + +```powershell +$cosmosDbContext = New-CosmosDbContext -Account 'MyAzureCosmosDB' -Database 'MyDatabase' -Key $primaryKey -Environment AzureUSGovernment +``` + #### Use CosmosDB Module to Retrieve Key from Azure Management Portal To create a context object so that the _CosmosDB PowerShell module_ diff --git a/docs/Get-CosmosDbUri.md b/docs/Get-CosmosDbUri.md index a7cd6ce8..7d309106 100644 --- a/docs/Get-CosmosDbUri.md +++ b/docs/Get-CosmosDbUri.md @@ -15,7 +15,8 @@ be sent to. ## SYNTAX ```powershell -Get-CosmosDbUri [-Account] [[-BaseUri] ] [] +Get-CosmosDbUri [-Account] [[-BaseUri] ] [[-Environment] ] + [] ``` ## DESCRIPTION @@ -41,6 +42,15 @@ PS C:\>$uri = Get-CosmosDbUri -Account 'MyAzureCosmosDB' -BaseUri 'localhost' Generates the URI for accessing a Cosmos DB emulator account on the localhost. +### EXAMPLE 3 + +```powershell +PS C:\>$uri = Get-CosmosDbUri -Account 'MyAzureCosmosDB' -Environment 'AzureUSGovernment' +``` + +Generates the URI for accessing a Cosmos DB account in the +US Government cloud. + ## PARAMETERS ### -Account @@ -67,7 +77,7 @@ If not specified it will default to 'documents.azure.com'. ```yaml Type: String -Parameter Sets: (All) +Parameter Sets: Uri Aliases: Required: False @@ -77,6 +87,27 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Environment + +This is the Azure environment hosting the Cosmos DB account. + +The supported values are: + +- AzureCloud +- AzureUSGovernment + +```yaml +Type: Environment +Parameter Sets: Environment +Aliases: + +Required: False +Position: Named +Default value: AzureCloud +Accept pipeline input: False +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. diff --git a/docs/New-CosmosDbContext.md b/docs/New-CosmosDbContext.md index 4a7dccda..c487d0f2 100644 --- a/docs/New-CosmosDbContext.md +++ b/docs/New-CosmosDbContext.md @@ -17,29 +17,32 @@ to connect to a Cosmos DB. ### Account (Default) ```powershell -New-CosmosDbContext -Account [-Database ] -Key [-KeyType ] - [-BackoffPolicy ] [] +New-CosmosDbContext -Account [-Database ] + -Key [-KeyType ] [-BackoffPolicy ] + [-Environment ] [] ``` ### AzureAccount ```powershell New-CosmosDbContext -Account [-Database ] -ResourceGroupName - [-MasterKeyType ] [-BackoffPolicy ] [] + [-MasterKeyType ] [-BackoffPolicy ] [-Environment ] + [] ``` ### Token ```powershell New-CosmosDbContext -Account [-Database ] -Token - [-BackoffPolicy ] [] + [-BackoffPolicy ] [-Environment ] [] ``` ### Emulator ```powershell -New-CosmosDbContext [-Database ] [-Key ] [-Emulator] [-Port ] [-URI ] - [-Token ] [-BackoffPolicy ] [] +New-CosmosDbContext [-Database ] [-Key ] [-Emulator] + [-Port ] [-URI ] [-Token ] + [-BackoffPolicy ] [] ``` ## DESCRIPTION @@ -106,6 +109,16 @@ PS C:\> $cosmosDbContext = New-CosmosDbContext -Emulator -URI 'mycosmosdb' -Key Creates a Cosmos DB context by using a Cosmos DB Emulator installed onto the machine 'mycosmosdb' with a custom key emulator key. +### EXAMPLE 6 + +```powershell +PS C:\> $primaryKey = ConvertTo-SecureString -String 'your master key' -AsPlainText -Force +PS C:\> $cosmosDbContext = New-CosmosDbContext -Account 'MyAzureGovCosmosDB' -Database 'MyDatabase' -Key $primaryKey -Environment 'AzureUSGovernment' +``` + +Creates a CosmosDB context specifying the master key manually connecting +to the Azure US Government cloud. + ## PARAMETERS ### -Account @@ -126,7 +139,7 @@ Accept wildcard characters: False ### -BackoffPolicy -This a Back-off Policy object that controls the retry behaviour for +This a Back-off Policy object that controls the retry behavior for requests using this context. ```yaml @@ -174,6 +187,27 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Environment + +This is the Azure environment hosting the Cosmos DB account. + +The supported values are: + +- AzureCloud +- AzureUSGovernment + +```yaml +Type: Environment +Parameter Sets: Account, AzureAccount, Token +Aliases: + +Required: False +Position: Named +Default value: AzureCloud +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Key The key to be used to access the Cosmos DB account or Cosmos DB emulator. diff --git a/source/Private/utils/Get-CosmosDbUri.ps1 b/source/Private/utils/Get-CosmosDbUri.ps1 index 74820572..c8f8600f 100644 --- a/source/Private/utils/Get-CosmosDbUri.ps1 +++ b/source/Private/utils/Get-CosmosDbUri.ps1 @@ -1,18 +1,35 @@ function Get-CosmosDbUri { - [CmdletBinding()] - [OutputType([uri])] + [CmdletBinding( + DefaultParameterSetName = 'Environment' + )] + [OutputType([System.Uri])] param ( [Parameter(Mandatory = $true)] [System.String] $Account, - [Parameter()] + [Parameter(ParameterSetName = 'Uri')] [System.String] - $BaseUri = 'documents.azure.com' + $BaseUri = 'documents.azure.com', + + [Parameter(ParameterSetName = 'Environment')] + [CosmosDB.Environment] + $Environment = [CosmosDB.Environment]::AzureCloud ) - return [uri]::new(('https://{0}.{1}' -f $Account, $BaseUri)) + if ($PSCmdlet.ParameterSetName -eq 'Environment') + { + switch ($Environment) + { + 'AzureUSGovernment' + { + $BaseUri = 'documents.azure.us' + } + } + } + + return [System.Uri]::new(('https://{0}.{1}' -f $Account, $BaseUri)) } diff --git a/source/Public/utils/New-CosmosDbContext.ps1 b/source/Public/utils/New-CosmosDbContext.ps1 index 33de7f2c..af5514d5 100644 --- a/source/Public/utils/New-CosmosDbContext.ps1 +++ b/source/Public/utils/New-CosmosDbContext.ps1 @@ -63,7 +63,13 @@ function New-CosmosDbContext [Parameter()] [ValidateNotNullOrEmpty()] [CosmosDB.BackoffPolicy] - $BackoffPolicy + $BackoffPolicy, + + [Parameter(ParameterSetName = 'Account')] + [Parameter(ParameterSetName = 'Token')] + [Parameter(ParameterSetName = 'AzureAccount')] + [CosmosDB.Environment] + $Environment = [CosmosDB.Environment]::AzureCloud ) switch ($PSCmdlet.ParameterSetName) @@ -92,7 +98,7 @@ function New-CosmosDbContext } catch { - $null = Connect-AzAccount + $null = Connect-AzAccount -Environment $Environment } $Key = Get-CosmosDbAccountMasterKey ` @@ -100,17 +106,17 @@ function New-CosmosDbContext -Name $Account ` -MasterKeyType $MasterKeyType - $BaseUri = (Get-CosmosDbUri -Account $Account) + $BaseUri = Get-CosmosDbUri -Account $Account -Environment $Environment } 'Account' { - $BaseUri = (Get-CosmosDbUri -Account $Account) + $BaseUri = Get-CosmosDbUri -Account $Account -Environment $Environment } 'Token' { - $BaseUri = (Get-CosmosDbUri -Account $Account) + $BaseUri = Get-CosmosDbUri -Account $Account -Environment $Environment } } diff --git a/source/classes/CosmosDB/CosmosDB.cs b/source/classes/CosmosDB/CosmosDB.cs index 1eb78d9f..bb24427f 100644 --- a/source/classes/CosmosDB/CosmosDB.cs +++ b/source/classes/CosmosDB/CosmosDB.cs @@ -1,6 +1,12 @@ using System; namespace CosmosDB { + public enum Environment { + AzureCloud, + AzureUSGovernment + } + + public class ContextToken { public System.String Resource; @@ -25,6 +31,7 @@ public class Context public System.String BaseUri; public CosmosDB.ContextToken[] Token; public CosmosDB.BackoffPolicy BackoffPolicy; + public CosmosDB.Environment Environment; } namespace IndexingPolicy { diff --git a/tests/Unit/CosmosDB.utils.Tests.ps1 b/tests/Unit/CosmosDB.utils.Tests.ps1 index a2430f26..099d5299 100644 --- a/tests/Unit/CosmosDB.utils.Tests.ps1 +++ b/tests/Unit/CosmosDB.utils.Tests.ps1 @@ -5,7 +5,12 @@ param () $ProjectPath = "$PSScriptRoot\..\.." | Convert-Path $ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{ ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and - $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + $(try + { Test-ModuleManifest $_.FullName -ErrorAction Stop + } + catch + { $false + } ) }).BaseName Import-Module -Name $ProjectName -Force @@ -24,6 +29,8 @@ InModuleScope $ProjectName { $script:testURI = 'emulatoruri.local' $script:testPort = '9999' $script:testBaseUri = 'documents.contoso.com' + $script:testBaseUriAzureCloud = 'documents.azure.com' + $script:testBaseUriAzureUsGov = 'documents.azure.us' $script:testDate = (Get-Date -Year 2017 -Month 11 -Day 29 -Hour 10 -Minute 45 -Second 10) $script:testUniversalDate = 'Tue, 28 Nov 2017 21:45:10 GMT' $script:testContext = [CosmosDb.Context] @{ @@ -226,7 +233,31 @@ console.log("done"); $script:result.Database | Should -Be $script:testDatabase $script:result.Key | Should -Be $script:testKeySecureString $script:result.KeyType | Should -Be 'master' - $script:result.BaseUri | Should -Be ('https://{0}.documents.azure.com/' -f $script:testAccount) + $script:result.BaseUri | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUriAzureCloud) + } + } + + Context 'When called with Account and Environment AzureUSGovernment parameters' { + $script:result = $null + + It 'Should not throw exception' { + $newCosmosDbContextParameters = @{ + Account = $script:testAccount + Database = $script:testDatabase + Key = $script:testKeySecureString + KeyType = 'master' + Environment = 'AzureUSGovernment' + } + + { $script:result = New-CosmosDbContext @newCosmosDbContextParameters } | Should -Not -Throw + } + + It 'Should return expected result' { + $script:result.Account | Should -Be $script:testAccount + $script:result.Database | Should -Be $script:testDatabase + $script:result.Key | Should -Be $script:testKeySecureString + $script:result.KeyType | Should -Be 'master' + $script:result.BaseUri | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUriAzureUsGov) } } @@ -258,7 +289,7 @@ console.log("done"); $script:result.Database | Should -Be $script:testDatabase $script:result.Key | Should -Be $script:testKeySecureString $script:result.KeyType | Should -Be 'master' - $script:result.BaseUri | Should -Be ('https://{0}.documents.azure.com/' -f $script:testAccount) + $script:result.BaseUri | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUriAzureCloud) $script:result.BackoffPolicy.MaxRetries | Should -Be $script:testMaxRetries $script:result.BackoffPolicy.Method | Should -Be $script:testMethod $script:result.BackoffPolicy.Delay | Should -Be $script:testDelay @@ -290,12 +321,54 @@ console.log("done"); $script:result.Database | Should -Be $script:testDatabase $script:result.KeyType | Should -Be 'master' $script:result.Key | Convert-CosmosDbSecureStringToString | Should -Be $script:testKey - $script:result.BaseUri | Should -Be ('https://{0}.documents.azure.com/' -f $script:testAccount) + $script:result.BaseUri | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUriAzureCloud) } It 'Should call expected mocks' { Assert-MockCalled -CommandName Get-AzContext -Exactly -Times 1 - Assert-MockCalled -CommandName Connect-AzAccount -Exactly -Times 1 + Assert-MockCalled -CommandName Connect-AzAccount ` + -ParameterFilter { $Environment -eq 'AzureCloud' } ` + -Exactly -Times 1 + Assert-MockCalled -CommandName Get-CosmosDbAccountMasterKey ` + -ParameterFilter { $MasterKeyType -eq 'PrimaryMasterKey' } ` + -Exactly -Times 1 + } + } + + Context 'When called with AzureAccount and Environment AzureUSGovernment parameters and not connected to Azure and PrimaryMasterKey requested' { + $script:result = $null + + Mock -CommandName Get-AzContext -MockWith { throw } + Mock -CommandName Connect-AzAccount + Mock ` + -CommandName Get-CosmosDbAccountMasterKey ` + -MockWith { $script:testKeySecureString } + + It 'Should not throw exception' { + $newCosmosDbContextParameters = @{ + Account = $script:testAccount + Database = $script:testDatabase + ResourceGroupName = $script:testResourceGroupName + MasterKeyType = 'PrimaryMasterKey' + Environment = 'AzureUSGovernment' + } + + { $script:result = New-CosmosDbContext @newCosmosDbContextParameters } | Should -Not -Throw + } + + It 'Should return expected result' { + $script:result.Account | Should -Be $script:testAccount + $script:result.Database | Should -Be $script:testDatabase + $script:result.KeyType | Should -Be 'master' + $script:result.Key | Convert-CosmosDbSecureStringToString | Should -Be $script:testKey + $script:result.BaseUri | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUriAzureUsGov) + } + + It 'Should call expected mocks' { + Assert-MockCalled -CommandName Get-AzContext -Exactly -Times 1 + Assert-MockCalled -CommandName Connect-AzAccount ` + -ParameterFilter { $Environment -eq 'AzureUSGovernment' } ` + -Exactly -Times 1 Assert-MockCalled -CommandName Get-CosmosDbAccountMasterKey ` -ParameterFilter { $MasterKeyType -eq 'PrimaryMasterKey' } ` -Exactly -Times 1 @@ -326,7 +399,7 @@ console.log("done"); $script:result.Database | Should -Be $script:testDatabase $script:result.KeyType | Should -Be 'master' $script:result.Key | Convert-CosmosDbSecureStringToString | Should -Be $script:testKey - $script:result.BaseUri | Should -Be ('https://{0}.documents.azure.com/' -f $script:testAccount) + $script:result.BaseUri | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUriAzureCloud) } It 'Should call expected mocks' { @@ -434,6 +507,8 @@ console.log("done"); $script:result.Token[0].Token | Convert-CosmosDbSecureStringToString | Should -Be $script:testToken } } + + } Describe 'Get-CosmosDbUri' -Tag 'Unit' { @@ -455,7 +530,7 @@ console.log("done"); It 'Should return expected result' { $script:result | Should -BeOfType uri - $script:result.ToString() | Should -Be ('https://{0}.documents.azure.com/' -f $script:testAccount) + $script:result.ToString() | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUriAzureCloud) } } @@ -477,6 +552,25 @@ console.log("done"); $script:result.ToString() | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUri) } } + + Context 'When called with Account and AzureUSGovernment Environment parameters' { + $script:result = $null + + It 'Should not throw exception' { + $GetCosmosDbUriParameters = @{ + Account = $script:testAccount + Environment = [CosmosDb.Environment]::AzureUSGovernment + Verbose = $true + } + + { $script:result = Get-CosmosDbUri @GetCosmosDbUriParameters } | Should -Not -Throw + } + + It 'Should return expected result' { + $script:result | Should -BeOfType uri + $script:result.ToString() | Should -Be ('https://{0}.{1}/' -f $script:testAccount, $script:testBaseUriAzureUsGov) + } + } } Describe 'ConvertTo-CosmosDbTokenDateString' -Tag 'Unit' { @@ -878,7 +972,7 @@ console.log("done"); Mock ` -CommandName Invoke-WebRequest ` - -MockWith { throw [System.Net.WebException] 'Test Exception'} + -MockWith { throw [System.Net.WebException] 'Test Exception' } $script:result = $null