Skip to content

Commit

Permalink
Adds CMK for CosmosDB Account (#8919)
Browse files Browse the repository at this point in the history
Co-authored-by: kt <[email protected]>
Co-authored-by: jackofallops <[email protected]>
  • Loading branch information
3 people authored Nov 11, 2020
1 parent 53946ff commit 9bc0a5a
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ func dataSourceArmCosmosDbAccount() *schema.Resource {
},
},

"key_vault_key_id": {
Type: schema.TypeString,
Computed: true,
},

"enable_multiple_write_locations": {
Type: schema.TypeBool,
Computed: true,
Expand Down Expand Up @@ -253,6 +258,10 @@ func dataSourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{})
d.Set("enable_free_tier", resp.EnableFreeTier)
d.Set("enable_automatic_failover", resp.EnableAutomaticFailover)

if v := props.KeyVaultKeyURI; v != nil {
d.Set("key_vault_key_id", resp.KeyVaultKeyURI)
}

if err = d.Set("consistency_policy", flattenAzureRmCosmosDBAccountConsistencyPolicy(resp.ConsistencyPolicy)); err != nil {
return fmt.Errorf("Error setting `consistency_policy`: %+v", err)
}
Expand Down
39 changes: 36 additions & 3 deletions azurerm/internal/services/cosmos/cosmosdb_account_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import (
"strings"
"time"

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/cosmos/common"

"github.com/Azure/azure-sdk-for-go/services/preview/cosmos-db/mgmt/2020-04-01-preview/documentdb"
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
Expand All @@ -22,6 +19,8 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/cosmos/common"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
Expand Down Expand Up @@ -117,6 +116,14 @@ func resourceArmCosmosDbAccount() *schema.Resource {
Default: false,
},

"key_vault_key_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
DiffSuppressFunc: diffSuppressIgnoreKeyVaultKeyVersion,
ValidateFunc: azure.ValidateKeyVaultChildIdVersionOptional,
},

"consistency_policy": {
Type: schema.TypeList,
Required: true,
Expand Down Expand Up @@ -407,6 +414,15 @@ func resourceArmCosmosDbAccountCreate(d *schema.ResourceData, meta interface{})
Tags: tags.Expand(t),
}

if keyVaultKeyIDRaw, ok := d.GetOk("key_vault_key_id"); ok {
keyVaultKey, err := azure.ParseKeyVaultChildIDVersionOptional(keyVaultKeyIDRaw.(string))
if err != nil {
return fmt.Errorf("could not parse Key Vault Key ID: %+v", err)
}
keyVaultKeyURI := fmt.Sprintf("%skeys/%s", keyVaultKey.KeyVaultBaseUrl, keyVaultKey.Name)
account.DatabaseAccountCreateUpdateProperties.KeyVaultKeyURI = utils.String(keyVaultKeyURI)
}

// additional validation on MaxStalenessPrefix as it varies depending on if the DB is multi region or not
consistencyPolicy := account.DatabaseAccountCreateUpdateProperties.ConsistencyPolicy
if len(geoLocations) > 1 && consistencyPolicy != nil && consistencyPolicy.DefaultConsistencyLevel == documentdb.BoundedStaleness {
Expand Down Expand Up @@ -596,6 +612,10 @@ func resourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) er
d.Set("enable_automatic_failover", resp.EnableAutomaticFailover)
}

if v := resp.KeyVaultKeyURI; v != nil {
d.Set("key_vault_key_id", resp.KeyVaultKeyURI)
}

if v := resp.EnableMultipleWriteLocations; v != nil {
d.Set("enable_multiple_write_locations", resp.EnableMultipleWriteLocations)
}
Expand Down Expand Up @@ -1020,3 +1040,16 @@ func resourceAzureRMCosmosDBAccountVirtualNetworkRuleHash(v interface{}) int {

return hashcode.String(buf.String())
}

