Skip to content

Commit

Permalink
azurerm_storage_account_customer_managed_key - allow use of keys fr…
Browse files Browse the repository at this point in the history
…om key vaults in remote subscription (#12142)

fixes #6298
  • Loading branch information
jackofallops authored Jun 10, 2021
1 parent cf735ad commit 888d584
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 3 deletions.
4 changes: 4 additions & 0 deletions azurerm/internal/acceptance/testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ func (td TestData) runAcceptanceTest(t *testing.T, testCase resource.TestCase) {
azurerm := provider.TestAzureProvider()
return azurerm, nil
},
"azurerm-alt": func() (terraform.ResourceProvider, error) {
azurerm := provider.TestAzureProvider()
return azurerm, nil
},
}

resource.ParallelTest(t, testCase)
Expand Down
8 changes: 8 additions & 0 deletions azurerm/internal/services/keyvault/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Client struct {
ManagedHsmClient *keyvault.ManagedHsmsClient
ManagementClient *keyvaultmgmt.BaseClient
VaultsClient *keyvault.VaultsClient
options *common.ClientOptions
}

func NewClient(o *common.ClientOptions) *Client {
Expand All @@ -26,5 +27,12 @@ func NewClient(o *common.ClientOptions) *Client {
ManagedHsmClient: &managedHsmClient,
ManagementClient: &managementClient,
VaultsClient: &vaultsClient,
options: o,
}
}

