Skip to content
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

azurerm_cosmosdb_account: Fix submitted key_vault_key_id values #10420

Merged
merged 12 commits into from
Feb 10, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func resourceCosmosDbAccount() *schema.Resource {
Optional: true,
ForceNew: true,
DiffSuppressFunc: diffSuppressIgnoreKeyVaultKeyVersion,
ValidateFunc: keyVaultValidate.NestedItemIdWithOptionalVersion,
ValidateFunc: keyVaultValidate.VersionlessNestedItemId,
},

"consistency_policy": {
Expand Down
17 changes: 13 additions & 4 deletions azurerm/internal/services/cosmos/cosmosdb_account_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,11 @@ resource "azurerm_cosmosdb_account" "test" {
func (CosmosDBAccountResource) key_vault_uri(data acceptance.TestData, kind documentdb.DatabaseAccountKind, consistency documentdb.DefaultConsistencyLevel) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
features {
key_vault {
purge_soft_delete_on_destroy = false
}
}
}

resource "azurerm_resource_group" "test" {
Expand All @@ -1154,8 +1158,9 @@ resource "azurerm_key_vault" "test" {
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"

purge_protection_enabled = true
soft_delete_enabled = true
purge_protection_enabled = true
soft_delete_enabled = true
soft_delete_retention_days = 7

access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
Expand Down Expand Up @@ -1221,7 +1226,11 @@ resource "azurerm_cosmosdb_account" "test" {
resource_group_name = azurerm_resource_group.test.name
offer_type = "Standard"
kind = "%s"
key_vault_key_id = azurerm_key_vault_key.test.id
key_vault_key_id = azurerm_key_vault_key.test.versionless_id

capabilities {
name = "EnableMongo"
}

consistency_policy {
consistency_level = "%s"
Expand Down
7 changes: 7 additions & 0 deletions azurerm/internal/services/keyvault/key_vault_key_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/base64"
"fmt"
"log"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
Expand Down Expand Up @@ -130,6 +131,11 @@ func resourceKeyVaultKey() *schema.Resource {
Computed: true,
},

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

"n": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -413,6 +419,7 @@ func resourceKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error {

// Computed
d.Set("version", id.Version)
d.Set("versionless_id", fmt.Sprintf("%s/%s/%s", strings.TrimSuffix(id.KeyVaultBaseUrl, "/"), id.NestedItemType, id.Name))

return tags.FlattenAndSet(d, resp.Tags)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func TestAccKeyVaultKey_complete(t *testing.T) {
check.That(data.ResourceName).Key("expiration_date").HasValue("2021-01-01T01:02:03Z"),
check.That(data.ResourceName).Key("tags.%").HasValue("1"),
check.That(data.ResourceName).Key("tags.hello").HasValue("world"),
check.That(data.ResourceName).Key("versionless_id").HasValue(fmt.Sprintf("https://acctestkv-%s.vault.azure.net/keys/key-%s", data.RandomString, data.RandomString)),
),
},
data.ImportStep("key_size"),
Expand Down
10 changes: 9 additions & 1 deletion azurerm/internal/services/keyvault/parse/nested_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,15 @@ func NewNestedItemID(keyVaultBaseUrl, nestedItemType, name, version string) (*Ne

func (n NestedItemId) ID() string {
// example: https://tharvey-keyvault.vault.azure.net/type/bird/fdf067c93bbb4b22bff4d8b7a9a56217
return fmt.Sprintf("%s/%s/%s/%s", n.KeyVaultBaseUrl, n.NestedItemType, n.Name, n.Version)
segments := []string{
strings.TrimSuffix(n.KeyVaultBaseUrl, "/"),
n.NestedItemType,
n.Name,
}
if n.Version != "" {
segments = append(segments, n.Version)
}
return strings.TrimSuffix(strings.Join(segments, "/"), "/")
}

// ParseNestedItemID parses a Key Vault Nested Item ID (such as a Certificate, Key or Secret)
Expand Down
20 changes: 14 additions & 6 deletions azurerm/internal/services/keyvault/parse/nested_item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ func TestParseNestedItemID(t *testing.T) {
for _, tc := range cases {
secretId, err := ParseNestedItemID(tc.Input)
if err != nil {
if !tc.ExpectError {
t.Fatalf("Got error for ID '%s': %+v", tc.Input, err)
if tc.ExpectError {
continue
}

return
t.Fatalf("Got error for ID '%s': %+v", tc.Input, err)
}

if secretId == nil {
Expand All @@ -122,6 +122,10 @@ func TestParseNestedItemID(t *testing.T) {
if tc.Expected.Version != secretId.Version {
t.Fatalf("Expected 'Version' to be '%s', got '%s' for ID '%s'", tc.Expected.Version, secretId.Version, tc.Input)
}

if tc.Input != secretId.ID() {
t.Fatalf("Expected 'ID()' to be '%s', got '%s'", tc.Input, secretId.ID())
}
}
}

Expand Down Expand Up @@ -184,11 +188,11 @@ func TestParseOptionallyVersionedNestedItemID(t *testing.T) {
for _, tc := range cases {
secretId, err := ParseOptionallyVersionedNestedItemID(tc.Input)
if err != nil {
if !tc.ExpectError {
t.Fatalf("Got error for ID '%s': %+v", tc.Input, err)
if tc.ExpectError {
continue
}

return
t.Fatalf("Got error for ID '%s': %+v", tc.Input, err)
}

if secretId == nil {
Expand All @@ -206,5 +210,9 @@ func TestParseOptionallyVersionedNestedItemID(t *testing.T) {
if tc.Expected.Version != secretId.Version {
t.Fatalf("Expected 'Version' to be '%s', got '%s' for ID '%s'", tc.Expected.Version, secretId.Version, tc.Input)
}

if tc.Input != secretId.ID() {
t.Fatalf("Expected 'ID()' to be '%s', got '%s'", tc.Input, secretId.ID())
}
}
}
24 changes: 24 additions & 0 deletions azurerm/internal/services/keyvault/validate/nested_item_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ func NestedItemId(i interface{}, k string) (warnings []string, errors []error) {
return warnings, errors
}

func VersionlessNestedItemId(i interface{}, k string) (warnings []string, errors []error) {
if warnings, errors = validation.StringIsNotEmpty(i, k); len(errors) > 0 {
return warnings, errors
}

v, ok := i.(string)
if !ok {
errors = append(errors, fmt.Errorf("Expected %s to be a string!", k))
return warnings, errors
}

id, err := keyVaultParse.ParseOptionallyVersionedNestedItemID(v)
if err != nil {
errors = append(errors, fmt.Errorf("parsing %q: %s", v, err))
return warnings, errors
}

if id.Version != "" {
errors = append(errors, fmt.Errorf("expected %s to not have a version", k))
}

return warnings, errors
}

func NestedItemIdWithOptionalVersion(i interface{}, k string) (warnings []string, errors []error) {
if warnings, errors = validation.StringIsNotEmpty(i, k); len(errors) > 0 {
return warnings, errors
Expand Down
53 changes: 53 additions & 0 deletions azurerm/internal/services/keyvault/validate/nested_item_id_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,59 @@ func TestNestedItemId(t *testing.T) {
}
}

func TestVersionlessNestedItemId(t *testing.T) {
cases := []struct {
Input string
ExpectError bool
}{
{
Input: "",
ExpectError: true,
},
{
Input: "https://my-keyvault.vault.azure.net/secrets",
ExpectError: true,
},
{
Input: "https://my-keyvault.vault.azure.net/secrets/bird",
ExpectError: false,
},
{
Input: "https://my-keyvault.vault.azure.net/secrets/bird/fdf067c93bbb4b22bff4d8b7a9a56217",
ExpectError: true,
},
{
Input: "https://my-keyvault.vault.azure.net/certificates/hello/world",
ExpectError: true,
},
{
Input: "https://my-keyvault.vault.azure.net/keys/castle/1492",
ExpectError: true,
},
{
Input: "https://my-keyvault.vault.azure.net/secrets/bird/fdf067c93bbb4b22bff4d8b7a9a56217/XXX",
ExpectError: true,
},
}

for _, tc := range cases {
warnings, err := VersionlessNestedItemId(tc.Input, "example")
if err != nil {
if tc.ExpectError {
continue
}

t.Fatalf("Got error for input %q: %+v", tc.Input, err)
}

if tc.ExpectError && len(warnings) == 0 {
t.Fatalf("Got no errors for input %q but expected some", tc.Input)
} else if !tc.ExpectError && len(warnings) > 0 {
t.Fatalf("Got %d errors for input %q when didn't expect any", len(warnings), tc.Input)
}
}
}

func TestNestedItemIdWithOptionalVersion(t *testing.T) {
cases := []struct {
Input string
Expand Down
2 changes: 2 additions & 0 deletions website/docs/d/key_vault_key.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ The following attributes are exported:

* `version` - The current version of the Key Vault Key.

* `versionless_id` - The Base ID of the Key Vault Key.


## Timeouts

Expand Down
5 changes: 3 additions & 2 deletions website/docs/r/cosmosdb_account.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,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.
* `key_vault_key_id` - (Optional) A versionless Key Vault Key ID for CMK encryption. Changing this forces a new resource to be created.

~> **NOTE:** When referencing an `azurerm_key_vault_key` resource, use `versionless_id` instead of `id`

~> **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.
~> **NOTE:** In order to use a `Custom Key` from Key Vault for encryption you must grant Azure Cosmos DB Service access to your key vault. For instuctions on how to configure your Key Vault correctly please refer to the [product documentation](https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-setup-cmk#add-an-access-policy-to-your-azure-key-vault-instance)

* `virtual_network_rule` - (Optional) Specifies a `virtual_network_rules` resource, used to define which subnets are allowed to access this CosmosDB account.
Expand Down
1 change: 1 addition & 0 deletions website/docs/r/key_vault_key.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ The following attributes are exported:

* `id` - The Key Vault Key ID.
* `version` - The current version of the Key Vault Key.
* `versionless_id` - The Base ID of the Key Vault Key.
* `n` - The RSA modulus of this Key Vault Key.
* `e` - The RSA public exponent of this Key Vault Key.
* `x` - The EC X component of this Key Vault Key.
Expand Down