diff --git a/azurerm/helpers/azure/key_vault_child.go b/azurerm/helpers/azure/key_vault_child.go index 386a4e7e89502..d352eda26aaec 100644 --- a/azurerm/helpers/azure/key_vault_child.go +++ b/azurerm/helpers/azure/key_vault_child.go @@ -42,6 +42,38 @@ func ParseKeyVaultChildID(id string) (*KeyVaultChildID, error) { return &childId, nil } +func ParseKeyVaultChildIDVersionOptional(id string) (*KeyVaultChildID, error) { + // example: https://tharvey-keyvault.vault.azure.net/type/bird/fdf067c93bbb4b22bff4d8b7a9a56217 + idURL, err := url.ParseRequestURI(id) + if err != nil { + return nil, fmt.Errorf("Cannot parse Azure KeyVault Child Id: %s", err) + } + + path := idURL.Path + + path = strings.TrimPrefix(path, "/") + path = strings.TrimSuffix(path, "/") + + components := strings.Split(path, "/") + + if len(components) != 2 && len(components) != 3 { + return nil, fmt.Errorf("Azure KeyVault Child Id should have 2 or 3 segments, got %d: '%s'", len(components), path) + } + + version := "" + if len(components) == 3 { + version = components[2] + } + + childId := KeyVaultChildID{ + KeyVaultBaseUrl: fmt.Sprintf("%s://%s/", idURL.Scheme, idURL.Host), + Name: components[1], + Version: version, + } + + return &childId, nil +} + func ValidateKeyVaultChildName(v interface{}, k string) (warnings []string, errors []error) { value := v.(string) @@ -72,3 +104,24 @@ func ValidateKeyVaultChildId(i interface{}, k string) (warnings []string, errors return warnings, errors } + +// Unfortunately this can't (easily) go in the Validate package +// since there's a circular reference on this package +func ValidateKeyVaultChildIdVersionOptional(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 + } + + if _, err := ParseKeyVaultChildIDVersionOptional(v); err != nil { + errors = append(errors, fmt.Errorf("Error parsing Key Vault Child ID: %s", err)) + return warnings, errors + } + + return warnings, errors +} diff --git a/azurerm/helpers/azure/key_vault_child_test.go b/azurerm/helpers/azure/key_vault_child_test.go index b8b1e8998aafe..2e5951cc7659a 100644 --- a/azurerm/helpers/azure/key_vault_child_test.go +++ b/azurerm/helpers/azure/key_vault_child_test.go @@ -55,6 +55,59 @@ func TestAccAzureRMValidateKeyVaultChildID(t *testing.T) { } } +func TestAccAzureRMValidateKeyVaultChildIDVersionOptional(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: false, + }, + { + Input: "https://my-keyvault.vault.azure.net/certificates/hello/world", + ExpectError: false, + }, + { + Input: "https://my-keyvault.vault.azure.net/keys/castle/1492", + ExpectError: false, + }, + { + Input: "https://my-keyvault.vault.azure.net/secrets/bird/fdf067c93bbb4b22bff4d8b7a9a56217/XXX", + ExpectError: true, + }, + } + + for _, tc := range cases { + warnings, err := ValidateKeyVaultChildIdVersionOptional(tc.Input, "example") + if err != nil { + if !tc.ExpectError { + t.Fatalf("Got error for input %q: %+v", tc.Input, err) + } + + return + } + + 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 TestAccAzureRMKeyVaultChild_parseID(t *testing.T) { cases := []struct { Input string @@ -134,6 +187,90 @@ func TestAccAzureRMKeyVaultChild_parseID(t *testing.T) { } } +func TestAccAzureRMKeyVaultChild_parseIDVersionOptional(t *testing.T) { + cases := []struct { + Input string + Expected KeyVaultChildID + 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, + Expected: KeyVaultChildID{ + Name: "bird", + KeyVaultBaseUrl: "https://my-keyvault.vault.azure.net/", + Version: "", + }, + }, + { + Input: "https://my-keyvault.vault.azure.net/secrets/bird/fdf067c93bbb4b22bff4d8b7a9a56217", + ExpectError: false, + Expected: KeyVaultChildID{ + Name: "bird", + KeyVaultBaseUrl: "https://my-keyvault.vault.azure.net/", + Version: "fdf067c93bbb4b22bff4d8b7a9a56217", + }, + }, + { + Input: "https://my-keyvault.vault.azure.net/certificates/hello/world", + ExpectError: false, + Expected: KeyVaultChildID{ + Name: "hello", + KeyVaultBaseUrl: "https://my-keyvault.vault.azure.net/", + Version: "world", + }, + }, + { + Input: "https://my-keyvault.vault.azure.net/keys/castle/1492", + ExpectError: false, + Expected: KeyVaultChildID{ + Name: "castle", + KeyVaultBaseUrl: "https://my-keyvault.vault.azure.net/", + Version: "1492", + }, + }, + { + Input: "https://my-keyvault.vault.azure.net/secrets/bird/fdf067c93bbb4b22bff4d8b7a9a56217/XXX", + ExpectError: true, + }, + } + + for _, tc := range cases { + secretId, err := ParseKeyVaultChildIDVersionOptional(tc.Input) + if err != nil { + if !tc.ExpectError { + t.Fatalf("Got error for ID '%s': %+v", tc.Input, err) + } + + return + } + + if secretId == nil { + t.Fatalf("Expected a SecretID to be parsed for ID '%s', got nil.", tc.Input) + } + + if tc.Expected.KeyVaultBaseUrl != secretId.KeyVaultBaseUrl { + t.Fatalf("Expected 'KeyVaultBaseUrl' to be '%s', got '%s' for ID '%s'", tc.Expected.KeyVaultBaseUrl, secretId.KeyVaultBaseUrl, tc.Input) + } + + if tc.Expected.Name != secretId.Name { + t.Fatalf("Expected 'Version' to be '%s', got '%s' for ID '%s'", tc.Expected.Name, secretId.Name, tc.Input) + } + + 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) + } + } +} + func TestAccAzureRMKeyVaultChild_validateName(t *testing.T) { cases := []struct { Input string diff --git a/azurerm/internal/services/apimanagement/resource_arm_api_management.go b/azurerm/internal/services/apimanagement/resource_arm_api_management.go index e01857116ccbf..458d00593341d 100644 --- a/azurerm/internal/services/apimanagement/resource_arm_api_management.go +++ b/azurerm/internal/services/apimanagement/resource_arm_api_management.go @@ -1058,7 +1058,7 @@ func apiManagementResourceHostnameSchema(schemaName string) map[string]*schema.S "key_vault_id": { Type: schema.TypeString, Optional: true, - ValidateFunc: azure.ValidateKeyVaultChildId, + ValidateFunc: azure.ValidateKeyVaultChildIdVersionOptional, ConflictsWith: []string{ fmt.Sprintf("hostname_configuration.0.%s.0.certificate", schemaName), fmt.Sprintf("hostname_configuration.0.%s.0.certificate_password", schemaName),