func (client Client) KeyVaultClientForSubscription(subscriptionId string) *keyvault.VaultsClient {
vaultsClient := keyvault.NewVaultsClientWithBaseURI(client.options.ResourceManagerEndpoint, subscriptionId)
client.options.ConfigureClient(&vaultsClient.Client, client.options.ResourceManagerAuthorizer)
return &vaultsClient
}
4 changes: 4 additions & 0 deletions azurerm/internal/services/keyvault/client/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func (c *Client) BaseUriForKeyVault(ctx context.Context, keyVaultId parse.VaultI
lock[cacheKey].Lock()
defer lock[cacheKey].Unlock()

if keyVaultId.SubscriptionId != c.VaultsClient.SubscriptionID {
c.VaultsClient = c.KeyVaultClientForSubscription(keyVaultId.SubscriptionId)
}

resp, err := c.VaultsClient.Get(ctx, keyVaultId.ResourceGroup, keyVaultId.Name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func resourceStorageAccountCustomerManagedKeyCreateUpdate(d *pluginsdk.ResourceD

storageAccount, err := storageClient.GetProperties(ctx, storageAccountID.ResourceGroup, storageAccountID.Name, "")
if err != nil {
return fmt.Errorf("Error retrieving Storage Account %q (Resource Group %q): %+v", storageAccountID.Name, storageAccountID.ResourceGroup, err)
return fmt.Errorf("retrieving Storage Account %q (Resource Group %q): %+v", storageAccountID.Name, storageAccountID.ResourceGroup, err)
}
if storageAccount.AccountProperties == nil {
return fmt.Errorf("Error retrieving Storage Account %q (Resource Group %q): `properties` was nil", storageAccountID.Name, storageAccountID.ResourceGroup)
Expand All @@ -94,7 +94,7 @@ func resourceStorageAccountCustomerManagedKeyCreateUpdate(d *pluginsdk.ResourceD
resourceID := storageAccountIDRaw

if d.IsNewResource() {
// whilst this looks superflurious given encryption is enabled by default, due to the way
// whilst this looks superfluous given encryption is enabled by default, due to the way
// the Azure API works this technically can be nil
if storageAccount.AccountProperties.Encryption != nil {
if storageAccount.AccountProperties.Encryption.KeySource == storage.KeySourceMicrosoftKeyvault {
Expand All @@ -108,6 +108,11 @@ func resourceStorageAccountCustomerManagedKeyCreateUpdate(d *pluginsdk.ResourceD
return err
}

// If the Keyvault is in another subscription we need to update the client
if keyVaultID.SubscriptionId != vaultsClient.SubscriptionID {
vaultsClient = meta.(*clients.Client).KeyVault.KeyVaultClientForSubscription(keyVaultID.SubscriptionId)
}

keyVault, err := vaultsClient.Get(ctx, keyVaultID.ResourceGroup, keyVaultID.Name)
if err != nil {
return fmt.Errorf("Error retrieving Key Vault %q (Resource Group %q): %+v", keyVaultID.Name, keyVaultID.ResourceGroup, err)
Expand All @@ -129,7 +134,7 @@ func resourceStorageAccountCustomerManagedKeyCreateUpdate(d *pluginsdk.ResourceD

keyVaultBaseURL, err := keyVaultsClient.BaseUriForKeyVault(ctx, *keyVaultID)
if err != nil {
return fmt.Errorf("Error looking up Key Vault URI from Key Vault %q (Resource Group %q): %+v", keyVaultID.Name, keyVaultID.ResourceGroup, err)
return fmt.Errorf("looking up Key Vault URI from Key Vault %q (Resource Group %q) (Subscription %q): %+v", keyVaultID.Name, keyVaultID.ResourceGroup, keyVaultsClient.VaultsClient.SubscriptionID, err)
}

keyName := d.Get("key_name").(string)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ func TestAccStorageAccountCustomerManagedKey_testKeyVersion(t *testing.T) {
})
}

func TestAccAzureRMStorageAccountCustomerManagedKey_remoteKeyVault(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "test")
r := StorageAccountCustomerManagedKeyResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.remoteKeyVault(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
})
}

func (r StorageAccountCustomerManagedKeyResource) accountHasDefaultSettings(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) error {
accountId, err := storageParse.StorageAccountID(state.Attributes["id"])
if err != nil {
Expand Down Expand Up @@ -234,6 +248,115 @@ resource "azurerm_storage_account_customer_managed_key" "test" {
`, template)
}

// (@jackofallops) - This test spans 2 subscriptions to check that it's possible to use a CMK stored in a vault in a non-local subscription. This is temporarily making use of an extra providerfactory which will need to be removed after the move to plugin-sdk-go
// TODO - review this config when plugin-sdk-go is implemented in the provider / test framework.
func (r StorageAccountCustomerManagedKeyResource) remoteKeyVault(data acceptance.TestData) string {
clientData := data.Client()
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
provider "azurerm-alt" {
subscription_id = "%s"
tenant_id = "%s"
features {
key_vault {
purge_soft_delete_on_destroy = false
}
}
}
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "remotetest" {
provider = azurerm-alt
name = "acctestRG-alt-%d"
location = "%s"
}
resource "azurerm_key_vault" "remotetest" {
provider = azurerm-alt
name = "acctestkv%s"
location = azurerm_resource_group.remotetest.location
resource_group_name = azurerm_resource_group.remotetest.name
tenant_id = "%s"
sku_name = "standard"
purge_protection_enabled = true
}
resource "azurerm_key_vault_access_policy" "storage" {
provider = azurerm-alt
key_vault_id = azurerm_key_vault.remotetest.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = azurerm_storage_account.test.identity.0.principal_id
key_permissions = ["get", "create", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"]
secret_permissions = ["get"]
}
resource "azurerm_key_vault_access_policy" "client" {
provider = azurerm-alt
key_vault_id = azurerm_key_vault.remotetest.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"]
secret_permissions = ["get"]
}
resource "azurerm_key_vault_key" "remote" {
provider = azurerm-alt
name = "remote"
key_vault_id = azurerm_key_vault.remotetest.id
key_type = "RSA"
key_size = 2048
key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"]
depends_on = [
azurerm_key_vault_access_policy.client,
azurerm_key_vault_access_policy.storage,
]
}
resource "azurerm_resource_group" "test" {
provider = azurerm
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_storage_account" "test" {
provider = azurerm
name = "acctestsa%s"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
account_tier = "Standard"
account_replication_type = "LRS"
identity {
type = "SystemAssigned"
}
}
resource "azurerm_storage_account_customer_managed_key" "test" {
provider = azurerm
storage_account_id = azurerm_storage_account.test.id
key_vault_id = azurerm_key_vault.remotetest.id
key_name = azurerm_key_vault_key.remote.name
key_version = azurerm_key_vault_key.remote.version
}
`, clientData.SubscriptionIDAlt, clientData.TenantID, data.RandomInteger, data.Locations.Primary, data.RandomString, clientData.TenantID, data.RandomInteger, data.Locations.Primary, data.RandomString)
}

func (r StorageAccountCustomerManagedKeyResource) template(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down

0 comments on commit 888d584

Please sign in to comment.