Skip to content

Commit

Permalink
Switch Key Vault sub resources to use Key Vault ID instead of URL (#2820
Browse files Browse the repository at this point in the history
)

This will allow us to easily check if the key vault exists before doing a update/read/delete on it causing a NXDOMAIN error (as the domain used for the API call vanishes when the key vault is deleted)

fixes #2396
  • Loading branch information
katbyte authored Feb 7, 2019
1 parent ac6aa0e commit 2b0fc5e
Show file tree
Hide file tree
Showing 22 changed files with 730 additions and 127 deletions.
48 changes: 42 additions & 6 deletions azurerm/data_source_key_vault_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,24 @@ func dataSourceArmKeyVaultKey() *schema.Resource {
ValidateFunc: azure.ValidateKeyVaultChildName,
},

"key_vault_id": {
Type: schema.TypeString,
Optional: true, //todo required in 2.0
Computed: true, //todo removed in 2.0
ForceNew: true,
ValidateFunc: azure.ValidateResourceID,
ConflictsWith: []string{"vault_uri"},
},

//todo remove in 2.0
"vault_uri": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.URLIsHTTPS,
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
Deprecated: "This property has been deprecated in favour of the key_vault_id property. This will prevent a class of bugs as described in https://github.com/terraform-providers/terraform-provider-azurerm/issues/2396 and will be removed in version 2.0 of the provider",
ValidateFunc: validate.URLIsHTTPS,
ConflictsWith: []string{"key_vault_id"},
},

"key_type": {
Expand Down Expand Up @@ -65,16 +79,38 @@ func dataSourceArmKeyVaultKey() *schema.Resource {
}

func dataSourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error {
vaultClient := meta.(*ArmClient).keyVaultClient
client := meta.(*ArmClient).keyVaultManagementClient
ctx := meta.(*ArmClient).StopContext

vaultUri := d.Get("vault_uri").(string)
keyVaultBaseUri := d.Get("vault_uri").(string)
name := d.Get("name").(string)
keyVaultId := d.Get("key_vault_id").(string)

if keyVaultBaseUri == "" {
if keyVaultId == "" {
return fmt.Errorf("one of `key_vault_id` or `vault_uri` must be set")
}

pKeyVaultBaseUrl, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId)
if err != nil {
return fmt.Errorf("Error looking up Key %q vault url from id %q: %+v", name, keyVaultId, err)
}

keyVaultBaseUri = pKeyVaultBaseUrl
d.Set("vault_uri", keyVaultBaseUri)
} else {
id, err := azure.GetKeyVaultIDFromBaseUrl(ctx, vaultClient, keyVaultBaseUri)
if err != nil {
return fmt.Errorf("Error unable to find key vault ID from URL %q for certificate %q: %+v", keyVaultBaseUri, name, err)
}
d.Set("key_vault_id", id)
}

resp, err := client.GetKey(ctx, vaultUri, name, "")
resp, err := client.GetKey(ctx, keyVaultBaseUri, name, "")
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("Key %q was not found in Key Vault at URI %q", name, vaultUri)
return fmt.Errorf("Key %q was not found in Key Vault at URI %q", name, keyVaultBaseUri)
}

return err
Expand Down
7 changes: 4 additions & 3 deletions azurerm/data_source_key_vault_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ func TestAccDataSourceAzureRMKeyVaultKey_complete(t *testing.T) {
}