func diffSuppressIgnoreKeyVaultKeyVersion(k, old, new string, d *schema.ResourceData) bool {
oldKey, err := azure.ParseKeyVaultChildIDVersionOptional(old)
if err != nil {
return false
}
newKey, err := azure.ParseKeyVaultChildIDVersionOptional(new)
if err != nil {
return false
}

return (oldKey.KeyVaultBaseUrl == newKey.KeyVaultBaseUrl) && (oldKey.Name == newKey.Name)
}
127 changes: 127 additions & 0 deletions azurerm/internal/services/cosmos/cosmosdb_account_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,29 @@ func TestAccAzureRMCosmosDBAccount_basic_parse_strong(t *testing.T) {
testAccAzureRMCosmosDBAccount_basicWith(t, documentdb.MongoDB, documentdb.Strong)
}

func TestAccAzureRMCosmosDBAccount_key_vault_uri(t *testing.T) {
testAccAzureRMCosmosDBAccount_key_vault_uri(t, documentdb.MongoDB, documentdb.Strong)
}

func testAccAzureRMCosmosDBAccount_key_vault_uri(t *testing.T, kind documentdb.DatabaseAccountKind, consistency documentdb.DefaultConsistencyLevel) {
data := acceptance.BuildTestData(t, "azurerm_cosmosdb_account", "test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMCosmosDBAccountDestroy,
Steps: []resource.TestStep{
{
Config: checkAccAzureRMCosmosDBAccount_key_vault_uri(data, kind, consistency),
Check: resource.ComposeAggregateTestCheckFunc(
checkAccAzureRMCosmosDBAccount_basic(data, consistency, 1),
),
},
data.ImportStep(),
},
})
}

func testAccAzureRMCosmosDBAccount_basicWith(t *testing.T, kind documentdb.DatabaseAccountKind, consistency documentdb.DefaultConsistencyLevel) {
data := acceptance.BuildTestData(t, "azurerm_cosmosdb_account", "test")

Expand Down Expand Up @@ -993,3 +1016,107 @@ func checkAccAzureRMCosmosDBAccount_basic(data acceptance.TestData, consistency
resource.TestCheckResourceAttrSet(data.ResourceName, "secondary_readonly_key"),
)
}

func checkAccAzureRMCosmosDBAccount_key_vault_uri(data acceptance.TestData, kind documentdb.DatabaseAccountKind, consistency documentdb.DefaultConsistencyLevel) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-cosmos-%d"
location = "%s"
}
data "azuread_service_principal" "cosmosdb" {
display_name = "Azure Cosmos DB"
}
data "azurerm_client_config" "current" {}
resource "azurerm_key_vault" "test" {
name = "acctestkv-%s"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "premium"
purge_protection_enabled = true
soft_delete_enabled = true
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = [
"list",
"create",
"delete",
"get",
"update",
]
secret_permissions = [
"get",
"delete",
"set",
]
}
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azuread_service_principal.cosmosdb.id
key_permissions = [
"list",
"create",
"delete",
"get",
"update",
"unwrapKey",
"wrapKey",
]
secret_permissions = [
"get",
"delete",
"set",
]
}
}
resource "azurerm_key_vault_key" "test" {
name = "key-%s"
key_vault_id = azurerm_key_vault.test.id
key_type = "RSA"
key_size = 2048
key_opts = [
"decrypt",
"encrypt",
"sign",
"unwrapKey",
"verify",
"wrapKey",
]
}
resource "azurerm_cosmosdb_account" "test" {
name = "acctest-ca-%d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
offer_type = "Standard"
kind = "%s"
key_vault_key_id = azurerm_key_vault_key.test.id
consistency_policy {
consistency_level = "%s"
}
geo_location {
location = azurerm_resource_group.test.location
failover_priority = 0
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString, data.RandomInteger, string(kind), string(consistency))
}
3 changes: 3 additions & 0 deletions examples/cosmos-db/customer-managed-key/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Example: CosmosDB with CMK (Customer Managed Key) encryption

