diff --git a/azurerm/internal/acceptance/testcase.go b/azurerm/internal/acceptance/testcase.go index 3f62e938c372..ef98b4c446df 100644 --- a/azurerm/internal/acceptance/testcase.go +++ b/azurerm/internal/acceptance/testcase.go @@ -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) diff --git a/azurerm/internal/services/keyvault/client/client.go b/azurerm/internal/services/keyvault/client/client.go index 1f01511dead5..28a5a0b3e0d0 100644 --- a/azurerm/internal/services/keyvault/client/client.go +++ b/azurerm/internal/services/keyvault/client/client.go @@ -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 { @@ -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 +} diff --git a/azurerm/internal/services/keyvault/client/helpers.go b/azurerm/internal/services/keyvault/client/helpers.go index c56ab4940038..cd16de5c6702 100644 --- a/azurerm/internal/services/keyvault/client/helpers.go +++ b/azurerm/internal/services/keyvault/client/helpers.go @@ -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) { diff --git a/azurerm/internal/services/storage/storage_account_customer_managed_key_resource.go b/azurerm/internal/services/storage/storage_account_customer_managed_key_resource.go index c79c06ce1fc6..01e8a76377be 100644 --- a/azurerm/internal/services/storage/storage_account_customer_managed_key_resource.go +++ b/azurerm/internal/services/storage/storage_account_customer_managed_key_resource.go @@ -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) @@ -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 { @@ -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) @@ -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) diff --git a/azurerm/internal/services/storage/storage_account_customer_managed_key_resource_test.go b/azurerm/internal/services/storage/storage_account_customer_managed_key_resource_test.go index d401ef76d5de..165e643abe2c 100644 --- a/azurerm/internal/services/storage/storage_account_customer_managed_key_resource_test.go +++ b/azurerm/internal/services/storage/storage_account_customer_managed_key_resource_test.go @@ -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 { @@ -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" {