func testAccDataSourceKeyVaultKey_complete(rString string, location string) string {
t := testAccAzureRMKeyVaultKey_complete(rString, location)
return fmt.Sprintf(`
%s
data "azurerm_key_vault_key" "test" {
name = "${azurerm_key_vault_key.test.name}"
vault_uri = "${azurerm_key_vault_key.test.vault_uri}"
name = "${azurerm_key_vault_key.test.name}"
key_vault_id = "${azurerm_key_vault.test.id}"
}
`, testAccAzureRMKeyVaultKey_complete(rString, location))
`, t)
}
49 changes: 42 additions & 7 deletions azurerm/data_source_key_vault_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,24 @@ func dataSourceArmKeyVaultSecret() *schema.Resource {
ValidateFunc: azure.ValidateKeyVaultChildName,
},

"key_vault_id": {
Type: schema.TypeString,
Optional: true, //todo required in 2.0
Computed: true, //todo removed in 2.0
ForceNew: true,
ValidateFunc: azure.ValidateResourceID,
ConflictsWith: []string{"vault_uri"},
},

//todo remove in 2.0
"vault_uri": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.URLIsHTTPS,
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
Deprecated: "This property has been deprecated in favour of the key_vault_id property. This will prevent a class of bugs as described in https://github.com/terraform-providers/terraform-provider-azurerm/issues/2396 and will be removed in version 2.0 of the provider",
ValidateFunc: validate.URLIsHTTPS,
ConflictsWith: []string{"key_vault_id"},
},

"value": {
Expand All @@ -50,17 +63,39 @@ func dataSourceArmKeyVaultSecret() *schema.Resource {
}

func dataSourceArmKeyVaultSecretRead(d *schema.ResourceData, meta interface{}) error {
vaultClient := meta.(*ArmClient).keyVaultClient
client := meta.(*ArmClient).keyVaultManagementClient
ctx := meta.(*ArmClient).StopContext

name := d.Get("name").(string)
vaultUri := d.Get("vault_uri").(string)
keyVaultBaseUri := d.Get("vault_uri").(string)
keyVaultId := d.Get("key_vault_id").(string)

if keyVaultBaseUri == "" {
if keyVaultId == "" {
return fmt.Errorf("one of `key_vault_id` or `vault_uri` must be set")
}

pKeyVaultBaseUrl, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId)
if err != nil {
return fmt.Errorf("Error looking up Secret %q vault url form id %q: %+v", name, keyVaultId, err)
}

keyVaultBaseUri = pKeyVaultBaseUrl
d.Set("vault_uri", keyVaultBaseUri)
} else {
id, err := azure.GetKeyVaultIDFromBaseUrl(ctx, vaultClient, keyVaultBaseUri)
if err != nil {
return fmt.Errorf("Error unable to find key vault ID from URL %q for certificate %q: %+v", keyVaultBaseUri, name, err)
}
d.Set("key_vault_id", id)
}

// we always want to get the latest version
resp, err := client.GetSecret(ctx, vaultUri, name, "")
resp, err := client.GetSecret(ctx, keyVaultBaseUri, name, "")
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("KeyVault Secret %q (KeyVault URI %q) does not exist", name, vaultUri)
return fmt.Errorf("KeyVault Secret %q (KeyVault URI %q) does not exist", name, keyVaultBaseUri)
}
return fmt.Errorf("Error making Read request on Azure KeyVault Secret %s: %+v", name, err)
}
Expand Down
8 changes: 4 additions & 4 deletions azurerm/data_source_key_vault_secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ func testAccDataSourceKeyVaultSecret_basic(rString string, location string) stri
%s
data "azurerm_key_vault_secret" "test" {
name = "${azurerm_key_vault_secret.test.name}"
vault_uri = "${azurerm_key_vault_secret.test.vault_uri}"
name = "${azurerm_key_vault_secret.test.name}"
key_vault_id = "${azurerm_key_vault.test.id}"
}
`, r)
}
Expand All @@ -71,8 +71,8 @@ func testAccDataSourceKeyVaultSecret_complete(rString string, location string) s
%s
data "azurerm_key_vault_secret" "test" {
name = "${azurerm_key_vault_secret.test.name}"
vault_uri = "${azurerm_key_vault_secret.test.vault_uri}"
name = "${azurerm_key_vault_secret.test.name}"
key_vault_id = "${azurerm_key_vault.test.id}"
}
`, r)
}
121 changes: 121 additions & 0 deletions azurerm/helpers/azure/key_vault.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package azure

import (
"context"
"fmt"
"log"

"github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func GetKeyVaultBaseUrlFromID(ctx context.Context, client keyvault.VaultsClient, keyVaultId string) (string, error) {

if keyVaultId == "" {
return "", fmt.Errorf("keyVaultId is empty")
}

id, err := ParseAzureResourceID(keyVaultId)
if err != nil {
return "", err
}
resourceGroup := id.ResourceGroup

vaultName, ok := id.Path["vaults"]
if !ok {
return "", fmt.Errorf("resource id does not contain `vaults`: %q", keyVaultId)
}

resp, err := client.Get(ctx, resourceGroup, vaultName)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return "", fmt.Errorf("Error unable to find KeyVault %q (Resource Group %q): %+v", vaultName, resourceGroup, err)
}
return "", fmt.Errorf("Error making Read request on KeyVault %q (Resource Group %q): %+v", vaultName, resourceGroup, err)
}

if resp.Properties == nil || resp.Properties.VaultURI == nil {
return "", fmt.Errorf("vault (%s) response properties or VaultURI is nil", keyVaultId)
}

return *resp.Properties.VaultURI, nil
}

func GetKeyVaultIDFromBaseUrl(ctx context.Context, client keyvault.VaultsClient, keyVaultUrl string) (string, error) {

list, err := client.ListComplete(ctx, utils.Int32(1000))
if err != nil {
return "", fmt.Errorf("Error GetKeyVaultId unable to list Key Vaults %v", err)
}

for list.NotDone() {
v := list.Value()
if v.ID == nil {
log.Printf("[DEBUG]GetKeyVaultId: v.ID was nil, continuing")
continue
}

vid, err := ParseAzureResourceID(*v.ID)
if err != nil {
log.Printf("[DEBUG] GetKeyVaultId: unable to parse v.ID (%s): %v", *v.ID, err)
continue
}
resourceGroup := vid.ResourceGroup
name := vid.Path["vaults"]

//resp does not appear to contain the vault properties, so lets fech them
get, err := client.Get(ctx, resourceGroup, name)
if err != nil {
log.Printf("[DEBUG] GetKeyVaultId: Error making Read request on KeyVault %q (Resource Group %q): %+v", name, resourceGroup, err)
continue
}

if get.ID == nil || get.Properties == nil || get.Properties.VaultURI == nil {
log.Printf("[DEBUG] GetKeyVaultId: KeyVault %q (Resource Group %q) has nil ID, properties or vault URI", name, resourceGroup)
continue
}

if keyVaultUrl == *get.Properties.VaultURI {
return *get.ID, nil
}

e := list.NextWithContext(ctx)
if e != nil {
return "", fmt.Errorf("Error GetKeyVaultId: Error getting next value on KeyVault %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}

return "", fmt.Errorf("Error GetKeyVaultId unable to find Key Vault with url %q", keyVaultUrl)
}

func KeyVaultExists(ctx context.Context, client keyvault.VaultsClient, keyVaultId string) (bool, error) {

if keyVaultId == "" {
return false, fmt.Errorf("keyVaultId is empty")
}

id, err := ParseAzureResourceID(keyVaultId)
if err != nil {
return false, err
}
resourceGroup := id.ResourceGroup

vaultName, ok := id.Path["vaults"]
if !ok {
return false, fmt.Errorf("resource id does not contain `vaults`: %q", keyVaultId)
}

resp, err := client.Get(ctx, resourceGroup, vaultName)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return false, nil
}
return false, fmt.Errorf("Error making Read request on KeyVault %q (Resource Group %q): %+v", vaultName, resourceGroup, err)
}

if resp.Properties == nil || resp.Properties.VaultURI == nil {
return false, fmt.Errorf("vault (%s) response properties or VaultURI is nil", keyVaultId)
}

return true, nil
}
2 changes: 1 addition & 1 deletion azurerm/helpers/azure/key_vault_access_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/satori/go.uuid"
uuid "github.com/satori/go.uuid"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress"
)

Expand Down
2 changes: 1 addition & 1 deletion azurerm/resource_arm_key_vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/satori/go.uuid"
uuid "github.com/satori/go.uuid"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/set"
Expand Down
Loading

0 comments on commit 2b0fc5e

Please sign in to comment.