This example provisions a CosmosDB Account in a single region that uses a generated key stored in a Key Vault for encryption.
83 changes: 83 additions & 0 deletions examples/cosmos-db/customer-managed-key/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "example" {
name = "${var.prefix}-resources"
location = var.location
}

data "azuread_service_principal" "cosmosdb" {
display_name = "Azure Cosmos DB"
}

data "azurerm_client_config" "current" {}

resource "azurerm_key_vault" "example" {
name = "${var.prefix}kv"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "premium"

purge_protection_enabled = true
soft_delete_enabled = true

access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id

key_permissions = [
"list",
"create",
"delete",
"get",
"update",
]

}

access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azuread_service_principal.cosmosdb.id

key_permissions = [
"get",
"unwrapKey",
"wrapKey",
]
}
}

resource "azurerm_key_vault_key" "example" {
name = "${var.prefix}key"
key_vault_id = azurerm_key_vault.example.id
key_type = "RSA"
key_size = 3072

key_opts = [
"decrypt",
"encrypt",
"wrapKey",
"unwrapKey",
]
}

resource "azurerm_cosmosdb_account" "example" {
name = "${var.prefix}-cosmosdb"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
offer_type = "Standard"
kind = "MongoDB"
key_vault_key_id = azurerm_key_vault_key.example.id

consistency_policy {
consistency_level = "Strong"
}

geo_location {
prefix = "${var.prefix}-customid"
location = azurerm_resource_group.example.location
failover_priority = 0
}
}
23 changes: 23 additions & 0 deletions examples/cosmos-db/customer-managed-key/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
output "cosmos-db-id" {
value = "${azurerm_cosmosdb_account.example.id}"
}

output "cosmos-db-endpoint" {
value = "${azurerm_cosmosdb_account.example.endpoint}"
}

output "cosmos-db-endpoints_read" {
value = "${azurerm_cosmosdb_account.example.read_endpoints}"
}

output "cosmos-db-endpoints_write" {
value = "${azurerm_cosmosdb_account.example.write_endpoints}"
}

output "cosmos-db-primary_key" {
value = "${azurerm_cosmosdb_account.example.primary_key}"
}

output "cosmos-db-secondary_key" {
value = "${azurerm_cosmosdb_account.example.secondary_key}"
}
7 changes: 7 additions & 0 deletions examples/cosmos-db/customer-managed-key/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
variable "prefix" {
description = "The prefix which should be used for all resources in this example"
}

variable "location" {
description = "The Azure Region in which all resources in this example should be created."
}
4 changes: 4 additions & 0 deletions website/docs/d/cosmosdb_account.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ The following attributes are exported:

* `kind` - The Kind of the CosmosDB account.

* `key_vault_key_id` - The Key Vault key URI for CMK encryption.

~> **NOTE:** The CosmosDB service always uses the latest version of the specified key.

* `ip_range_filter` - The current IP Filter for this CosmosDB account

* `enable_free_tier` - If Free Tier pricing option is enabled for this CosmosDB Account.
Expand Down
4 changes: 4 additions & 0 deletions website/docs/r/cosmosdb_account.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ The following arguments are supported:

* `is_virtual_network_filter_enabled` - (Optional) Enables virtual network filtering for this Cosmos DB account.

* `key_vault_key_id` - (Optional) A Key Vault Key ID for CMK encryption. Changing this forces a new resource to be created.

~> **NOTE:** The CosmosDB service always uses the latest version of the specified key, so terraform ignores the version specified in the Key Vault Key ID.

* `virtual_network_rule` - (Optional) Specifies a `virtual_network_rules` resource, used to define which subnets are allowed to access this CosmosDB account.

* `enable_multiple_write_locations` - (Optional) Enable multi-master support for this Cosmos DB account.
Expand Down

0 comments on commit 9bc0a5a

Please sign in to comment.