From d1cf0b73d3e95f3c1c1191dca109f3f2d27dedac Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Mon, 10 Feb 2020 13:58:39 -0800 Subject: [PATCH 01/23] Initial port to new code base --- .../storage/data_source_storage_account.go | 27 -- .../storage/resource_arm_storage_account.go | 76 ---- ...arm_storage_account_encryption_settings.go | 327 +++++++++++++++++ ...torage_account_encryption_settings_test.go | 341 ++++++++++++++++++ .../resource_arm_storage_account_test.go | 155 -------- website/docs/r/storage_account.html.markdown | 6 - ..._account_encryption_settings.html.markdown | 133 +++++++ 7 files changed, 801 insertions(+), 264 deletions(-) create mode 100644 azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go create mode 100644 azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go create mode 100644 website/docs/r/storage_account_encryption_settings.html.markdown diff --git a/azurerm/internal/services/storage/data_source_storage_account.go b/azurerm/internal/services/storage/data_source_storage_account.go index ba8d8770769e..6239a3385a1d 100644 --- a/azurerm/internal/services/storage/data_source_storage_account.go +++ b/azurerm/internal/services/storage/data_source_storage_account.go @@ -54,11 +54,6 @@ func dataSourceArmStorageAccount() *schema.Resource { Computed: true, }, - "account_encryption_source": { - Type: schema.TypeString, - Computed: true, - }, - "custom_domain": { Type: schema.TypeList, Computed: true, @@ -73,16 +68,6 @@ func dataSourceArmStorageAccount() *schema.Resource { }, }, - "enable_blob_encryption": { - Type: schema.TypeBool, - Computed: true, - }, - - "enable_file_encryption": { - Type: schema.TypeBool, - Computed: true, - }, - "enable_https_traffic_only": { Type: schema.TypeBool, Computed: true, @@ -330,18 +315,6 @@ func dataSourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) e } } - if encryption := props.Encryption; encryption != nil { - if services := encryption.Services; services != nil { - if blob := services.Blob; blob != nil { - d.Set("enable_blob_encryption", blob.Enabled) - } - if file := services.File; file != nil { - d.Set("enable_file_encryption", file.Enabled) - } - } - d.Set("account_encryption_source", string(encryption.KeySource)) - } - // Computed d.Set("primary_location", props.PrimaryLocation) d.Set("secondary_location", props.SecondaryLocation) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account.go b/azurerm/internal/services/storage/resource_arm_storage_account.go index dda85e0d247f..c6a48bd2caac 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account.go @@ -122,17 +122,6 @@ func resourceArmStorageAccount() *schema.Resource { }, true), }, - "account_encryption_source": { - Type: schema.TypeString, - Optional: true, - Default: string(storage.MicrosoftStorage), - ValidateFunc: validation.StringInSlice([]string{ - string(storage.MicrosoftKeyvault), - string(storage.MicrosoftStorage), - }, true), - DiffSuppressFunc: suppress.CaseDifference, - }, - "custom_domain": { Type: schema.TypeList, Optional: true, @@ -153,18 +142,6 @@ func resourceArmStorageAccount() *schema.Resource { }, }, - "enable_blob_encryption": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - - "enable_file_encryption": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "enable_https_traffic_only": { Type: schema.TypeBool, Optional: true, @@ -672,15 +649,12 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e accountKind := d.Get("account_kind").(string) location := azure.NormalizeLocation(d.Get("location").(string)) t := d.Get("tags").(map[string]interface{}) - enableBlobEncryption := d.Get("enable_blob_encryption").(bool) - enableFileEncryption := d.Get("enable_file_encryption").(bool) enableHTTPSTrafficOnly := d.Get("enable_https_traffic_only").(bool) isHnsEnabled := d.Get("is_hns_enabled").(bool) accountTier := d.Get("account_tier").(string) replicationType := d.Get("account_replication_type").(string) storageType := fmt.Sprintf("%s_%s", accountTier, replicationType) - storageAccountEncryptionSource := d.Get("account_encryption_source").(string) parameters := storage.AccountCreateParameters{ Location: &location, @@ -691,15 +665,6 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e Kind: storage.Kind(accountKind), AccountPropertiesCreateParameters: &storage.AccountPropertiesCreateParameters{ Encryption: &storage.Encryption{ - Services: &storage.EncryptionServices{ - Blob: &storage.EncryptionService{ - Enabled: utils.Bool(enableBlobEncryption), - }, - File: &storage.EncryptionService{ - Enabled: utils.Bool(enableFileEncryption), - }}, - KeySource: storage.KeySource(storageAccountEncryptionSource), - }, EnableHTTPSTrafficOnly: &enableHTTPSTrafficOnly, NetworkRuleSet: expandStorageAccountNetworkRules(d), IsHnsEnabled: &isHnsEnabled, @@ -903,35 +868,6 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e d.SetPartial("tags") } - if d.HasChange("enable_blob_encryption") || d.HasChange("enable_file_encryption") { - encryptionSource := d.Get("account_encryption_source").(string) - - opts := storage.AccountUpdateParameters{ - AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ - Encryption: &storage.Encryption{ - Services: &storage.EncryptionServices{}, - KeySource: storage.KeySource(encryptionSource), - }, - }, - } - - if d.HasChange("enable_blob_encryption") { - enableEncryption := d.Get("enable_blob_encryption").(bool) - opts.Encryption.Services.Blob = &storage.EncryptionService{ - Enabled: utils.Bool(enableEncryption), - } - - d.SetPartial("enable_blob_encryption") - } - - if d.HasChange("enable_file_encryption") { - enableEncryption := d.Get("enable_file_encryption").(bool) - opts.Encryption.Services.File = &storage.EncryptionService{ - Enabled: utils.Bool(enableEncryption), - } - d.SetPartial("enable_file_encryption") - } - if _, err := client.Update(ctx, resourceGroupName, storageAccountName, opts); err != nil { return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) } @@ -1122,18 +1058,6 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err } } - if encryption := props.Encryption; encryption != nil { - if services := encryption.Services; services != nil { - if blob := services.Blob; blob != nil { - d.Set("enable_blob_encryption", blob.Enabled) - } - if file := services.File; file != nil { - d.Set("enable_file_encryption", file.Enabled) - } - } - d.Set("account_encryption_source", string(encryption.KeySource)) - } - // Computed d.Set("primary_location", props.PrimaryLocation) d.Set("secondary_location", props.SecondaryLocation) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go new file mode 100644 index 000000000000..d536707a7398 --- /dev/null +++ b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go @@ -0,0 +1,327 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmStorageAccountEncryptionSettings() *schema.Resource { + return &schema.Resource{ + Read: resourceArmStorageAccountEncryptionSettingsRead, + Create: resourceArmStorageAccountEncryptionSettingsCreateUpdate, + Update: resourceArmStorageAccountEncryptionSettingsCreateUpdate, + Delete: resourceArmStorageAccountEncryptionSettingsDelete, + + Importer: &schema.ResourceImporter{ + State: resourceArmStorageAccountEncryptionSettingsImportState, + }, + SchemaVersion: 2, + + Schema: map[string]*schema.Schema{ + "storage_account_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + "enable_blob_encryption": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "enable_file_encryption": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "key_vault": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + // This attribute is not used, it was only added + // to create a dependency between this resource + // and the key vault policy + "key_vault_policy_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "key_vault_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "key_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "key_version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "key_vault_uri": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + vaultClient := meta.(*ArmClient).keyVaultClient + client := meta.(*ArmClient).storageServiceClient + + storageAccountId := d.Get("storage_account_id").(string) + + id, err := azure.ParseAzureResourceID(storageAccountId) + if err != nil { + return err + } + + storageAccountName := id.Path["storageAccounts"] + resourceGroupName := id.ResourceGroup + + // set default values for the attributes + enableBlobEncryption := true + enableFileEncryption := true + + // create the update object with the default values + opts := storage.AccountUpdateParameters{ + AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ + Encryption: &storage.Encryption{ + Services: &storage.EncryptionServices{ + Blob: &storage.EncryptionService{ + Enabled: utils.Bool(enableBlobEncryption), + }, + File: &storage.EncryptionService{ + Enabled: utils.Bool(enableFileEncryption), + }}, + KeySource: storage.MicrosoftStorage, + KeyVaultProperties: &storage.KeyVaultProperties{}, + }, + }, + } + + if d.HasChange("enable_blob_encryption") || d.HasChange("enable_file_encryption") { + opts.Encryption.Services.Blob.Enabled = utils.Bool(d.Get("enable_blob_encryption").(bool)) + opts.Encryption.Services.File.Enabled = utils.Bool(d.Get("enable_file_encryption").(bool)) + } + + if keyVaultProperties := expandAzureRmStorageAccountKeyVaultProperties(d); keyVaultProperties.KeyName != utils.String("") { + if v, ok := d.GetOk("key_vault.0.key_vault_id"); ok { + // Get the key vault base URL from the key vault + keyVaultId := v.(string) + pKeyVaultBaseUrl, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId) + + if err != nil { + return fmt.Errorf("Error looking up Key Vault URI from id %q: %+v", keyVaultId, err) + } + + keyVaultProperties.KeyVaultURI = utils.String(pKeyVaultBaseUrl) + opts.Encryption.KeyVaultProperties = keyVaultProperties + opts.Encryption.KeySource = storage.MicrosoftKeyvault + } + } + + _, err = client.Update(ctx, resourceGroupName, storageAccountName, opts) + if err != nil { + return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) + } + + resourceId := fmt.Sprintf("%s/encryptionSettings", storageAccountId) + d.SetId(resourceId) + + return resourceArmStorageAccountEncryptionSettingsRead(d, meta) +} + +func resourceArmStorageAccountEncryptionSettingsRead(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + client := meta.(*ArmClient).storageServiceClient + + storageAccountId := d.Get("storage_account_id").(string) + + id, err := parseAzureResourceID(storageAccountId) + if err != nil { + return err + } + name := id.Path["storageAccounts"] + resGroup := id.ResourceGroup + + resp, err := client.GetProperties(ctx, resGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %+v", name, err) + } + + if props := resp.AccountProperties; props != nil { + if encryption := props.Encryption; encryption != nil { + if services := encryption.Services; services != nil { + if blob := services.Blob; blob != nil { + d.Set("enable_blob_encryption", blob.Enabled) + } + if file := services.File; file != nil { + d.Set("enable_file_encryption", file.Enabled) + } + } + + if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { + keyVaultId := d.Get("key_vault.0.key_vault_id").(string) + keyVaultPolicyId := d.Get("key_vault.0.key_vault_policy_id").(string) + + if err := d.Set("key_vault", flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties, keyVaultId, keyVaultPolicyId)); err != nil { + return fmt.Errorf("Error flattening `key_vault_properties`: %+v", err) + } + } + } + } + + return nil +} + +func resourceArmStorageAccountEncryptionSettingsDelete(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + client := meta.(*ArmClient).storageServiceClient + + storageAccountId := d.Get("storage_account_id").(string) + + id, err := azure.ParseAzureResourceID(storageAccountId) + if err != nil { + return err + } + + storageAccountName := id.Path["storageAccounts"] + resourceGroupName := id.ResourceGroup + + // Since this isn't a real object, just modifying an existing object + // "Delete" doesn't really make sense it should really be a "Revert to Default" + // So instead of the Delete func actually deleting the Storage Account I am + // making it reset the Storage Account to it's default state + opts := storage.AccountUpdateParameters{ + AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ + Encryption: &storage.Encryption{ + KeySource: storage.MicrosoftStorage, + }, + }, + } + + _, err = client.Update(ctx, resourceGroupName, storageAccountName, opts) + if err != nil { + return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) + } + + return nil +} + +func resourceArmStorageAccountEncryptionSettingsImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + ctx := meta.(*ArmClient).StopContext + client := meta.(*ArmClient).storageServiceClient + + id := d.Id() + + d.Set("storage_account_id", id) + + saId, err := parseAzureResourceID(id) + if err != nil { + return nil, err + } + name := saId.Path["storageAccounts"] + resGroup := saId.ResourceGroup + + resp, err := client.GetProperties(ctx, resGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil, nil + } + return nil, fmt.Errorf("Error importing the state of AzureRM Storage Account %q: %+v", name, err) + } + + if props := resp.AccountProperties; props != nil { + if encryption := props.Encryption; encryption != nil { + if services := encryption.Services; services != nil { + if blob := services.Blob; blob != nil { + d.Set("enable_blob_encryption", blob.Enabled) + } + if file := services.File; file != nil { + d.Set("enable_file_encryption", file.Enabled) + } + } + + if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { + if err := d.Set("key_vault", flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties, "", "")); err != nil { + return nil, fmt.Errorf("Error flattening `key_vault_properties` on import: %+v", err) + } + } + } + } + + resourceId := fmt.Sprintf("%s/encryptionSettings", id) + d.SetId(resourceId) + + results := make([]*schema.ResourceData, 1) + + results[0] = d + return results, nil +} + +func expandAzureRmStorageAccountKeyVaultProperties(d *schema.ResourceData) *storage.KeyVaultProperties { + vs := d.Get("key_vault").([]interface{}) + if len(vs) == 0 { + return &storage.KeyVaultProperties{} + } + + v := vs[0].(map[string]interface{}) + keyName := v["key_name"].(string) + keyVersion := v["key_version"].(string) + + return &storage.KeyVaultProperties{ + KeyName: utils.String(keyName), + KeyVersion: utils.String(keyVersion), + } +} + +func flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties *storage.KeyVaultProperties, keyVaultId string, keyVaultPolicyId string) []interface{} { + if keyVaultProperties == nil { + return make([]interface{}, 0) + } + + result := make(map[string]interface{}) + if keyVaultId != "" { + result["key_vault_id"] = keyVaultId + } + + if keyVaultPolicyId != "" { + result["key_vault_policy_id"] = keyVaultPolicyId + } + + if keyVaultProperties.KeyName != nil { + result["key_name"] = *keyVaultProperties.KeyName + } + if keyVaultProperties.KeyVersion != nil { + result["key_version"] = *keyVaultProperties.KeyVersion + } + if keyVaultProperties.KeyVaultURI != nil { + result["key_vault_uri"] = *keyVaultProperties.KeyVaultURI + } + + return []interface{}{result} +} diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go new file mode 100644 index 000000000000..751b0b733fa6 --- /dev/null +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go @@ -0,0 +1,341 @@ +package azurerm + +import ( + "fmt" + "net/http" + "os" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMStorageAccountEncryptionSettings_basic(t *testing.T) { + parentResourceName := "azurerm_storage_account.testsa" + resourceName := "azurerm_storage_account_encryption_settings.custom" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + preConfig := testAccAzureRMStorageAccountEncryptionSettings_basic(ri, rs, location) + postConfig := testAccAzureRMStorageAccountEncryptionSettings_basicDelete(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroyed, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enable_blob_encryption", "true"), + resource.TestCheckResourceAttr(resourceName, "enable_file_encryption", "true"), + ), + }, + { + // Delete the encryption settings resource and verify it is gone + // Whilst making sure the encryption settings on the storage account + // have been reverted to their default state + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExistsWithDefaultSettings(parentResourceName), + testCheckAzureRMStorageAccountEncryptionSettingsDestroyed(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMStorageAccountEncryptionSettings_blobEncryptionDisable(t *testing.T) { + _, exists := os.LookupEnv("TF_ACC_STORAGE_ENCRYPTION_DISABLE") + if !exists { + t.Skip("`TF_ACC_STORAGE_ENCRYPTION_DISABLE` isn't specified - skipping since disabling encryption is generally disabled") + } + + resourceName := "azurerm_storage_account_encryption_settings.custom" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + config := testAccAzureRMStorageAccountEncryptionSettings_blobEncryptionDisabled(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroyed, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enable_blob_encryption", "false"), + ), + }, + }, + }) +} + +func TestAccAzureRMStorageAccountEncryptionSettings_fileEncryptionDisable(t *testing.T) { + _, exists := os.LookupEnv("TF_ACC_STORAGE_ENCRYPTION_DISABLE") + if !exists { + t.Skip("`TF_ACC_STORAGE_ENCRYPTION_DISABLE` isn't specified - skipping since disabling encryption is generally disabled") + } + + resourceName := "azurerm_storage_account_encryption_settings.custom" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + config := testAccAzureRMStorageAccountEncryptionSettings_fileEncryptionDisabled(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroyed, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enable_file_encryption", "false"), + ), + }, + }, + }) +} + +func TestAccAzureRMStorageAccountEncryptionSettings_disappears(t *testing.T) { + parentResourceName := "azurerm_storage_account.testsa" + resourceName := "azurerm_storage_account_encryption_settings.custom" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + preConfig := testAccAzureRMStorageAccountEncryptionSettings_basic(ri, rs, testLocation()) + postConfig := testAccAzureRMStorageAccountEncryptionSettings_basicDelete(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroyed, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enable_blob_encryption", "true"), + resource.TestCheckResourceAttr(resourceName, "enable_file_encryption", "true"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExistsWithDefaultSettings(parentResourceName), + testCheckAzureRMStorageAccountEncryptionSettingsDestroyed(resourceName), + ), + }, + }, + }) +} + +func testCheckAzureRMStorageAccountExistsWithDefaultSettings(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + storageAccount := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + // Ensure resource group exists in API + ctx := testAccProvider.Meta().(*ArmClient).StopContext + conn := testAccProvider.Meta().(*ArmClient).storageServiceClient + + resp, err := conn.GetProperties(ctx, resourceGroup, storageAccount) + if err != nil { + return fmt.Errorf("Bad: Get on storageServiceClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: StorageAccount %q (resource group: %q) does not exist", storageAccount, resourceGroup) + } + + if props := resp.AccountProperties; props != nil { + if encryption := props.Encryption; encryption != nil { + if services := encryption.Services; services != nil { + if !*services.Blob.Enabled { + return fmt.Errorf("enable_blob_encryption not set to default: %s", resourceName) + } + if !*services.File.Enabled { + return fmt.Errorf("enable_file_encryption not set to default: %s", resourceName) + } + } + + if encryption.KeySource != storage.MicrosoftStorage { + return fmt.Errorf("%s keySource not set to default(storage.MicrosoftStorage): %s", resourceName, encryption.KeySource) + } + } else { + return fmt.Errorf("storage account encryption properties not found: %s", resourceName) + } + } + + return nil + } +} + +func testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + if storageAccountId := rs.Primary.Attributes["storage_account_id"]; storageAccountId == "" { + return fmt.Errorf("Unable to read storageAccountId: %s", resourceName) + } + + return nil + } +} + +func testCheckAzureRMStorageAccountDestroyed(s *terraform.State) error { + ctx := testAccProvider.Meta().(*ArmClient).StopContext + conn := testAccProvider.Meta().(*ArmClient).storageServiceClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_account" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.GetProperties(ctx, resourceGroup, name) + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Storage Account still exists:\n%#v", resp.AccountProperties) + } + } + + return nil +} + +func testCheckAzureRMStorageAccountEncryptionSettingsDestroyed(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[resourceName] + if !ok { + return nil + } + + return fmt.Errorf("Found: %s", resourceName) + } +} + +func testAccAzureRMStorageAccountEncryptionSettings_basic(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "production" + } +} + +resource "azurerm_storage_account_encryption_settings" "custom" { + storage_account_id = "${azurerm_storage_account.testsa.id}" + enable_blob_encryption = true + enable_file_encryption = true +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccountEncryptionSettings_basicDelete(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "production" + } +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccountEncryptionSettings_fileEncryptionDisabled(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "production" + } +} + +resource "azurerm_storage_account_encryption_settings" "custom" { + storage_account_id = "${azurerm_storage_account.testsa.id} + enable_file_encryption = false" +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccountEncryptionSettings_blobEncryptionDisabled(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "production" + } +} + +resource "azurerm_storage_account_encryption_settings" "custom" { + storage_account_id = "${azurerm_storage_account.testsa.id} + enable_blob_encryption = false" +} +`, rInt, location, rString) +} diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_test.go index 24f8b1618d89..99c1cda3a0e2 100644 --- a/azurerm/internal/services/storage/tests/resource_arm_storage_account_test.go +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_test.go @@ -3,7 +3,6 @@ package tests import ( "fmt" "net/http" - "os" "regexp" "testing" @@ -212,68 +211,6 @@ func TestAccAzureRMStorageAccount_blobConnectionString(t *testing.T) { }) } -func TestAccAzureRMStorageAccount_blobEncryption(t *testing.T) { - _, exists := os.LookupEnv("TF_ACC_STORAGE_ENCRYPTION_DISABLE") - if !exists { - t.Skip("`TF_ACC_STORAGE_ENCRYPTION_DISABLE` isn't specified - skipping since disabling encryption is generally disabled") - } - data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAzureRMStorageAccount_blobEncryption(data), - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enable_blob_encryption", "true"), - ), - }, - data.ImportStep(), - { - Config: testAccAzureRMStorageAccount_blobEncryptionDisabled(data), - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enable_blob_encryption", "false"), - ), - }, - }, - }) -} - -func TestAccAzureRMStorageAccount_fileEncryption(t *testing.T) { - _, exists := os.LookupEnv("TF_ACC_STORAGE_ENCRYPTION_DISABLE") - if !exists { - t.Skip("`TF_ACC_STORAGE_ENCRYPTION_DISABLE` isn't specified - skipping since disabling encryption is generally disabled") - } - data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAzureRMStorageAccount_fileEncryption(data), - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enable_file_encryption", "true"), - ), - }, - data.ImportStep(), - { - Config: testAccAzureRMStorageAccount_fileEncryptionDisabled(data), - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enable_file_encryption", "false"), - ), - }, - }, - }) -} - func TestAccAzureRMStorageAccount_enableHttpsTrafficOnly(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") @@ -823,98 +760,6 @@ resource "azurerm_storage_account" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString) } -func testAccAzureRMStorageAccount_blobEncryption(data acceptance.TestData) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-storage-%d" - location = "%s" -} - -resource "azurerm_storage_account" "test" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.test.name}" - - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" - enable_blob_encryption = true - - tags = { - environment = "production" - } -} -`, data.RandomInteger, data.Locations.Primary, data.RandomString) -} - -func testAccAzureRMStorageAccount_blobEncryptionDisabled(data acceptance.TestData) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-storage-%d" - location = "%s" -} - -resource "azurerm_storage_account" "test" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.test.name}" - - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" - enable_blob_encryption = false - - tags = { - environment = "production" - } -} -`, data.RandomInteger, data.Locations.Primary, data.RandomString) -} - -func testAccAzureRMStorageAccount_fileEncryption(data acceptance.TestData) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-storage-%d" - location = "%s" -} - -resource "azurerm_storage_account" "test" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.test.name}" - - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" - enable_file_encryption = true - - tags = { - environment = "production" - } -} -`, data.RandomInteger, data.Locations.Primary, data.RandomString) -} - -func testAccAzureRMStorageAccount_fileEncryptionDisabled(data acceptance.TestData) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-storage-%d" - location = "%s" -} - -resource "azurerm_storage_account" "test" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.test.name}" - - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" - enable_file_encryption = false - - tags = { - environment = "production" - } -} -`, data.RandomInteger, data.Locations.Primary, data.RandomString) -} - func testAccAzureRMStorageAccount_enableHttpsTrafficOnly(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/website/docs/r/storage_account.html.markdown b/website/docs/r/storage_account.html.markdown index b62d07a008e0..068392892101 100644 --- a/website/docs/r/storage_account.html.markdown +++ b/website/docs/r/storage_account.html.markdown @@ -92,17 +92,11 @@ The following arguments are supported: * `access_tier` - (Optional) Defines the access tier for `BlobStorage`, `FileStorage` and `StorageV2` accounts. Valid options are `Hot` and `Cool`, defaults to `Hot`. -* `enable_blob_encryption` - (Optional) Boolean flag which controls if Encryption Services are enabled for Blob storage, see [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) for more information. Defaults to `true`. - -* `enable_file_encryption` - (Optional) Boolean flag which controls if Encryption Services are enabled for File storage, see [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) for more information. Defaults to `true`. - * `enable_https_traffic_only` - (Optional) Boolean flag which forces HTTPS if enabled, see [here](https://docs.microsoft.com/en-us/azure/storage/storage-require-secure-transfer/) for more information. * `is_hns_enabled` - (Optional) Is Hierarchical Namespace enabled? This can be used with Azure Data Lake Storage Gen 2 ([see here for more information](https://docs.microsoft.com/en-us/azure/storage/blobs/data-lake-storage-quickstart-create-account/)). Changing this forces a new resource to be created. -* `account_encryption_source` - (Optional) The Encryption Source for this Storage Account. Possible values are `Microsoft.Keyvault` and `Microsoft.Storage`. Defaults to `Microsoft.Storage`. - * `custom_domain` - (Optional) A `custom_domain` block as documented below. * `enable_advanced_threat_protection` (Optional) Boolean flag which controls if advanced threat protection is enabled, see [here](https://docs.microsoft.com/en-us/azure/storage/common/storage-advanced-threat-protection) for more information. Defaults to `false`. diff --git a/website/docs/r/storage_account_encryption_settings.html.markdown b/website/docs/r/storage_account_encryption_settings.html.markdown new file mode 100644 index 000000000000..88680d48328a --- /dev/null +++ b/website/docs/r/storage_account_encryption_settings.html.markdown @@ -0,0 +1,133 @@ +--- +subcategory: "Storage" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_account_encryption_settings" +description: |- + Manages the encryption settings of an Azure Storage Account. +--- + +# azurerm_storage_account_encryption_settings + +Manages the encryption settings of an Azure Storage Account. + +## Example Usage + +```hcl +resource "azurerm_storage_account_encryption_settings" "tfex" { + storage_account_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.Storage/storageAccounts/tfexstorageaccount" + + key_vault { + key_vault_policy_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault/objectId/00000000-0000-0000-0000-000000000000" + key_vault_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault" + key_name = "tfex-key" + key_version = "955b9ad9579e4501a311df5493bacd02" + } +} +``` + +## Example Usage with User Managed Key Vault Key + +```hcl +resource "azurerm_resource_group" "tfex" { + name = "tfex-RG" + location = "westeurope" +} + +resource "azurerm_key_vault" "tfex" { + name = "tfex-key-vault" + location = "${azurerm_resource_group.tfex.location}" + resource_group_name = "${azurerm_resource_group.tfex.name}" + enabled_for_disk_encryption = true + tenant_id = "00000000-0000-0000-0000-000000000000" + + sku { + name = "standard" + } + + tags { + environment = "testing" + } +} + +resource "azurerm_key_vault_key" "tfex" { + name = "tfex-key" + key_vault_id = "${azurerm_key_vault.tfex.id}" + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] +} + +resource "azurerm_key_vault_access_policy" "tfex" { + key_vault_id = "${azurerm_key_vault.tfex.id}" + tenant_id = "00000000-0000-0000-0000-000000000000" + object_id = "${azurerm_storage_account.tfex.identity.0.principal_id}" + + key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] + secret_permissions = ["get"] +} + +resource "azurerm_storage_account" "tfex" { + name = "tfexstorageaccount" + resource_group_name = "${azurerm_resource_group.tfex.name}" + location = "${azurerm_resource_group.tfex.location}" + account_tier = "Standard" + account_replication_type = "GRS" + + identity { + type = "SystemAssigned" + } + + tags { + environment = "testing" + } +} + +resource "azurerm_storage_account_encryption_settings" "tfex" { + storage_account_id = "${azurerm_storage_account.tfex.id}" + + key_vault { + key_vault_policy_id = "${azurerm_key_vault_access_policy.tfex.id}" + key_vault_id = "${azurerm_key_vault.tfex.id}" + key_name = "${azurerm_key_vault_key.tfex.name}" + key_version = "${azurerm_key_vault_key.tfex.version}" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `storage_account_id` - (Required) The id of the storage account to manage the encryption settings for. + +* `enable_blob_encryption` - (Optional) Boolean flag which controls if Encryption Services are enabled for Blob storage, see [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) for more information. Defaults to `true`. + +* `enable_file_encryption` - (Optional) Boolean flag which controls if Encryption Services are enabled for File storage, see [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) for more information. Defaults to `true`. + +* `key_vault` - (Optional) A `key_vault` block as documented below. + +--- + +* `key_vault` supports the following: + +* `key_vault_id` - (Required) The ID of the Key Vault. +* `key_vault_policy_id` - (Required) The resource ID of the `azurerm_key_vault_access_policy` granting the storage account access to the key vault. +* `key_name` - (Required) The name of Key Vault key. +* `key_version` - (Required) The version of Key Vault key. + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `id` - The storage account encryption settings Resource ID. +* `key_vault_uri` - The base URI of the Key Vault. + +--- + +## Import + +Storage Accounts Encryption Settings can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_storage_account_encryption_settings.tfex /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Storage/storageAccounts/myaccount +``` \ No newline at end of file From 8f9d064990fb90efd72b0cdce8f440cee244b2ae Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Mon, 10 Feb 2020 16:12:07 -0800 Subject: [PATCH 02/23] Got it to compile --- .../internal/services/storage/registration.go | 23 +++---- .../storage/resource_arm_storage_account.go | 6 -- ...arm_storage_account_encryption_settings.go | 63 +++++++++++-------- ...torage_account_encryption_settings_test.go | 2 +- ..._account_encryption_settings.html.markdown | 48 ++++++++------ 5 files changed, 81 insertions(+), 61 deletions(-) diff --git a/azurerm/internal/services/storage/registration.go b/azurerm/internal/services/storage/registration.go index bdf608f1635c..bc6bb490477a 100644 --- a/azurerm/internal/services/storage/registration.go +++ b/azurerm/internal/services/storage/registration.go @@ -25,16 +25,17 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "azurerm_storage_account": resourceArmStorageAccount(), - "azurerm_storage_account_network_rules": resourceArmStorageAccountNetworkRules(), - "azurerm_storage_blob": resourceArmStorageBlob(), - "azurerm_storage_container": resourceArmStorageContainer(), - "azurerm_storage_data_lake_gen2_filesystem": resourceArmStorageDataLakeGen2FileSystem(), - "azurerm_storage_management_policy": resourceArmStorageManagementPolicy(), - "azurerm_storage_queue": resourceArmStorageQueue(), - "azurerm_storage_share": resourceArmStorageShare(), - "azurerm_storage_share_directory": resourceArmStorageShareDirectory(), - "azurerm_storage_table": resourceArmStorageTable(), - "azurerm_storage_table_entity": resourceArmStorageTableEntity(), + "azurerm_storage_account": resourceArmStorageAccount(), + "azurerm_storage_account_encryption_settings": resourceArmStorageAccountEncryptionSettings(), + "azurerm_storage_account_network_rules": resourceArmStorageAccountNetworkRules(), + "azurerm_storage_blob": resourceArmStorageBlob(), + "azurerm_storage_container": resourceArmStorageContainer(), + "azurerm_storage_data_lake_gen2_filesystem": resourceArmStorageDataLakeGen2FileSystem(), + "azurerm_storage_management_policy": resourceArmStorageManagementPolicy(), + "azurerm_storage_queue": resourceArmStorageQueue(), + "azurerm_storage_share": resourceArmStorageShare(), + "azurerm_storage_share_directory": resourceArmStorageShareDirectory(), + "azurerm_storage_table": resourceArmStorageTable(), + "azurerm_storage_table_entity": resourceArmStorageTableEntity(), } } diff --git a/azurerm/internal/services/storage/resource_arm_storage_account.go b/azurerm/internal/services/storage/resource_arm_storage_account.go index c6a48bd2caac..6ce053f5425d 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account.go @@ -664,7 +664,6 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e Tags: tags.Expand(t), Kind: storage.Kind(accountKind), AccountPropertiesCreateParameters: &storage.AccountPropertiesCreateParameters{ - Encryption: &storage.Encryption{ EnableHTTPSTrafficOnly: &enableHTTPSTrafficOnly, NetworkRuleSet: expandStorageAccountNetworkRules(d), IsHnsEnabled: &isHnsEnabled, @@ -868,11 +867,6 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e d.SetPartial("tags") } - if _, err := client.Update(ctx, resourceGroupName, storageAccountName, opts); err != nil { - return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) - } - } - if d.HasChange("custom_domain") { opts := storage.AccountUpdateParameters{ AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go index d536707a7398..0ba2bc5380a4 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go @@ -1,26 +1,35 @@ -package azurerm +package storage import ( "fmt" + "time" - "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage" - "github.com/hashicorp/terraform/helper/schema" + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func resourceArmStorageAccountEncryptionSettings() *schema.Resource { return &schema.Resource{ - Read: resourceArmStorageAccountEncryptionSettingsRead, - Create: resourceArmStorageAccountEncryptionSettingsCreateUpdate, - Update: resourceArmStorageAccountEncryptionSettingsCreateUpdate, - Delete: resourceArmStorageAccountEncryptionSettingsDelete, - + Read: resourceArmStorageAccountEncryptionSettingsRead, + Create: resourceArmStorageAccountEncryptionSettingsCreateUpdate, + Update: resourceArmStorageAccountEncryptionSettingsCreateUpdate, + Delete: resourceArmStorageAccountEncryptionSettingsDelete, + SchemaVersion: 2, Importer: &schema.ResourceImporter{ - State: resourceArmStorageAccountEncryptionSettingsImportState, + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), }, - SchemaVersion: 2, Schema: map[string]*schema.Schema{ "storage_account_id": { @@ -83,9 +92,10 @@ func resourceArmStorageAccountEncryptionSettings() *schema.Resource { } func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceData, meta interface{}) error { - ctx := meta.(*ArmClient).StopContext - vaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).storageServiceClient + vaultClient := meta.(*clients.Client).KeyVault.VaultsClient + storageClient := meta.(*clients.Client).Storage.AccountsClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() storageAccountId := d.Get("storage_account_id").(string) @@ -139,7 +149,7 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD } } - _, err = client.Update(ctx, resourceGroupName, storageAccountName, opts) + _, err = storageClient.Update(ctx, resourceGroupName, storageAccountName, opts) if err != nil { return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) } @@ -151,19 +161,20 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD } func resourceArmStorageAccountEncryptionSettingsRead(d *schema.ResourceData, meta interface{}) error { - ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).storageServiceClient + storageClient := meta.(*clients.Client).Storage.AccountsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() storageAccountId := d.Get("storage_account_id").(string) - id, err := parseAzureResourceID(storageAccountId) + id, err := azure.ParseAzureResourceID(storageAccountId) if err != nil { return err } name := id.Path["storageAccounts"] resGroup := id.ResourceGroup - resp, err := client.GetProperties(ctx, resGroup, name) + resp, err := storageClient.GetProperties(ctx, resGroup, name, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { d.SetId("") @@ -198,8 +209,9 @@ func resourceArmStorageAccountEncryptionSettingsRead(d *schema.ResourceData, met } func resourceArmStorageAccountEncryptionSettingsDelete(d *schema.ResourceData, meta interface{}) error { - ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).storageServiceClient + storageClient := meta.(*clients.Client).Storage.AccountsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() storageAccountId := d.Get("storage_account_id").(string) @@ -223,7 +235,7 @@ func resourceArmStorageAccountEncryptionSettingsDelete(d *schema.ResourceData, m }, } - _, err = client.Update(ctx, resourceGroupName, storageAccountName, opts) + _, err = storageClient.Update(ctx, resourceGroupName, storageAccountName, opts) if err != nil { return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) } @@ -232,21 +244,22 @@ func resourceArmStorageAccountEncryptionSettingsDelete(d *schema.ResourceData, m } func resourceArmStorageAccountEncryptionSettingsImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).storageServiceClient + storageClient := meta.(*clients.Client).Storage.AccountsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() id := d.Id() d.Set("storage_account_id", id) - saId, err := parseAzureResourceID(id) + saId, err := azure.ParseAzureResourceID(id) if err != nil { return nil, err } name := saId.Path["storageAccounts"] resGroup := saId.ResourceGroup - resp, err := client.GetProperties(ctx, resGroup, name) + resp, err := storageClient.GetProperties(ctx, resGroup, name, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { d.SetId("") diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go index 751b0b733fa6..cc994667f940 100644 --- a/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go @@ -6,7 +6,7 @@ import ( "os" "testing" - "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage" + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" diff --git a/website/docs/r/storage_account_encryption_settings.html.markdown b/website/docs/r/storage_account_encryption_settings.html.markdown index 88680d48328a..c234b895f6ee 100644 --- a/website/docs/r/storage_account_encryption_settings.html.markdown +++ b/website/docs/r/storage_account_encryption_settings.html.markdown @@ -28,6 +28,16 @@ resource "azurerm_storage_account_encryption_settings" "tfex" { ## Example Usage with User Managed Key Vault Key ```hcl +provider "azurerm" { + alias = "keyVault" + + features { + key_vault { + purge_soft_delete_on_destroy = true + } + } +} + resource "azurerm_resource_group" "tfex" { name = "tfex-RG" location = "westeurope" @@ -35,32 +45,34 @@ resource "azurerm_resource_group" "tfex" { resource "azurerm_key_vault" "tfex" { name = "tfex-key-vault" - location = "${azurerm_resource_group.tfex.location}" - resource_group_name = "${azurerm_resource_group.tfex.name}" - enabled_for_disk_encryption = true + provider = azurerm.keyVault + location = azurerm_resource_group.tfex.location + resource_group_name = azurerm_resource_group.tfex.name tenant_id = "00000000-0000-0000-0000-000000000000" + + enabled_for_disk_encryption = true + soft_delete_enabled = true + purge_protection_enabled = false - sku { - name = "standard" - } + sku_name = "standard" - tags { + tags = { environment = "testing" } } resource "azurerm_key_vault_key" "tfex" { name = "tfex-key" - key_vault_id = "${azurerm_key_vault.tfex.id}" + key_vault_id = azurerm_key_vault.tfex.id key_type = "RSA" key_size = 2048 key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] } resource "azurerm_key_vault_access_policy" "tfex" { - key_vault_id = "${azurerm_key_vault.tfex.id}" + key_vault_id = azurerm_key_vault.tfex.id tenant_id = "00000000-0000-0000-0000-000000000000" - object_id = "${azurerm_storage_account.tfex.identity.0.principal_id}" + object_id = azurerm_storage_account.tfex.identity.0.principal_id key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] secret_permissions = ["get"] @@ -68,8 +80,8 @@ resource "azurerm_key_vault_access_policy" "tfex" { resource "azurerm_storage_account" "tfex" { name = "tfexstorageaccount" - resource_group_name = "${azurerm_resource_group.tfex.name}" - location = "${azurerm_resource_group.tfex.location}" + resource_group_name = azurerm_resource_group.tfex.name + location = azurerm_resource_group.tfex.location account_tier = "Standard" account_replication_type = "GRS" @@ -77,19 +89,19 @@ resource "azurerm_storage_account" "tfex" { type = "SystemAssigned" } - tags { + tags = { environment = "testing" } } resource "azurerm_storage_account_encryption_settings" "tfex" { - storage_account_id = "${azurerm_storage_account.tfex.id}" + storage_account_id = azurerm_storage_account.tfex.id key_vault { - key_vault_policy_id = "${azurerm_key_vault_access_policy.tfex.id}" - key_vault_id = "${azurerm_key_vault.tfex.id}" - key_name = "${azurerm_key_vault_key.tfex.name}" - key_version = "${azurerm_key_vault_key.tfex.version}" + key_vault_policy_id = azurerm_key_vault_access_policy.tfex.id + key_vault_id = azurerm_key_vault.tfex.id + key_name = azurerm_key_vault_key.tfex.name + key_version = azurerm_key_vault_key.tfex.version } } ``` From 10e222d3c890a066d6663029c6be5123ab6a0020 Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Mon, 10 Feb 2020 19:01:36 -0800 Subject: [PATCH 03/23] Mostly working --- ...arm_storage_account_encryption_settings.go | 33 ++----------------- ..._account_encryption_settings.html.markdown | 6 +--- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go index 0ba2bc5380a4..32c868cba41f 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go @@ -37,17 +37,6 @@ func resourceArmStorageAccountEncryptionSettings() *schema.Resource { Required: true, ValidateFunc: azure.ValidateResourceID, }, - "enable_blob_encryption": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - - "enable_file_encryption": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, "key_vault": { Type: schema.TypeList, @@ -107,9 +96,7 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD storageAccountName := id.Path["storageAccounts"] resourceGroupName := id.ResourceGroup - // set default values for the attributes - enableBlobEncryption := true - enableFileEncryption := true + // TODO: Validate that the key vault has both soft delete and purge protection enabled // create the update object with the default values opts := storage.AccountUpdateParameters{ @@ -117,10 +104,10 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD Encryption: &storage.Encryption{ Services: &storage.EncryptionServices{ Blob: &storage.EncryptionService{ - Enabled: utils.Bool(enableBlobEncryption), + Enabled: true, }, File: &storage.EncryptionService{ - Enabled: utils.Bool(enableFileEncryption), + Enabled: true, }}, KeySource: storage.MicrosoftStorage, KeyVaultProperties: &storage.KeyVaultProperties{}, @@ -128,11 +115,6 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD }, } - if d.HasChange("enable_blob_encryption") || d.HasChange("enable_file_encryption") { - opts.Encryption.Services.Blob.Enabled = utils.Bool(d.Get("enable_blob_encryption").(bool)) - opts.Encryption.Services.File.Enabled = utils.Bool(d.Get("enable_file_encryption").(bool)) - } - if keyVaultProperties := expandAzureRmStorageAccountKeyVaultProperties(d); keyVaultProperties.KeyName != utils.String("") { if v, ok := d.GetOk("key_vault.0.key_vault_id"); ok { // Get the key vault base URL from the key vault @@ -270,15 +252,6 @@ func resourceArmStorageAccountEncryptionSettingsImportState(d *schema.ResourceDa if props := resp.AccountProperties; props != nil { if encryption := props.Encryption; encryption != nil { - if services := encryption.Services; services != nil { - if blob := services.Blob; blob != nil { - d.Set("enable_blob_encryption", blob.Enabled) - } - if file := services.File; file != nil { - d.Set("enable_file_encryption", file.Enabled) - } - } - if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { if err := d.Set("key_vault", flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties, "", "")); err != nil { return nil, fmt.Errorf("Error flattening `key_vault_properties` on import: %+v", err) diff --git a/website/docs/r/storage_account_encryption_settings.html.markdown b/website/docs/r/storage_account_encryption_settings.html.markdown index c234b895f6ee..118ffcf34b92 100644 --- a/website/docs/r/storage_account_encryption_settings.html.markdown +++ b/website/docs/r/storage_account_encryption_settings.html.markdown @@ -52,7 +52,7 @@ resource "azurerm_key_vault" "tfex" { enabled_for_disk_encryption = true soft_delete_enabled = true - purge_protection_enabled = false + purge_protection_enabled = true sku_name = "standard" @@ -112,10 +112,6 @@ The following arguments are supported: * `storage_account_id` - (Required) The id of the storage account to manage the encryption settings for. -* `enable_blob_encryption` - (Optional) Boolean flag which controls if Encryption Services are enabled for Blob storage, see [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) for more information. Defaults to `true`. - -* `enable_file_encryption` - (Optional) Boolean flag which controls if Encryption Services are enabled for File storage, see [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) for more information. Defaults to `true`. - * `key_vault` - (Optional) A `key_vault` block as documented below. --- From b15d1b390eb42e61cbc7704e8ed59dbc73b99068 Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Tue, 11 Feb 2020 18:34:18 -0800 Subject: [PATCH 04/23] Progress --- azurerm/helpers/azure/key_vault.go | 29 +++++++++++ .../keyvault/resource_arm_key_vault_key.go | 51 +++++++------------ ...arm_storage_account_encryption_settings.go | 13 +++-- website/docs/r/key_vault.html.markdown | 11 ++-- website/docs/r/key_vault_key.html.markdown | 2 + ..._account_encryption_settings.html.markdown | 20 ++++---- 6 files changed, 72 insertions(+), 54 deletions(-) diff --git a/azurerm/helpers/azure/key_vault.go b/azurerm/helpers/azure/key_vault.go index f75368e5d237..b070b87a7160 100644 --- a/azurerm/helpers/azure/key_vault.go +++ b/azurerm/helpers/azure/key_vault.go @@ -163,3 +163,32 @@ func KeyVaultGetSoftDeletedState(ctx context.Context, client *keyvault.VaultsCli // this means we found an existing key vault that is not soft deleted return nil, nil, nil } + +func KeyVaultIsSoftDeleteAndPurgeProtected(ctx context.Context, client *keyvault.VaultsClient, keyVaultId string) bool { + id, err := ParseAzureResourceID(keyVaultId) + if err != nil { + return false + } + resourceGroup := id.ResourceGroup + name := id.Path["vaults"] + + resp, err := client.Get(ctx, resourceGroup, name) + + if props := resp.Properties; props != nil { + softDeleteEnabled := false + purgeProtectionEnabled := false + + if esd := props.EnableSoftDelete; esd != nil { + softDeleteEnabled = *esd + } + if epp := props.EnableSoftDelete; epp != nil { + purgeProtectionEnabled = *epp + } + + if softDeleteEnabled && purgeProtectionEnabled { + return true + } + } + + return false +} diff --git a/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go b/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go index 73dc13da185e..454aaa8728d7 100644 --- a/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go +++ b/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go @@ -46,23 +46,17 @@ func resourceArmKeyVaultKey() *schema.Resource { }, "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"}, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, }, - // todo remove in 2.0 - "vault_uri": { - 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: validation.IsURLWithHTTPS, - ConflictsWith: []string{"key_vault_id"}, + "key_vault_access_policy_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, }, "key_type": { @@ -174,28 +168,15 @@ func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) erro log.Print("[INFO] preparing arguments for AzureRM KeyVault Key creation.") name := d.Get("name").(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 Key %q vault url from id %q: %+v", name, keyVaultId, err) - } - - keyVaultBaseUri = pKeyVaultBaseUrl - } 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) + 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 + if features.ShouldResourcesBeImported() { existing, err := client.GetKey(ctx, keyVaultBaseUri, name, "") if err != nil { @@ -330,6 +311,8 @@ func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() + keyVaultAccessPolicyId := d.Get("key_vault_access_policy_id").(string) + id, err := azure.ParseKeyVaultChildID(d.Id()) if err != nil { return err @@ -367,7 +350,7 @@ func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error } d.Set("name", id.Name) - d.Set("vault_uri", id.KeyVaultBaseUrl) + d.Set("key_vault_access_policy_id", keyVaultAccessPolicyId) if key := resp.Key; key != nil { d.Set("key_type", string(key.Kty)) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go index 32c868cba41f..c9e61d23393a 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go @@ -96,18 +96,17 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD storageAccountName := id.Path["storageAccounts"] resourceGroupName := id.ResourceGroup - // TODO: Validate that the key vault has both soft delete and purge protection enabled - // create the update object with the default values + alwaysEnabled := true opts := storage.AccountUpdateParameters{ AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ Encryption: &storage.Encryption{ Services: &storage.EncryptionServices{ Blob: &storage.EncryptionService{ - Enabled: true, + Enabled: utils.Bool(alwaysEnabled), }, File: &storage.EncryptionService{ - Enabled: true, + Enabled: utils.Bool(alwaysEnabled), }}, KeySource: storage.MicrosoftStorage, KeyVaultProperties: &storage.KeyVaultProperties{}, @@ -125,6 +124,10 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD return fmt.Errorf("Error looking up Key Vault URI from id %q: %+v", keyVaultId, err) } + if azure.KeyVaultIsSoftDeleteAndPurgeProtected(ctx, vaultClient, keyVaultId) { + return fmt.Errorf("Key Vault %q is not configured correctly, please make sure that both 'soft_delete_enabled' and 'purge_protection_enabled' arguments are set to 'true'", keyVaultId) + } + keyVaultProperties.KeyVaultURI = utils.String(pKeyVaultBaseUrl) opts.Encryption.KeyVaultProperties = keyVaultProperties opts.Encryption.KeySource = storage.MicrosoftKeyvault @@ -219,7 +222,7 @@ func resourceArmStorageAccountEncryptionSettingsDelete(d *schema.ResourceData, m _, err = storageClient.Update(ctx, resourceGroupName, storageAccountName, opts) if err != nil { - return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) + return fmt.Errorf("Error deleting Azure Storage Account Encryption %q: %+v", storageAccountName, err) } return nil diff --git a/website/docs/r/key_vault.html.markdown b/website/docs/r/key_vault.html.markdown index 39cff382c441..3726d985fa13 100644 --- a/website/docs/r/key_vault.html.markdown +++ b/website/docs/r/key_vault.html.markdown @@ -15,6 +15,8 @@ Manages a Key Vault. ## Example Usage ```hcl +data "azurerm_client_config" "current" {} + provider "azurerm" { alias = "keyVault" @@ -36,16 +38,15 @@ resource "azurerm_key_vault" "example" { location = azurerm_resource_group.example.location resource_group_name = azurerm_resource_group.example.name - enabled_for_disk_encryption = true - tenant_id = "d6e396d0-5584-41dc-9fc0-268df99bc610" + tenant_id = data.azurerm_client_config.current.tenant_id soft_delete_enabled = true purge_protection_enabled = false sku_name = "standard" access_policy { - tenant_id = "d6e396d0-5584-41dc-9fc0-268df99bc610" - object_id = "d746815a-0433-4a21-b95d-fc437d2d475b" + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.service_principal_object_id key_permissions = [ "get", @@ -66,7 +67,7 @@ resource "azurerm_key_vault" "example" { } tags = { - environment = "Production" + environment = "Testing" } } ``` diff --git a/website/docs/r/key_vault_key.html.markdown b/website/docs/r/key_vault_key.html.markdown index 415d4af543d2..a854886a837c 100644 --- a/website/docs/r/key_vault_key.html.markdown +++ b/website/docs/r/key_vault_key.html.markdown @@ -82,6 +82,8 @@ The following arguments are supported: * `key_vault_id` - (Required) The ID of the Key Vault where the Key should be created. Changing this forces a new resource to be created. +* `key_vault_access_policy_id` - (Optional) The ID of the Key Vault Access Policy which should exist before the Key should be created. Changing this forces a new resource to be created. + * `key_type` - (Required) Specifies the Key Type to use for this Key Vault Key. Possible values are `EC` (Elliptic Curve), `EC-HSM`, `Oct` (Octet), `RSA` and `RSA-HSM`. Changing this forces a new resource to be created. * `key_size` - (Optional) Specifies the Size of the RSA key to create in bytes. For example, 1024 or 2048. *Note*: This field is required if `key_type` is `RSA` or `RSA-HSM`. Changing this forces a new resource to be created. diff --git a/website/docs/r/storage_account_encryption_settings.html.markdown b/website/docs/r/storage_account_encryption_settings.html.markdown index 118ffcf34b92..553bdcdfad95 100644 --- a/website/docs/r/storage_account_encryption_settings.html.markdown +++ b/website/docs/r/storage_account_encryption_settings.html.markdown @@ -50,9 +50,8 @@ resource "azurerm_key_vault" "tfex" { resource_group_name = azurerm_resource_group.tfex.name tenant_id = "00000000-0000-0000-0000-000000000000" - enabled_for_disk_encryption = true soft_delete_enabled = true - purge_protection_enabled = true + purge_protection_enabled = false sku_name = "standard" @@ -62,17 +61,18 @@ resource "azurerm_key_vault" "tfex" { } resource "azurerm_key_vault_key" "tfex" { - name = "tfex-key" - key_vault_id = azurerm_key_vault.tfex.id - key_type = "RSA" - key_size = 2048 - key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] + name = "tfex-key" + key_vault_id = azurerm_key_vault.tfex.id + key_vault_access_policy_id = azurerm_key_vault_access_policy.tfex.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] } resource "azurerm_key_vault_access_policy" "tfex" { - key_vault_id = azurerm_key_vault.tfex.id - tenant_id = "00000000-0000-0000-0000-000000000000" - object_id = azurerm_storage_account.tfex.identity.0.principal_id + key_vault_id = azurerm_key_vault.tfex.id + tenant_id = "00000000-0000-0000-0000-000000000000" + object_id = azurerm_storage_account.tfex.identity.0.principal_id key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] secret_permissions = ["get"] From 99b6fe5f22658bcfe23002e0cb92d4156d9fece9 Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Wed, 12 Feb 2020 14:25:52 -0800 Subject: [PATCH 05/23] Make error msg more user friendly --- ...rce_arm_storage_account_encryption_settings.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go index c9e61d23393a..c5d2a5e51fd3 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go @@ -118,17 +118,26 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD if v, ok := d.GetOk("key_vault.0.key_vault_id"); ok { // Get the key vault base URL from the key vault keyVaultId := v.(string) - pKeyVaultBaseUrl, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId) + + kvId, err := azure.ParseAzureResourceID(keyVaultId) + if err != nil { + return err + } + + keyVaultAccountName := kvId.Path["vaults"] + keyVaultResourceGroupName := kvId.ResourceGroup + + pKeyVaultBaseURL, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId) if err != nil { return fmt.Errorf("Error looking up Key Vault URI from id %q: %+v", keyVaultId, err) } if azure.KeyVaultIsSoftDeleteAndPurgeProtected(ctx, vaultClient, keyVaultId) { - return fmt.Errorf("Key Vault %q is not configured correctly, please make sure that both 'soft_delete_enabled' and 'purge_protection_enabled' arguments are set to 'true'", keyVaultId) + return fmt.Errorf("Key Vault %q (Resource Group %q) is not configured correctly, please make sure that both 'soft_delete_enabled' and 'purge_protection_enabled' arguments are set to 'true'", keyVaultAccountName, keyVaultResourceGroupName) } - keyVaultProperties.KeyVaultURI = utils.String(pKeyVaultBaseUrl) + keyVaultProperties.KeyVaultURI = utils.String(pKeyVaultBaseURL) opts.Encryption.KeyVaultProperties = keyVaultProperties opts.Encryption.KeySource = storage.MicrosoftKeyvault } From bb8ed6858fa58a5da11ba0f0d7c6d8e331ee01a6 Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Wed, 12 Feb 2020 14:56:30 -0800 Subject: [PATCH 06/23] Update error conditional --- .../storage/resource_arm_storage_account_encryption_settings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go index c5d2a5e51fd3..e03e8a77d9c3 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go @@ -133,7 +133,7 @@ func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceD return fmt.Errorf("Error looking up Key Vault URI from id %q: %+v", keyVaultId, err) } - if azure.KeyVaultIsSoftDeleteAndPurgeProtected(ctx, vaultClient, keyVaultId) { + if !azure.KeyVaultIsSoftDeleteAndPurgeProtected(ctx, vaultClient, keyVaultId) { return fmt.Errorf("Key Vault %q (Resource Group %q) is not configured correctly, please make sure that both 'soft_delete_enabled' and 'purge_protection_enabled' arguments are set to 'true'", keyVaultAccountName, keyVaultResourceGroupName) } From 1d4684a36f4e26ec7919418e37b5fe6b0ef5b659 Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Thu, 13 Feb 2020 14:56:28 -0800 Subject: [PATCH 07/23] Rename resource --- azurerm/helpers/azure/key_vault.go | 10 + azurerm/internal/acceptance/data.go | 13 +- .../internal/services/storage/registration.go | 24 +- ...rm_storage_account_customer_managed_key.go | 249 +++++++++++++ ...arm_storage_account_encryption_settings.go | 325 ----------------- ...orage_account_customer_managed_key_test.go | 221 ++++++++++++ ...torage_account_encryption_settings_test.go | 341 ------------------ ...ccount_customer_managed_key.html.markdown} | 53 +-- 8 files changed, 524 insertions(+), 712 deletions(-) create mode 100644 azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go delete mode 100644 azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go create mode 100644 azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go delete mode 100644 azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go rename website/docs/r/{storage_account_encryption_settings.html.markdown => storage_account_customer_managed_key.html.markdown} (58%) diff --git a/azurerm/helpers/azure/key_vault.go b/azurerm/helpers/azure/key_vault.go index b070b87a7160..e5e68e8ec8ef 100644 --- a/azurerm/helpers/azure/key_vault.go +++ b/azurerm/helpers/azure/key_vault.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -192,3 +193,12 @@ func KeyVaultIsSoftDeleteAndPurgeProtected(ctx context.Context, client *keyvault return false } + +func KeyVaultGetResourceNameResource(resourceId string, namePathKey string) (resourceName interface{}, resourceGroup interface{}, err error) { + id, err := azure.ParseAzureResourceID(resourceId) + if err != nil { + return nil, nil, err + } + + return id.Path[namePathKey], id.ResourceGroup, nil +} diff --git a/azurerm/internal/acceptance/data.go b/azurerm/internal/acceptance/data.go index 3e79ea88bee1..2a7ad06e102e 100644 --- a/azurerm/internal/acceptance/data.go +++ b/azurerm/internal/acceptance/data.go @@ -24,7 +24,7 @@ type TestData struct { // Locations is a set of Azure Regions which should be used for this Test Locations Regions - // RandomString is a random integer which is unique to this test case + // RandomInteger is a random integer which is unique to this test case RandomInteger int // RandomString is a random 5 character string is unique to this test case @@ -90,6 +90,7 @@ func BuildTestData(t *testing.T, resourceType string, resourceLabel string) Test return testData } +// RandomIntOfLength is a random 8 to 18 digit integer which is unique to this test case func (td *TestData) RandomIntOfLength(len int) int { // len should not be // - greater then 18, longest a int can represent @@ -116,3 +117,13 @@ func (td *TestData) RandomIntOfLength(len int) int { return i } + +// RandomStringOfLength is a random 1 to 1024 character string which is unique to this test case +func (td *TestData) RandomStringOfLength(len int) string { + // len should not be less then 1 or greater than 1024 + if 1 > len || len > 1024 { + panic(fmt.Sprintf("Invalid Test: RandomStringOfLength: length argument must be between 1 and 1024 characters")) + } + + return acctest.RandString(len) +} diff --git a/azurerm/internal/services/storage/registration.go b/azurerm/internal/services/storage/registration.go index bc6bb490477a..608eebed1313 100644 --- a/azurerm/internal/services/storage/registration.go +++ b/azurerm/internal/services/storage/registration.go @@ -25,17 +25,17 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "azurerm_storage_account": resourceArmStorageAccount(), - "azurerm_storage_account_encryption_settings": resourceArmStorageAccountEncryptionSettings(), - "azurerm_storage_account_network_rules": resourceArmStorageAccountNetworkRules(), - "azurerm_storage_blob": resourceArmStorageBlob(), - "azurerm_storage_container": resourceArmStorageContainer(), - "azurerm_storage_data_lake_gen2_filesystem": resourceArmStorageDataLakeGen2FileSystem(), - "azurerm_storage_management_policy": resourceArmStorageManagementPolicy(), - "azurerm_storage_queue": resourceArmStorageQueue(), - "azurerm_storage_share": resourceArmStorageShare(), - "azurerm_storage_share_directory": resourceArmStorageShareDirectory(), - "azurerm_storage_table": resourceArmStorageTable(), - "azurerm_storage_table_entity": resourceArmStorageTableEntity(), + "azurerm_storage_account": resourceArmStorageAccount(), + "azurerm_storage_account_customer_managed_key": resourceArmStorageAccountCustomerManagedKey(), + "azurerm_storage_account_network_rules": resourceArmStorageAccountNetworkRules(), + "azurerm_storage_blob": resourceArmStorageBlob(), + "azurerm_storage_container": resourceArmStorageContainer(), + "azurerm_storage_data_lake_gen2_filesystem": resourceArmStorageDataLakeGen2FileSystem(), + "azurerm_storage_management_policy": resourceArmStorageManagementPolicy(), + "azurerm_storage_queue": resourceArmStorageQueue(), + "azurerm_storage_share": resourceArmStorageShare(), + "azurerm_storage_share_directory": resourceArmStorageShareDirectory(), + "azurerm_storage_table": resourceArmStorageTable(), + "azurerm_storage_table_entity": resourceArmStorageTableEntity(), } } diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go new file mode 100644 index 000000000000..280717be845a --- /dev/null +++ b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go @@ -0,0 +1,249 @@ +package storage + +import ( + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmStorageAccountCustomerManagedKey() *schema.Resource { + return &schema.Resource{ + Read: resourceArmStorageAccountCustomerManagedKeyRead, + Create: resourceArmStorageAccountCustomerManagedKeyCreateUpdate, + Update: resourceArmStorageAccountCustomerManagedKeyCreateUpdate, + Delete: resourceArmStorageAccountCustomerManagedKeyDelete, + SchemaVersion: 2, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "storage_account_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "key_vault_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "key_vault_access_policy_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "key_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "key_version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "key_vault_uri": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceArmStorageAccountCustomerManagedKeyCreateUpdate(d *schema.ResourceData, meta interface{}) error { + vaultClient := meta.(*clients.Client).KeyVault.VaultsClient + storageClient := meta.(*clients.Client).Storage.AccountsClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + keyVaultId := d.Get("key_vault_id").(string) + keyName := d.Get("key_name").(string) + keyVersion := d.Get("key_version").(string) + storageAccountId := d.Get("storage_account_id").(string) + encryptionServices := true + + storageAccountName, storageAccountResourceGroupName, err := azure.KeyVaultGetResourceNameResource(storageAccountId, "storageAccounts") + if err != nil { + return err + } + + keyVaultAccountName, keyVaultResourceGroupName, err := azure.KeyVaultGetResourceNameResource(keyVaultId, "vaults") + if err != nil { + return err + } + + // First check to see if the key vault is configured correctly or not + if !azure.KeyVaultIsSoftDeleteAndPurgeProtected(ctx, vaultClient, keyVaultId) { + return fmt.Errorf("Key Vault %q (Resource Group %q) is not configured correctly, please make sure that both 'soft_delete_enabled' and 'purge_protection_enabled' arguments are set to 'true'", keyVaultAccountName, keyVaultResourceGroupName) + } + + pKeyVaultBaseURL, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId) + if err != nil { + return fmt.Errorf("Error looking up Key Vault URI from Key Vault %q (Resource Group %q): %+v", keyVaultAccountName, keyVaultResourceGroupName, err) + } + + props := storage.AccountUpdateParameters{ + AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ + Encryption: &storage.Encryption{ + Services: &storage.EncryptionServices{ + Blob: &storage.EncryptionService{ + Enabled: utils.Bool(encryptionServices), + }, + File: &storage.EncryptionService{ + Enabled: utils.Bool(encryptionServices), + }, + }, + KeySource: storage.MicrosoftKeyvault, + KeyVaultProperties: &storage.KeyVaultProperties{ + KeyName: utils.String(keyName), + KeyVersion: utils.String(keyVersion), + KeyVaultURI: utils.String(pKeyVaultBaseURL), + }, + }, + }, + } + + _, err = storageClient.Update(ctx, storageAccountResourceGroupName.(string), storageAccountName.(string), props) + if err != nil { + return fmt.Errorf("Error updating Azure Storage Account %q (Resource Group %q) Customer Managed Key : %+v", storageAccountName, storageAccountResourceGroupName, err) + } + + resourceId := fmt.Sprintf("%s/customerManagedKey", storageAccountId) + d.SetId(resourceId) + + return resourceArmStorageAccountCustomerManagedKeyRead(d, meta) +} + +func resourceArmStorageAccountCustomerManagedKeyRead(d *schema.ResourceData, meta interface{}) error { + storageClient := meta.(*clients.Client).Storage.AccountsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + storageAccountId := d.Get("storage_account_id").(string) + keyVaultId := d.Get("key_vault_id").(string) + keyVaultPolicy := d.Get("key_vault_access_policy_id").(string) + + d.Set("storage_account_id", storageAccountId) + d.Set("key_vault_id", keyVaultId) + d.Set("key_vault_access_policy_id", keyVaultPolicy) + + name, resGroup, err := azure.KeyVaultGetResourceNameResource(storageAccountId, "storageAccounts") + if err != nil { + return err + } + + resp, err := storageClient.GetProperties(ctx, resGroup.(string), name.(string), "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading the state of AzureRM Storage Account %q (Resource Group %q): %+v", name, resGroup, err) + } + + if props := resp.AccountProperties; props != nil { + if encryption := props.Encryption; encryption != nil { + if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { + d.Set("key_name", *keyVaultProperties.KeyName) + d.Set("key_version", *keyVaultProperties.KeyVersion) + d.Set("key_vault_uri", *keyVaultProperties.KeyVaultURI) + } + } + } + + return nil +} + +func resourceArmStorageAccountCustomerManagedKeyDelete(d *schema.ResourceData, meta interface{}) error { + storageClient := meta.(*clients.Client).Storage.AccountsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + storageAccountId := d.Get("storage_account_id").(string) + + storageAccountName, resourceGroupName, err := azure.KeyVaultGetResourceNameResource(storageAccountId, "storageAccounts") + if err != nil { + return err + } + + // Since this isn't a real object, just modifying an existing object + // "Delete" doesn't really make sense it should really be a "Revert to Default" + // So instead of the Delete func actually deleting the Storage Account I am + // making it reset the Storage Account to it's default state + opts := storage.AccountUpdateParameters{ + AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ + Encryption: &storage.Encryption{ + KeySource: storage.MicrosoftStorage, + }, + }, + } + + _, err = storageClient.Update(ctx, resourceGroupName.(string), storageAccountName.(string), opts) + if err != nil { + return fmt.Errorf("Error deleting AzureRM Storage Account %q (Resource Group %q) %q: %+v", storageAccountName.(string), resourceGroupName.(string), err) + } + + return nil +} + +func resourceArmStorageAccountCustomerManagedKeyImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + storageClient := meta.(*clients.Client).Storage.AccountsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id := d.Id() + + d.Set("storage_account_id", id) + d.Set("key_vault_id", "") + d.Set("key_vault_access_policy_id", "") + + name, resGroup, err := azure.KeyVaultGetResourceNameResource(id, "storageAccounts") + if err != nil { + return nil, err + } + + resp, err := storageClient.GetProperties(ctx, resGroup.(string), name.(string), "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil, nil + } + return nil, fmt.Errorf("Error importing the state of AzureRM Storage Account %q (Resource Group %q): %+v", name.(string), resGroup.(string), err) + } + + if props := resp.AccountProperties; props != nil { + if encryption := props.Encryption; encryption != nil { + if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { + d.Set("key_name", keyVaultProperties.KeyName) + d.Set("key_version", keyVaultProperties.KeyVersion) + d.Set("key_vault_uri", keyVaultProperties.KeyVaultURI) + } + } + } + + resourceId := fmt.Sprintf("%s/customerManagedKey", id) + d.SetId(resourceId) + + results := make([]*schema.ResourceData, 1) + + results[0] = d + return results, nil +} diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go b/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go deleted file mode 100644 index e03e8a77d9c3..000000000000 --- a/azurerm/internal/services/storage/resource_arm_storage_account_encryption_settings.go +++ /dev/null @@ -1,325 +0,0 @@ -package storage - -import ( - "fmt" - "time" - - "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" -) - -func resourceArmStorageAccountEncryptionSettings() *schema.Resource { - return &schema.Resource{ - Read: resourceArmStorageAccountEncryptionSettingsRead, - Create: resourceArmStorageAccountEncryptionSettingsCreateUpdate, - Update: resourceArmStorageAccountEncryptionSettingsCreateUpdate, - Delete: resourceArmStorageAccountEncryptionSettingsDelete, - SchemaVersion: 2, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(30 * time.Minute), - Read: schema.DefaultTimeout(5 * time.Minute), - Update: schema.DefaultTimeout(30 * time.Minute), - Delete: schema.DefaultTimeout(30 * time.Minute), - }, - - Schema: map[string]*schema.Schema{ - "storage_account_id": { - Type: schema.TypeString, - Required: true, - ValidateFunc: azure.ValidateResourceID, - }, - - "key_vault": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - // This attribute is not used, it was only added - // to create a dependency between this resource - // and the key vault policy - "key_vault_policy_id": { - Type: schema.TypeString, - Required: true, - ValidateFunc: azure.ValidateResourceID, - }, - - "key_vault_id": { - Type: schema.TypeString, - Required: true, - ValidateFunc: azure.ValidateResourceID, - }, - - "key_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validate.NoEmptyStrings, - }, - "key_version": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validate.NoEmptyStrings, - }, - "key_vault_uri": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - }, - } -} - -func resourceArmStorageAccountEncryptionSettingsCreateUpdate(d *schema.ResourceData, meta interface{}) error { - vaultClient := meta.(*clients.Client).KeyVault.VaultsClient - storageClient := meta.(*clients.Client).Storage.AccountsClient - ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) - defer cancel() - - storageAccountId := d.Get("storage_account_id").(string) - - id, err := azure.ParseAzureResourceID(storageAccountId) - if err != nil { - return err - } - - storageAccountName := id.Path["storageAccounts"] - resourceGroupName := id.ResourceGroup - - // create the update object with the default values - alwaysEnabled := true - opts := storage.AccountUpdateParameters{ - AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ - Encryption: &storage.Encryption{ - Services: &storage.EncryptionServices{ - Blob: &storage.EncryptionService{ - Enabled: utils.Bool(alwaysEnabled), - }, - File: &storage.EncryptionService{ - Enabled: utils.Bool(alwaysEnabled), - }}, - KeySource: storage.MicrosoftStorage, - KeyVaultProperties: &storage.KeyVaultProperties{}, - }, - }, - } - - if keyVaultProperties := expandAzureRmStorageAccountKeyVaultProperties(d); keyVaultProperties.KeyName != utils.String("") { - if v, ok := d.GetOk("key_vault.0.key_vault_id"); ok { - // Get the key vault base URL from the key vault - keyVaultId := v.(string) - - kvId, err := azure.ParseAzureResourceID(keyVaultId) - if err != nil { - return err - } - - keyVaultAccountName := kvId.Path["vaults"] - keyVaultResourceGroupName := kvId.ResourceGroup - - pKeyVaultBaseURL, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId) - - if err != nil { - return fmt.Errorf("Error looking up Key Vault URI from id %q: %+v", keyVaultId, err) - } - - if !azure.KeyVaultIsSoftDeleteAndPurgeProtected(ctx, vaultClient, keyVaultId) { - return fmt.Errorf("Key Vault %q (Resource Group %q) is not configured correctly, please make sure that both 'soft_delete_enabled' and 'purge_protection_enabled' arguments are set to 'true'", keyVaultAccountName, keyVaultResourceGroupName) - } - - keyVaultProperties.KeyVaultURI = utils.String(pKeyVaultBaseURL) - opts.Encryption.KeyVaultProperties = keyVaultProperties - opts.Encryption.KeySource = storage.MicrosoftKeyvault - } - } - - _, err = storageClient.Update(ctx, resourceGroupName, storageAccountName, opts) - if err != nil { - return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) - } - - resourceId := fmt.Sprintf("%s/encryptionSettings", storageAccountId) - d.SetId(resourceId) - - return resourceArmStorageAccountEncryptionSettingsRead(d, meta) -} - -func resourceArmStorageAccountEncryptionSettingsRead(d *schema.ResourceData, meta interface{}) error { - storageClient := meta.(*clients.Client).Storage.AccountsClient - ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) - defer cancel() - - storageAccountId := d.Get("storage_account_id").(string) - - id, err := azure.ParseAzureResourceID(storageAccountId) - if err != nil { - return err - } - name := id.Path["storageAccounts"] - resGroup := id.ResourceGroup - - resp, err := storageClient.GetProperties(ctx, resGroup, name, "") - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %+v", name, err) - } - - if props := resp.AccountProperties; props != nil { - if encryption := props.Encryption; encryption != nil { - if services := encryption.Services; services != nil { - if blob := services.Blob; blob != nil { - d.Set("enable_blob_encryption", blob.Enabled) - } - if file := services.File; file != nil { - d.Set("enable_file_encryption", file.Enabled) - } - } - - if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { - keyVaultId := d.Get("key_vault.0.key_vault_id").(string) - keyVaultPolicyId := d.Get("key_vault.0.key_vault_policy_id").(string) - - if err := d.Set("key_vault", flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties, keyVaultId, keyVaultPolicyId)); err != nil { - return fmt.Errorf("Error flattening `key_vault_properties`: %+v", err) - } - } - } - } - - return nil -} - -func resourceArmStorageAccountEncryptionSettingsDelete(d *schema.ResourceData, meta interface{}) error { - storageClient := meta.(*clients.Client).Storage.AccountsClient - ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) - defer cancel() - - storageAccountId := d.Get("storage_account_id").(string) - - id, err := azure.ParseAzureResourceID(storageAccountId) - if err != nil { - return err - } - - storageAccountName := id.Path["storageAccounts"] - resourceGroupName := id.ResourceGroup - - // Since this isn't a real object, just modifying an existing object - // "Delete" doesn't really make sense it should really be a "Revert to Default" - // So instead of the Delete func actually deleting the Storage Account I am - // making it reset the Storage Account to it's default state - opts := storage.AccountUpdateParameters{ - AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ - Encryption: &storage.Encryption{ - KeySource: storage.MicrosoftStorage, - }, - }, - } - - _, err = storageClient.Update(ctx, resourceGroupName, storageAccountName, opts) - if err != nil { - return fmt.Errorf("Error deleting Azure Storage Account Encryption %q: %+v", storageAccountName, err) - } - - return nil -} - -func resourceArmStorageAccountEncryptionSettingsImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - storageClient := meta.(*clients.Client).Storage.AccountsClient - ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) - defer cancel() - - id := d.Id() - - d.Set("storage_account_id", id) - - saId, err := azure.ParseAzureResourceID(id) - if err != nil { - return nil, err - } - name := saId.Path["storageAccounts"] - resGroup := saId.ResourceGroup - - resp, err := storageClient.GetProperties(ctx, resGroup, name, "") - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - d.SetId("") - return nil, nil - } - return nil, fmt.Errorf("Error importing the state of AzureRM Storage Account %q: %+v", name, err) - } - - if props := resp.AccountProperties; props != nil { - if encryption := props.Encryption; encryption != nil { - if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { - if err := d.Set("key_vault", flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties, "", "")); err != nil { - return nil, fmt.Errorf("Error flattening `key_vault_properties` on import: %+v", err) - } - } - } - } - - resourceId := fmt.Sprintf("%s/encryptionSettings", id) - d.SetId(resourceId) - - results := make([]*schema.ResourceData, 1) - - results[0] = d - return results, nil -} - -func expandAzureRmStorageAccountKeyVaultProperties(d *schema.ResourceData) *storage.KeyVaultProperties { - vs := d.Get("key_vault").([]interface{}) - if len(vs) == 0 { - return &storage.KeyVaultProperties{} - } - - v := vs[0].(map[string]interface{}) - keyName := v["key_name"].(string) - keyVersion := v["key_version"].(string) - - return &storage.KeyVaultProperties{ - KeyName: utils.String(keyName), - KeyVersion: utils.String(keyVersion), - } -} - -func flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties *storage.KeyVaultProperties, keyVaultId string, keyVaultPolicyId string) []interface{} { - if keyVaultProperties == nil { - return make([]interface{}, 0) - } - - result := make(map[string]interface{}) - if keyVaultId != "" { - result["key_vault_id"] = keyVaultId - } - - if keyVaultPolicyId != "" { - result["key_vault_policy_id"] = keyVaultPolicyId - } - - if keyVaultProperties.KeyName != nil { - result["key_name"] = *keyVaultProperties.KeyName - } - if keyVaultProperties.KeyVersion != nil { - result["key_version"] = *keyVaultProperties.KeyVersion - } - if keyVaultProperties.KeyVaultURI != nil { - result["key_vault_uri"] = *keyVaultProperties.KeyVaultURI - } - - return []interface{}{result} -} diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go new file mode 100644 index 000000000000..5a735cf7ba5c --- /dev/null +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go @@ -0,0 +1,221 @@ +package tests + +import ( + "fmt" + "net/http" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccAzureRMStorageAccountCustomerManagedKey_basic(t *testing.T) { + parentResourceName := "azurerm_storage_account.testsa" + data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "custom") + preConfig := testAccAzureRMStorageAccountCustomerManagedKey_basic(data) + postConfig := testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroyed, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountCustomerManagedKeyExists(data.ResourceName), + ), + }, + { + // Delete the encryption settings resource and verify it is gone + // Whilst making sure the encryption settings on the storage account + // have been reverted to their default state + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExistsWithDefaultSettings(parentResourceName), + testCheckAzureRMStorageAccountCustomerManagedKeyDestroyed(data.ResourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMStorageAccountCustomerManagedKey_disappears(t *testing.T) { + parentResourceName := "azurerm_storage_account.testsa" + data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "custom") + preConfig := testAccAzureRMStorageAccountCustomerManagedKey_basic(data) + postConfig := testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroyed, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountCustomerManagedKeyExists(data.ResourceName), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExistsWithDefaultSettings(parentResourceName), + testCheckAzureRMStorageAccountCustomerManagedKeyDestroyed(data.ResourceName), + ), + }, + }, + }) +} + +func testCheckAzureRMStorageAccountExistsWithDefaultSettings(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + storageAccount := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + // Ensure resource group exists in API + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + conn := acceptance.AzureProvider.Meta().(*clients.Client).Storage.AccountsClient + + resp, err := conn.GetProperties(ctx, resourceGroup, storageAccount, "") + if err != nil { + return fmt.Errorf("Bad: Get on storageServiceClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: StorageAccount %q (resource group: %q) does not exist", storageAccount, resourceGroup) + } + + if props := resp.AccountProperties; props != nil { + if encryption := props.Encryption; encryption != nil { + if services := encryption.Services; services != nil { + if !*services.Blob.Enabled { + return fmt.Errorf("enable_blob_encryption not set to default: %s", resourceName) + } + if !*services.File.Enabled { + return fmt.Errorf("enable_file_encryption not set to default: %s", resourceName) + } + } + + if encryption.KeySource != storage.MicrosoftStorage { + return fmt.Errorf("%s keySource not set to default(storage.MicrosoftStorage): %s", resourceName, encryption.KeySource) + } + } else { + return fmt.Errorf("storage account encryption properties not found: %s", resourceName) + } + } + + return nil + } +} + +func testCheckAzureRMStorageAccountCustomerManagedKeyExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + if storageAccountId := rs.Primary.Attributes["storage_account_id"]; storageAccountId == "" { + return fmt.Errorf("Unable to read storageAccountId: %s", resourceName) + } + + return nil + } +} + +func testCheckAzureRMStorageAccountDestroyed(s *terraform.State) error { + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + conn := acceptance.AzureProvider.Meta().(*clients.Client).Storage.AccountsClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_account" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.GetProperties(ctx, resourceGroup, name, "") + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Storage Account still exists:\n%#v", resp.AccountProperties) + } + } + + return nil +} + +func testCheckAzureRMStorageAccountCustomerManagedKeyDestroyed(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[resourceName] + if !ok { + return nil + } + + return fmt.Errorf("Found: %s", resourceName) + } +} + +func testAccAzureRMStorageAccountCustomerManagedKey_basic(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "production" + } +} + +resource "azurerm_storage_account_customer_managed_key" "custom" { + storage_account_id = "${azurerm_storage_account.testsa.id}" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomStringOfLength(4)) +} + +func testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "production" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomStringOfLength(4)) +} diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go deleted file mode 100644 index cc994667f940..000000000000 --- a/azurerm/internal/services/storage/tests/resource_arm_storage_account_encryption_settings_test.go +++ /dev/null @@ -1,341 +0,0 @@ -package azurerm - -import ( - "fmt" - "net/http" - "os" - "testing" - - "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" -) - -func TestAccAzureRMStorageAccountEncryptionSettings_basic(t *testing.T) { - parentResourceName := "azurerm_storage_account.testsa" - resourceName := "azurerm_storage_account_encryption_settings.custom" - ri := tf.AccRandTimeInt() - rs := acctest.RandString(4) - location := testLocation() - preConfig := testAccAzureRMStorageAccountEncryptionSettings_basic(ri, rs, location) - postConfig := testAccAzureRMStorageAccountEncryptionSettings_basicDelete(ri, rs, location) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroyed, - Steps: []resource.TestStep{ - { - Config: preConfig, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "enable_blob_encryption", "true"), - resource.TestCheckResourceAttr(resourceName, "enable_file_encryption", "true"), - ), - }, - { - // Delete the encryption settings resource and verify it is gone - // Whilst making sure the encryption settings on the storage account - // have been reverted to their default state - Config: postConfig, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExistsWithDefaultSettings(parentResourceName), - testCheckAzureRMStorageAccountEncryptionSettingsDestroyed(resourceName), - ), - }, - }, - }) -} - -func TestAccAzureRMStorageAccountEncryptionSettings_blobEncryptionDisable(t *testing.T) { - _, exists := os.LookupEnv("TF_ACC_STORAGE_ENCRYPTION_DISABLE") - if !exists { - t.Skip("`TF_ACC_STORAGE_ENCRYPTION_DISABLE` isn't specified - skipping since disabling encryption is generally disabled") - } - - resourceName := "azurerm_storage_account_encryption_settings.custom" - ri := tf.AccRandTimeInt() - rs := acctest.RandString(4) - location := testLocation() - config := testAccAzureRMStorageAccountEncryptionSettings_blobEncryptionDisabled(ri, rs, location) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroyed, - Steps: []resource.TestStep{ - { - Config: config, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "enable_blob_encryption", "false"), - ), - }, - }, - }) -} - -func TestAccAzureRMStorageAccountEncryptionSettings_fileEncryptionDisable(t *testing.T) { - _, exists := os.LookupEnv("TF_ACC_STORAGE_ENCRYPTION_DISABLE") - if !exists { - t.Skip("`TF_ACC_STORAGE_ENCRYPTION_DISABLE` isn't specified - skipping since disabling encryption is generally disabled") - } - - resourceName := "azurerm_storage_account_encryption_settings.custom" - ri := tf.AccRandTimeInt() - rs := acctest.RandString(4) - location := testLocation() - config := testAccAzureRMStorageAccountEncryptionSettings_fileEncryptionDisabled(ri, rs, location) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroyed, - Steps: []resource.TestStep{ - { - Config: config, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "enable_file_encryption", "false"), - ), - }, - }, - }) -} - -func TestAccAzureRMStorageAccountEncryptionSettings_disappears(t *testing.T) { - parentResourceName := "azurerm_storage_account.testsa" - resourceName := "azurerm_storage_account_encryption_settings.custom" - ri := tf.AccRandTimeInt() - rs := acctest.RandString(4) - preConfig := testAccAzureRMStorageAccountEncryptionSettings_basic(ri, rs, testLocation()) - postConfig := testAccAzureRMStorageAccountEncryptionSettings_basicDelete(ri, rs, testLocation()) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroyed, - Steps: []resource.TestStep{ - { - Config: preConfig, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "enable_blob_encryption", "true"), - resource.TestCheckResourceAttr(resourceName, "enable_file_encryption", "true"), - ), - }, - { - Config: postConfig, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExistsWithDefaultSettings(parentResourceName), - testCheckAzureRMStorageAccountEncryptionSettingsDestroyed(resourceName), - ), - }, - }, - }) -} - -func testCheckAzureRMStorageAccountExistsWithDefaultSettings(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - // Ensure we have enough information in state to look up in API - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Not found: %s", resourceName) - } - - storageAccount := rs.Primary.Attributes["name"] - resourceGroup := rs.Primary.Attributes["resource_group_name"] - - // Ensure resource group exists in API - ctx := testAccProvider.Meta().(*ArmClient).StopContext - conn := testAccProvider.Meta().(*ArmClient).storageServiceClient - - resp, err := conn.GetProperties(ctx, resourceGroup, storageAccount) - if err != nil { - return fmt.Errorf("Bad: Get on storageServiceClient: %+v", err) - } - - if resp.StatusCode == http.StatusNotFound { - return fmt.Errorf("Bad: StorageAccount %q (resource group: %q) does not exist", storageAccount, resourceGroup) - } - - if props := resp.AccountProperties; props != nil { - if encryption := props.Encryption; encryption != nil { - if services := encryption.Services; services != nil { - if !*services.Blob.Enabled { - return fmt.Errorf("enable_blob_encryption not set to default: %s", resourceName) - } - if !*services.File.Enabled { - return fmt.Errorf("enable_file_encryption not set to default: %s", resourceName) - } - } - - if encryption.KeySource != storage.MicrosoftStorage { - return fmt.Errorf("%s keySource not set to default(storage.MicrosoftStorage): %s", resourceName, encryption.KeySource) - } - } else { - return fmt.Errorf("storage account encryption properties not found: %s", resourceName) - } - } - - return nil - } -} - -func testCheckAzureRMStorageAccountEncryptionSettingsExists(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - // Ensure we have enough information in state to look up in API - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Not found: %s", resourceName) - } - - if storageAccountId := rs.Primary.Attributes["storage_account_id"]; storageAccountId == "" { - return fmt.Errorf("Unable to read storageAccountId: %s", resourceName) - } - - return nil - } -} - -func testCheckAzureRMStorageAccountDestroyed(s *terraform.State) error { - ctx := testAccProvider.Meta().(*ArmClient).StopContext - conn := testAccProvider.Meta().(*ArmClient).storageServiceClient - - for _, rs := range s.RootModule().Resources { - if rs.Type != "azurerm_storage_account" { - continue - } - - name := rs.Primary.Attributes["name"] - resourceGroup := rs.Primary.Attributes["resource_group_name"] - - resp, err := conn.GetProperties(ctx, resourceGroup, name) - if err != nil { - return nil - } - - if resp.StatusCode != http.StatusNotFound { - return fmt.Errorf("Storage Account still exists:\n%#v", resp.AccountProperties) - } - } - - return nil -} - -func testCheckAzureRMStorageAccountEncryptionSettingsDestroyed(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[resourceName] - if !ok { - return nil - } - - return fmt.Errorf("Found: %s", resourceName) - } -} - -func testAccAzureRMStorageAccountEncryptionSettings_basic(rInt int, rString string, location string) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "testrg" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_storage_account" "testsa" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.testrg.name}" - - location = "${azurerm_resource_group.testrg.location}" - account_tier = "Standard" - account_replication_type = "LRS" - - tags { - environment = "production" - } -} - -resource "azurerm_storage_account_encryption_settings" "custom" { - storage_account_id = "${azurerm_storage_account.testsa.id}" - enable_blob_encryption = true - enable_file_encryption = true -} -`, rInt, location, rString) -} - -func testAccAzureRMStorageAccountEncryptionSettings_basicDelete(rInt int, rString string, location string) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "testrg" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_storage_account" "testsa" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.testrg.name}" - - location = "${azurerm_resource_group.testrg.location}" - account_tier = "Standard" - account_replication_type = "LRS" - - tags { - environment = "production" - } -} -`, rInt, location, rString) -} - -func testAccAzureRMStorageAccountEncryptionSettings_fileEncryptionDisabled(rInt int, rString string, location string) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "testrg" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_storage_account" "testsa" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.testrg.name}" - - location = "${azurerm_resource_group.testrg.location}" - account_tier = "Standard" - account_replication_type = "LRS" - - tags { - environment = "production" - } -} - -resource "azurerm_storage_account_encryption_settings" "custom" { - storage_account_id = "${azurerm_storage_account.testsa.id} - enable_file_encryption = false" -} -`, rInt, location, rString) -} - -func testAccAzureRMStorageAccountEncryptionSettings_blobEncryptionDisabled(rInt int, rString string, location string) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "testrg" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_storage_account" "testsa" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.testrg.name}" - - location = "${azurerm_resource_group.testrg.location}" - account_tier = "Standard" - account_replication_type = "LRS" - - tags { - environment = "production" - } -} - -resource "azurerm_storage_account_encryption_settings" "custom" { - storage_account_id = "${azurerm_storage_account.testsa.id} - enable_blob_encryption = false" -} -`, rInt, location, rString) -} diff --git a/website/docs/r/storage_account_encryption_settings.html.markdown b/website/docs/r/storage_account_customer_managed_key.html.markdown similarity index 58% rename from website/docs/r/storage_account_encryption_settings.html.markdown rename to website/docs/r/storage_account_customer_managed_key.html.markdown index 553bdcdfad95..abfb32696860 100644 --- a/website/docs/r/storage_account_encryption_settings.html.markdown +++ b/website/docs/r/storage_account_customer_managed_key.html.markdown @@ -1,27 +1,24 @@ --- subcategory: "Storage" layout: "azurerm" -page_title: "Azure Resource Manager: azurerm_storage_account_encryption_settings" +page_title: "Azure Resource Manager: azurerm_storage_account_customer_managed_key" description: |- - Manages the encryption settings of an Azure Storage Account. + Manages the customer managed key of an Azure Storage Account. --- -# azurerm_storage_account_encryption_settings +# azurerm_storage_account_customer_managed_key -Manages the encryption settings of an Azure Storage Account. +Manages the customer managed key of an Azure Storage Account. ## Example Usage ```hcl -resource "azurerm_storage_account_encryption_settings" "tfex" { - storage_account_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.Storage/storageAccounts/tfexstorageaccount" - - key_vault { - key_vault_policy_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault/objectId/00000000-0000-0000-0000-000000000000" - key_vault_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault" - key_name = "tfex-key" - key_version = "955b9ad9579e4501a311df5493bacd02" - } +resource "azurerm_storage_account_customer_managed_key" "tfex" { + storage_account_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.Storage/storageAccounts/tfexstorageaccount" + key_vault_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault" + key_vault_access_policy_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault/objectId/00000000-0000-0000-0000-000000000000" + key_name = "tfex-key" + key_version = "955b9ad9579e4501a311df5493bacd02" } ``` @@ -33,7 +30,7 @@ provider "azurerm" { features { key_vault { - purge_soft_delete_on_destroy = true + purge_soft_delete_on_destroy = false } } } @@ -51,7 +48,7 @@ resource "azurerm_key_vault" "tfex" { tenant_id = "00000000-0000-0000-0000-000000000000" soft_delete_enabled = true - purge_protection_enabled = false + purge_protection_enabled = true sku_name = "standard" @@ -94,15 +91,12 @@ resource "azurerm_storage_account" "tfex" { } } -resource "azurerm_storage_account_encryption_settings" "tfex" { - storage_account_id = azurerm_storage_account.tfex.id - - key_vault { - key_vault_policy_id = azurerm_key_vault_access_policy.tfex.id - key_vault_id = azurerm_key_vault.tfex.id - key_name = azurerm_key_vault_key.tfex.name - key_version = azurerm_key_vault_key.tfex.version - } +resource "azurerm_storage_account_customer_managed_key" "tfex" { + storage_account_id = azurerm_storage_account.tfex.id + key_vault_id = azurerm_key_vault.tfex.id + key_vault_access_policy_id = azurerm_key_vault_access_policy.tfex.id + key_name = azurerm_key_vault_key.tfex.name + key_version = azurerm_key_vault_key.tfex.version } ``` @@ -111,15 +105,8 @@ resource "azurerm_storage_account_encryption_settings" "tfex" { The following arguments are supported: * `storage_account_id` - (Required) The id of the storage account to manage the encryption settings for. - -* `key_vault` - (Optional) A `key_vault` block as documented below. - ---- - -* `key_vault` supports the following: - * `key_vault_id` - (Required) The ID of the Key Vault. -* `key_vault_policy_id` - (Required) The resource ID of the `azurerm_key_vault_access_policy` granting the storage account access to the key vault. +* `key_vault_access_policy_id` - (Required) The resource ID of the `azurerm_key_vault_access_policy` granting the storage account access to the key vault. * `key_name` - (Required) The name of Key Vault key. * `key_version` - (Required) The version of Key Vault key. @@ -137,5 +124,5 @@ The following attributes are exported in addition to the arguments listed above: Storage Accounts Encryption Settings can be imported using the `resource id`, e.g. ```shell -terraform import azurerm_storage_account_encryption_settings.tfex /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Storage/storageAccounts/myaccount +terraform import azurerm_storage_account_customer_managed_key.tfex /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Storage/storageAccounts/myaccount ``` \ No newline at end of file From a86c893394a4784d66286fc6f1ca6c6355b6c46e Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Thu, 13 Feb 2020 15:04:28 -0800 Subject: [PATCH 08/23] Add SACMK doc to TOC --- website/azurerm.erb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/azurerm.erb b/website/azurerm.erb index 4301935ec7ab..e03d2a220a19 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -2185,6 +2185,10 @@ azurerm_storage_account +
  • + azurerm_storage_account_customer_managed_key +
  • +
  • azurerm_storage_account_network_rules
  • From cf39a6e4617ec8e72b9ec7bcf0418a42c990c080 Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Thu, 13 Feb 2020 15:52:52 -0800 Subject: [PATCH 09/23] Fixed import issue --- azurerm/helpers/azure/key_vault.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azurerm/helpers/azure/key_vault.go b/azurerm/helpers/azure/key_vault.go index e5e68e8ec8ef..d35fa9400c2c 100644 --- a/azurerm/helpers/azure/key_vault.go +++ b/azurerm/helpers/azure/key_vault.go @@ -7,7 +7,6 @@ import ( "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -195,7 +194,7 @@ func KeyVaultIsSoftDeleteAndPurgeProtected(ctx context.Context, client *keyvault } func KeyVaultGetResourceNameResource(resourceId string, namePathKey string) (resourceName interface{}, resourceGroup interface{}, err error) { - id, err := azure.ParseAzureResourceID(resourceId) + id, err := ParseAzureResourceID(resourceId) if err != nil { return nil, nil, err } From 5d7f77b76cca75916ae94f169bc7224ceff5c5b5 Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Thu, 13 Feb 2020 18:50:46 -0800 Subject: [PATCH 10/23] Updates --- .../keyvault/data_source_key_vault_key.go | 46 ++++--------------- .../keyvault/resource_arm_key_vault_key.go | 4 +- ...rm_storage_account_customer_managed_key.go | 32 ++++++------- ...account_customer_managed_key.html.markdown | 11 ++--- 4 files changed, 31 insertions(+), 62 deletions(-) diff --git a/azurerm/internal/services/keyvault/data_source_key_vault_key.go b/azurerm/internal/services/keyvault/data_source_key_vault_key.go index b1d672a650e0..2632b6f01572 100644 --- a/azurerm/internal/services/keyvault/data_source_key_vault_key.go +++ b/azurerm/internal/services/keyvault/data_source_key_vault_key.go @@ -5,7 +5,6 @@ import ( "time" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" @@ -29,21 +28,13 @@ func dataSourceArmKeyVaultKey() *schema.Resource { }, "key_vault_id": { - Type: schema.TypeString, - Optional: true, //todo required in 2.0 - Computed: true, //todo removed in 2.0 - ValidateFunc: azure.ValidateResourceID, - ConflictsWith: []string{"vault_uri"}, + Type: schema.TypeString, + Computed: true, }, - // todo remove in 2.0 - "vault_uri": { - Type: schema.TypeString, - Optional: 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: validation.IsURLWithHTTPS, - ConflictsWith: []string{"key_vault_id"}, + "key_vault_access_policy_id": { + Type: schema.TypeString, + Computed: true, }, "key_type": { @@ -90,33 +81,16 @@ func dataSourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) erro ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - 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 retrieving the Resource ID the Key Vault at URL %q: %s", keyVaultBaseUri, err) - } - if id == nil { - return fmt.Errorf("Unable to locate the Resource ID for the Key Vault at URL %q: %s", keyVaultBaseUri, err) - } - d.Set("key_vault_id", id) + keyVaultBaseUri, 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) } + d.Set("key_vault_id", keyVaultId) + resp, err := client.GetKey(ctx, keyVaultBaseUri, name, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { diff --git a/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go b/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go index 454aaa8728d7..b48a213873da 100644 --- a/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go +++ b/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go @@ -170,13 +170,11 @@ func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) erro name := d.Get("name").(string) keyVaultId := d.Get("key_vault_id").(string) - pKeyVaultBaseUrl, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId) + keyVaultBaseUri, 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 - if features.ShouldResourcesBeImported() { existing, err := client.GetKey(ctx, keyVaultBaseUri, name, "") if err != nil { diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go index 280717be845a..3291bc3ef832 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go @@ -44,12 +44,6 @@ func resourceArmStorageAccountCustomerManagedKey() *schema.Resource { ValidateFunc: azure.ValidateResourceID, }, - "key_vault_access_policy_id": { - Type: schema.TypeString, - Required: true, - ValidateFunc: azure.ValidateResourceID, - }, - "key_name": { Type: schema.TypeString, Required: true, @@ -121,9 +115,9 @@ func resourceArmStorageAccountCustomerManagedKeyCreateUpdate(d *schema.ResourceD }, } - _, err = storageClient.Update(ctx, storageAccountResourceGroupName.(string), storageAccountName.(string), props) + _, err := storageClient.Update(ctx, storageAccountResourceGroupName.(string), storageAccountName.(string), props) if err != nil { - return fmt.Errorf("Error updating Azure Storage Account %q (Resource Group %q) Customer Managed Key : %+v", storageAccountName, storageAccountResourceGroupName, err) + return fmt.Errorf("Error updating Azure Storage Account %q (Resource Group %q) Customer Managed Key: %+v", storageAccountName, storageAccountResourceGroupName, err) } resourceId := fmt.Sprintf("%s/customerManagedKey", storageAccountId) @@ -139,11 +133,9 @@ func resourceArmStorageAccountCustomerManagedKeyRead(d *schema.ResourceData, met storageAccountId := d.Get("storage_account_id").(string) keyVaultId := d.Get("key_vault_id").(string) - keyVaultPolicy := d.Get("key_vault_access_policy_id").(string) d.Set("storage_account_id", storageAccountId) d.Set("key_vault_id", keyVaultId) - d.Set("key_vault_access_policy_id", keyVaultPolicy) name, resGroup, err := azure.KeyVaultGetResourceNameResource(storageAccountId, "storageAccounts") if err != nil { @@ -178,6 +170,7 @@ func resourceArmStorageAccountCustomerManagedKeyDelete(d *schema.ResourceData, m defer cancel() storageAccountId := d.Get("storage_account_id").(string) + encryptionServices := true storageAccountName, resourceGroupName, err := azure.KeyVaultGetResourceNameResource(storageAccountId, "storageAccounts") if err != nil { @@ -188,15 +181,23 @@ func resourceArmStorageAccountCustomerManagedKeyDelete(d *schema.ResourceData, m // "Delete" doesn't really make sense it should really be a "Revert to Default" // So instead of the Delete func actually deleting the Storage Account I am // making it reset the Storage Account to it's default state - opts := storage.AccountUpdateParameters{ + props := storage.AccountUpdateParameters{ AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ Encryption: &storage.Encryption{ + Services: &storage.EncryptionServices{ + Blob: &storage.EncryptionService{ + Enabled: utils.Bool(encryptionServices), + }, + File: &storage.EncryptionService{ + Enabled: utils.Bool(encryptionServices), + }, + }, KeySource: storage.MicrosoftStorage, }, }, } - _, err = storageClient.Update(ctx, resourceGroupName.(string), storageAccountName.(string), opts) + _, err = storageClient.Update(ctx, resourceGroupName.(string), storageAccountName.(string), props) if err != nil { return fmt.Errorf("Error deleting AzureRM Storage Account %q (Resource Group %q) %q: %+v", storageAccountName.(string), resourceGroupName.(string), err) } @@ -213,20 +214,19 @@ func resourceArmStorageAccountCustomerManagedKeyImportState(d *schema.ResourceDa d.Set("storage_account_id", id) d.Set("key_vault_id", "") - d.Set("key_vault_access_policy_id", "") - name, resGroup, err := azure.KeyVaultGetResourceNameResource(id, "storageAccounts") + name, resourceGroup, err := azure.KeyVaultGetResourceNameResource(id, "storageAccounts") if err != nil { return nil, err } - resp, err := storageClient.GetProperties(ctx, resGroup.(string), name.(string), "") + resp, err := storageClient.GetProperties(ctx, resourceGroup.(string), name.(string), "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { d.SetId("") return nil, nil } - return nil, fmt.Errorf("Error importing the state of AzureRM Storage Account %q (Resource Group %q): %+v", name.(string), resGroup.(string), err) + return nil, fmt.Errorf("Error importing the state of AzureRM Storage Account %q (Resource Group %q): %+v", name.(string), resourceGroup.(string), err) } if props := resp.AccountProperties; props != nil { diff --git a/website/docs/r/storage_account_customer_managed_key.html.markdown b/website/docs/r/storage_account_customer_managed_key.html.markdown index abfb32696860..8210f51f5baf 100644 --- a/website/docs/r/storage_account_customer_managed_key.html.markdown +++ b/website/docs/r/storage_account_customer_managed_key.html.markdown @@ -16,7 +16,6 @@ Manages the customer managed key of an Azure Storage Account. resource "azurerm_storage_account_customer_managed_key" "tfex" { storage_account_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.Storage/storageAccounts/tfexstorageaccount" key_vault_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault" - key_vault_access_policy_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault/objectId/00000000-0000-0000-0000-000000000000" key_name = "tfex-key" key_version = "955b9ad9579e4501a311df5493bacd02" } @@ -92,11 +91,10 @@ resource "azurerm_storage_account" "tfex" { } resource "azurerm_storage_account_customer_managed_key" "tfex" { - storage_account_id = azurerm_storage_account.tfex.id - key_vault_id = azurerm_key_vault.tfex.id - key_vault_access_policy_id = azurerm_key_vault_access_policy.tfex.id - key_name = azurerm_key_vault_key.tfex.name - key_version = azurerm_key_vault_key.tfex.version + storage_account_id = azurerm_storage_account.tfex.id + key_vault_id = azurerm_key_vault.tfex.id + key_name = azurerm_key_vault_key.tfex.name + key_version = azurerm_key_vault_key.tfex.version } ``` @@ -106,7 +104,6 @@ The following arguments are supported: * `storage_account_id` - (Required) The id of the storage account to manage the encryption settings for. * `key_vault_id` - (Required) The ID of the Key Vault. -* `key_vault_access_policy_id` - (Required) The resource ID of the `azurerm_key_vault_access_policy` granting the storage account access to the key vault. * `key_name` - (Required) The name of Key Vault key. * `key_version` - (Required) The version of Key Vault key. From 3a45d8e6dcce3a2174de7d5b7d30aa6dc664a56c Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Fri, 14 Feb 2020 12:57:55 -0800 Subject: [PATCH 11/23] Updated doc --- ...account_customer_managed_key.html.markdown | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/website/docs/r/storage_account_customer_managed_key.html.markdown b/website/docs/r/storage_account_customer_managed_key.html.markdown index 8210f51f5baf..6c855704bb51 100644 --- a/website/docs/r/storage_account_customer_managed_key.html.markdown +++ b/website/docs/r/storage_account_customer_managed_key.html.markdown @@ -13,17 +13,8 @@ Manages the customer managed key of an Azure Storage Account. ## Example Usage ```hcl -resource "azurerm_storage_account_customer_managed_key" "tfex" { - storage_account_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.Storage/storageAccounts/tfexstorageaccount" - key_vault_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tfex-RG/providers/Microsoft.KeyVault/vaults/tfex-key-vault" - key_name = "tfex-key" - key_version = "955b9ad9579e4501a311df5493bacd02" -} -``` +data "azurerm_client_config" "current" {} -## Example Usage with User Managed Key Vault Key - -```hcl provider "azurerm" { alias = "keyVault" @@ -44,7 +35,7 @@ resource "azurerm_key_vault" "tfex" { provider = azurerm.keyVault location = azurerm_resource_group.tfex.location resource_group_name = azurerm_resource_group.tfex.name - tenant_id = "00000000-0000-0000-0000-000000000000" + tenant_id = data.azurerm_client_config.current.tenant_id soft_delete_enabled = true purge_protection_enabled = true @@ -59,15 +50,24 @@ resource "azurerm_key_vault" "tfex" { resource "azurerm_key_vault_key" "tfex" { name = "tfex-key" key_vault_id = azurerm_key_vault.tfex.id - key_vault_access_policy_id = azurerm_key_vault_access_policy.tfex.id + key_vault_access_policy_id = azurerm_key_vault_access_policy.storage.id key_type = "RSA" key_size = 2048 key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] } -resource "azurerm_key_vault_access_policy" "tfex" { +resource "azurerm_key_vault_access_policy" "storage" { + key_vault_id = azurerm_key_vault.tfex.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = azurerm_storage_account.tfex.identity.0.principal_id + + key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] + secret_permissions = ["get"] +} + +resource "azurerm_key_vault_access_policy" "client" { key_vault_id = azurerm_key_vault.tfex.id - tenant_id = "00000000-0000-0000-0000-000000000000" + tenant_id = data.azurerm_client_config.current.tenant_id object_id = azurerm_storage_account.tfex.identity.0.principal_id key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] From 1668aa3be036ee992cbf8eaa25df908592c299aa Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Fri, 14 Feb 2020 13:56:15 -0800 Subject: [PATCH 12/23] Format doc hcl blocks --- ...rm_storage_account_customer_managed_key.go | 2 +- website/docs/r/key_vault.html.markdown | 21 +++++-------- ...account_customer_managed_key.html.markdown | 30 +++++++++---------- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go index 3291bc3ef832..f56c98db9a7a 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go @@ -115,7 +115,7 @@ func resourceArmStorageAccountCustomerManagedKeyCreateUpdate(d *schema.ResourceD }, } - _, err := storageClient.Update(ctx, storageAccountResourceGroupName.(string), storageAccountName.(string), props) + _, err = storageClient.Update(ctx, storageAccountResourceGroupName.(string), storageAccountName.(string), props) if err != nil { return fmt.Errorf("Error updating Azure Storage Account %q (Resource Group %q) Customer Managed Key: %+v", storageAccountName, storageAccountResourceGroupName, err) } diff --git a/website/docs/r/key_vault.html.markdown b/website/docs/r/key_vault.html.markdown index 3726d985fa13..9da136c4273e 100644 --- a/website/docs/r/key_vault.html.markdown +++ b/website/docs/r/key_vault.html.markdown @@ -33,14 +33,14 @@ resource "azurerm_resource_group" "example" { } resource "azurerm_key_vault" "example" { - name = "testvault" - provider = azurerm.keyVault - location = azurerm_resource_group.example.location - resource_group_name = azurerm_resource_group.example.name + name = "testvault" + provider = azurerm.keyVault + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name - tenant_id = data.azurerm_client_config.current.tenant_id - soft_delete_enabled = true - purge_protection_enabled = false + tenant_id = data.azurerm_client_config.current.tenant_id + soft_delete_enabled = true + purge_protection_enabled = false sku_name = "standard" @@ -82,8 +82,6 @@ The following arguments are supported: * `resource_group_name` - (Required) The name of the resource group in which to create the Key Vault. Changing this forces a new resource to be created. -* `sku` - (Optional **Deprecated**)) A `sku` block as described below. - * `sku_name` - (Optional) The Name of the SKU used for this Key Vault. Possible values are `standard` and `premium`. * `tenant_id` - (Required) The Azure Active Directory tenant ID that should be used for authenticating requests to the key vault. @@ -110,11 +108,6 @@ The following arguments are supported: * `tags` - (Optional) A mapping of tags to assign to the resource. ---- -A `sku` block supports the following: - -* `name` - (Required) The Name of the SKU used for this Key Vault. Possible values are `standard` and `premium`. - --- A `access_policy` block supports the following: diff --git a/website/docs/r/storage_account_customer_managed_key.html.markdown b/website/docs/r/storage_account_customer_managed_key.html.markdown index 6c855704bb51..74058b8101a9 100644 --- a/website/docs/r/storage_account_customer_managed_key.html.markdown +++ b/website/docs/r/storage_account_customer_managed_key.html.markdown @@ -31,14 +31,14 @@ resource "azurerm_resource_group" "tfex" { } resource "azurerm_key_vault" "tfex" { - name = "tfex-key-vault" - provider = azurerm.keyVault - location = azurerm_resource_group.tfex.location - resource_group_name = azurerm_resource_group.tfex.name - tenant_id = data.azurerm_client_config.current.tenant_id - - soft_delete_enabled = true - purge_protection_enabled = true + name = "tfex-key-vault" + provider = azurerm.keyVault + location = azurerm_resource_group.tfex.location + resource_group_name = azurerm_resource_group.tfex.name + tenant_id = data.azurerm_client_config.current.tenant_id + + soft_delete_enabled = true + purge_protection_enabled = true sku_name = "standard" @@ -61,7 +61,7 @@ resource "azurerm_key_vault_access_policy" "storage" { tenant_id = data.azurerm_client_config.current.tenant_id object_id = azurerm_storage_account.tfex.identity.0.principal_id - key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] + key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] secret_permissions = ["get"] } @@ -70,16 +70,16 @@ resource "azurerm_key_vault_access_policy" "client" { tenant_id = data.azurerm_client_config.current.tenant_id object_id = azurerm_storage_account.tfex.identity.0.principal_id - key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] + key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] secret_permissions = ["get"] } resource "azurerm_storage_account" "tfex" { - name = "tfexstorageaccount" - resource_group_name = azurerm_resource_group.tfex.name - location = azurerm_resource_group.tfex.location - account_tier = "Standard" - account_replication_type = "GRS" + name = "tfexstorageaccount" + resource_group_name = azurerm_resource_group.tfex.name + location = azurerm_resource_group.tfex.location + account_tier = "Standard" + account_replication_type = "GRS" identity { type = "SystemAssigned" From 1736632e927d8c7fcd767c2cfd98c51e4a6926e8 Mon Sep 17 00:00:00 2001 From: Jeffrey Cline <20408400+WodansSon@users.noreply.github.com> Date: Fri, 21 Feb 2020 18:45:03 -0800 Subject: [PATCH 13/23] Updates to CMK --- azurerm/helpers/azure/key_vault.go | 64 +++++++++++++++ ...rm_storage_account_customer_managed_key.go | 10 +-- ...orage_account_customer_managed_key_test.go | 78 ++++++++++++++++--- ...account_customer_managed_key.html.markdown | 10 +-- 4 files changed, 140 insertions(+), 22 deletions(-) diff --git a/azurerm/helpers/azure/key_vault.go b/azurerm/helpers/azure/key_vault.go index 7957380bb594..36233887e95b 100644 --- a/azurerm/helpers/azure/key_vault.go +++ b/azurerm/helpers/azure/key_vault.go @@ -3,6 +3,7 @@ package azure import ( "context" "fmt" + "time" "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -118,3 +119,66 @@ func KeyVaultExists(ctx context.Context, client *keyvault.VaultsClient, keyVault return true, nil } + +func KeyVaultGetSoftDeletedState(ctx context.Context, client *keyvault.VaultsClient, name string, location string) (deleteDate interface{}, purgeDate interface{}, err error) { + softDel, err := client.GetDeleted(ctx, name, location) + if err != nil { + return nil, nil, fmt.Errorf("unable to get soft delete state information: %+v", err) + } + + // the logic is this way because the GetDeleted call will return an existing key vault + // that is not soft deleted, but the Deleted Vault properties will be nil + if props := softDel.Properties; props != nil { + var delDate interface{} + var purgeDate interface{} + + if dd := props.DeletionDate; dd != nil { + delDate = (*dd).Format(time.RFC3339) + } + if pg := props.ScheduledPurgeDate; pg != nil { + purgeDate = (*pg).Format(time.RFC3339) + } + if delDate != nil && purgeDate != nil { + return delDate, purgeDate, nil + } + } + + // this means we found an existing key vault that is not soft deleted + return nil, nil, nil +} + +func KeyVaultIsSoftDeleteAndPurgeProtected(ctx context.Context, client *keyvault.VaultsClient, keyVaultId string) bool { + name, resourceGroup, err := ParseNameAndResourceGroupFromResourceId(keyVaultId, "vaults") + if err != nil { + return false + } + + resp, err := client.Get(ctx, resourceGroup.(string), name.(string)) + + if props := resp.Properties; props != nil { + softDeleteEnabled := false + purgeProtectionEnabled := false + + if esd := props.EnableSoftDelete; esd != nil { + softDeleteEnabled = *esd + } + if epp := props.EnableSoftDelete; epp != nil { + purgeProtectionEnabled = *epp + } + + if softDeleteEnabled && purgeProtectionEnabled { + return true + } + } + + return false +} + +func ParseNameAndResourceGroupFromResourceId(resourceId string, namePathKey string) (resourceName interface{}, resourceGroup interface{}, err error) { + id, err := ParseAzureResourceID(resourceId) + if err != nil { + return nil, nil, err + } + + return id.Path[namePathKey], id.ResourceGroup, nil +} diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go index f56c98db9a7a..e363995ed042 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go @@ -74,12 +74,12 @@ func resourceArmStorageAccountCustomerManagedKeyCreateUpdate(d *schema.ResourceD storageAccountId := d.Get("storage_account_id").(string) encryptionServices := true - storageAccountName, storageAccountResourceGroupName, err := azure.KeyVaultGetResourceNameResource(storageAccountId, "storageAccounts") + storageAccountName, storageAccountResourceGroupName, err := azure.ParseNameAndResourceGroupFromResourceId(storageAccountId, "storageAccounts") if err != nil { return err } - keyVaultAccountName, keyVaultResourceGroupName, err := azure.KeyVaultGetResourceNameResource(keyVaultId, "vaults") + keyVaultAccountName, keyVaultResourceGroupName, err := azure.ParseNameAndResourceGroupFromResourceId(keyVaultId, "vaults") if err != nil { return err } @@ -137,7 +137,7 @@ func resourceArmStorageAccountCustomerManagedKeyRead(d *schema.ResourceData, met d.Set("storage_account_id", storageAccountId) d.Set("key_vault_id", keyVaultId) - name, resGroup, err := azure.KeyVaultGetResourceNameResource(storageAccountId, "storageAccounts") + name, resGroup, err := azure.ParseNameAndResourceGroupFromResourceId(storageAccountId, "storageAccounts") if err != nil { return err } @@ -172,7 +172,7 @@ func resourceArmStorageAccountCustomerManagedKeyDelete(d *schema.ResourceData, m storageAccountId := d.Get("storage_account_id").(string) encryptionServices := true - storageAccountName, resourceGroupName, err := azure.KeyVaultGetResourceNameResource(storageAccountId, "storageAccounts") + storageAccountName, resourceGroupName, err := azure.ParseNameAndResourceGroupFromResourceId(storageAccountId, "storageAccounts") if err != nil { return err } @@ -215,7 +215,7 @@ func resourceArmStorageAccountCustomerManagedKeyImportState(d *schema.ResourceDa d.Set("storage_account_id", id) d.Set("key_vault_id", "") - name, resourceGroup, err := azure.KeyVaultGetResourceNameResource(id, "storageAccounts") + name, resourceGroup, err := azure.ParseNameAndResourceGroupFromResourceId(id, "storageAccounts") if err != nil { return nil, err } diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go index 5a735cf7ba5c..711703307781 100644 --- a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go @@ -15,7 +15,7 @@ import ( ) func TestAccAzureRMStorageAccountCustomerManagedKey_basic(t *testing.T) { - parentResourceName := "azurerm_storage_account.testsa" + parentResourceName := "azurerm_storage_account.test" data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "custom") preConfig := testAccAzureRMStorageAccountCustomerManagedKey_basic(data) postConfig := testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data) @@ -46,7 +46,7 @@ func TestAccAzureRMStorageAccountCustomerManagedKey_basic(t *testing.T) { } func TestAccAzureRMStorageAccountCustomerManagedKey_disappears(t *testing.T) { - parentResourceName := "azurerm_storage_account.testsa" + parentResourceName := "azurerm_storage_account.test" data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "custom") preConfig := testAccAzureRMStorageAccountCustomerManagedKey_basic(data) postConfig := testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data) @@ -174,28 +174,86 @@ func testCheckAzureRMStorageAccountCustomerManagedKeyDestroyed(resourceName stri func testAccAzureRMStorageAccountCustomerManagedKey_basic(data acceptance.TestData) string { return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = false + } + } +} + resource "azurerm_resource_group" "testrg" { name = "acctestRG-%d" location = "%s" } -resource "azurerm_storage_account" "testsa" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.testrg.name}" +resource "azurerm_key_vault" "test" { + name = "vault%d" + location = azurerm_resource_group.testrg.location + resource_group_name = azurerm_resource_group.testrg.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + + soft_delete_enabled = true + purge_protection_enabled = true + + tags = { + environment = "testing" + } +} - location = "${azurerm_resource_group.testrg.location}" +resource "azurerm_key_vault_key" "test" { + name = "key%d" + key_vault_id = azurerm_key_vault.test.id + key_vault_access_policy_id = azurerm_key_vault_access_policy.storage.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] +} + +resource "azurerm_key_vault_access_policy" "storage" { + key_vault_id = azurerm_key_vault.test.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" { + key_vault_id = azurerm_key_vault.test.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.service_principal_application_id + + key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] +} + +resource "azurerm_storage_account" "test" { + name = "unlikely23exst2acct%s" + resource_group_name = azurerm_resource_group.testrg.name + location = azurerm_resource_group.testrg.location account_tier = "Standard" account_replication_type = "LRS" + identity { + type = "SystemAssigned" + } + tags = { - environment = "production" + environment = "testing" } } resource "azurerm_storage_account_customer_managed_key" "custom" { - storage_account_id = "${azurerm_storage_account.testsa.id}" + storage_account_id = azurerm_storage_account.test.id + key_vault_id = azurerm_key_vault.test.id + key_name = azurerm_key_vault_key.test.name + key_version = azurerm_key_vault_key.test.version } -`, data.RandomInteger, data.Locations.Primary, data.RandomStringOfLength(4)) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomStringOfLength(4)) } func testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data acceptance.TestData) string { @@ -209,7 +267,7 @@ resource "azurerm_storage_account" "testsa" { name = "unlikely23exst2acct%s" resource_group_name = "${azurerm_resource_group.testrg.name}" - location = "${azurerm_resource_group.testrg.location}" + location = azurerm_resource_group.testrg.location account_tier = "Standard" account_replication_type = "LRS" diff --git a/website/docs/r/storage_account_customer_managed_key.html.markdown b/website/docs/r/storage_account_customer_managed_key.html.markdown index 74058b8101a9..ce782079124d 100644 --- a/website/docs/r/storage_account_customer_managed_key.html.markdown +++ b/website/docs/r/storage_account_customer_managed_key.html.markdown @@ -16,8 +16,6 @@ Manages the customer managed key of an Azure Storage Account. data "azurerm_client_config" "current" {} provider "azurerm" { - alias = "keyVault" - features { key_vault { purge_soft_delete_on_destroy = false @@ -32,16 +30,14 @@ resource "azurerm_resource_group" "tfex" { resource "azurerm_key_vault" "tfex" { name = "tfex-key-vault" - provider = azurerm.keyVault location = azurerm_resource_group.tfex.location resource_group_name = azurerm_resource_group.tfex.name tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" soft_delete_enabled = true purge_protection_enabled = true - sku_name = "standard" - tags = { environment = "testing" } @@ -61,14 +57,14 @@ resource "azurerm_key_vault_access_policy" "storage" { tenant_id = data.azurerm_client_config.current.tenant_id object_id = azurerm_storage_account.tfex.identity.0.principal_id - key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + key_permissions = ["get", "create", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] secret_permissions = ["get"] } resource "azurerm_key_vault_access_policy" "client" { key_vault_id = azurerm_key_vault.tfex.id tenant_id = data.azurerm_client_config.current.tenant_id - object_id = azurerm_storage_account.tfex.identity.0.principal_id + object_id = data.azurerm_client_config.current.service_principal_application_id key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] secret_permissions = ["get"] From 3ce6b8c6685cd685704383faf1298ccbf8d37c37 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sat, 22 Feb 2020 08:41:37 +0100 Subject: [PATCH 14/23] r/key_vault_key: formatting --- .../internal/services/keyvault/resource_arm_key_vault_key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go b/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go index 79c6ca98b38b..88735f042eac 100644 --- a/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go +++ b/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go @@ -350,7 +350,7 @@ func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error d.Set("name", id.Name) d.Set("key_vault_access_policy_id", keyVaultAccessPolicyId) - if key := resp.Key; key != nil { + if key := resp.Key; key != nil { d.Set("key_type", string(key.Kty)) options := flattenKeyVaultKeyOptions(key.KeyOps) From fb8e38281a8073ac4ef5bfbbf7bd425ef256e647 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sun, 23 Feb 2020 19:37:57 +0100 Subject: [PATCH 15/23] r/storage_account: locking on the correct key during update --- azurerm/internal/services/iothub/resource_arm_iothub.go | 2 ++ .../services/storage/resource_arm_storage_account.go | 7 ++++--- .../storage/resource_arm_storage_account_network_rules.go | 2 -- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/azurerm/internal/services/iothub/resource_arm_iothub.go b/azurerm/internal/services/iothub/resource_arm_iothub.go index 270efa6c597c..f0587350b78b 100644 --- a/azurerm/internal/services/iothub/resource_arm_iothub.go +++ b/azurerm/internal/services/iothub/resource_arm_iothub.go @@ -26,6 +26,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) +// TODO: outside of this pr make this private + var IothubResourceName = "azurerm_iothub" func suppressIfTypeIsNot(t string) schema.SchemaDiffSuppressFunc { diff --git a/azurerm/internal/services/storage/resource_arm_storage_account.go b/azurerm/internal/services/storage/resource_arm_storage_account.go index 5d7991f2fc27..3a924d8395df 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account.go @@ -20,7 +20,6 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/iothub" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" @@ -31,6 +30,8 @@ import ( const blobStorageAccountDefaultAccessTier = "Hot" +var storageAccountResourceName = "azurerm_storage_account" + func resourceArmStorageAccount() *schema.Resource { return &schema.Resource{ Create: resourceArmStorageAccountCreate, @@ -749,8 +750,8 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e storageAccountName := id.Path["storageAccounts"] resourceGroupName := id.ResourceGroup - locks.ByName(storageAccountName, iothub.IothubResourceName) - defer locks.UnlockByName(storageAccountName, iothub.IothubResourceName) + locks.ByName(storageAccountName, storageAccountResourceName) + defer locks.UnlockByName(storageAccountName, storageAccountResourceName) accountTier := d.Get("account_tier").(string) replicationType := d.Get("account_replication_type").(string) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_network_rules.go b/azurerm/internal/services/storage/resource_arm_storage_account_network_rules.go index 7bbfda7558ce..2d8f676f3c88 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_network_rules.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_network_rules.go @@ -17,8 +17,6 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -var storageAccountResourceName = "azurerm_storage_account" - func resourceArmStorageAccountNetworkRules() *schema.Resource { return &schema.Resource{ Create: resourceArmStorageAccountNetworkRulesCreateUpdate, From 83102459fea5a9f12f32270d69b91d77f8972cee Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sun, 23 Feb 2020 22:09:51 +0100 Subject: [PATCH 16/23] r/storage_account_customer_managed_key: comments from code review ``` $ TF_ACC=1 go test -v ./azurerm/internal/services/storage/tests/ -timeout=60m -run=TestAccAzureRMStorageAccountCustomerManagedKey === RUN TestAccAzureRMStorageAccountCustomerManagedKey_basic === PAUSE TestAccAzureRMStorageAccountCustomerManagedKey_basic === RUN TestAccAzureRMStorageAccountCustomerManagedKey_requiresImport === PAUSE TestAccAzureRMStorageAccountCustomerManagedKey_requiresImport === RUN TestAccAzureRMStorageAccountCustomerManagedKey_updateKey === PAUSE TestAccAzureRMStorageAccountCustomerManagedKey_updateKey === CONT TestAccAzureRMStorageAccountCustomerManagedKey_basic === CONT TestAccAzureRMStorageAccountCustomerManagedKey_updateKey === CONT TestAccAzureRMStorageAccountCustomerManagedKey_requiresImport --- PASS: TestAccAzureRMStorageAccountCustomerManagedKey_requiresImport (381.82s) --- PASS: TestAccAzureRMStorageAccountCustomerManagedKey_updateKey (410.36s) --- PASS: TestAccAzureRMStorageAccountCustomerManagedKey_basic (507.32s) PASS ok github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/tests 507.376s ``` --- azurerm/helpers/azure/key_vault.go | 64 ----- .../services/keyvault/parse/key_vault_id.go | 31 +++ ...rm_storage_account_customer_managed_key.go | 235 ++++++++++-------- ...orage_account_customer_managed_key_test.go | 207 ++++++++------- .../resource_arm_storage_account_test.go | 1 + website/docs/d/key_vault_key.html.markdown | 2 +- ...account_customer_managed_key.html.markdown | 101 ++------ 7 files changed, 292 insertions(+), 349 deletions(-) create mode 100644 azurerm/internal/services/keyvault/parse/key_vault_id.go diff --git a/azurerm/helpers/azure/key_vault.go b/azurerm/helpers/azure/key_vault.go index 36233887e95b..7957380bb594 100644 --- a/azurerm/helpers/azure/key_vault.go +++ b/azurerm/helpers/azure/key_vault.go @@ -3,7 +3,6 @@ package azure import ( "context" "fmt" - "time" "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -119,66 +118,3 @@ func KeyVaultExists(ctx context.Context, client *keyvault.VaultsClient, keyVault return true, nil } - -func KeyVaultGetSoftDeletedState(ctx context.Context, client *keyvault.VaultsClient, name string, location string) (deleteDate interface{}, purgeDate interface{}, err error) { - softDel, err := client.GetDeleted(ctx, name, location) - if err != nil { - return nil, nil, fmt.Errorf("unable to get soft delete state information: %+v", err) - } - - // the logic is this way because the GetDeleted call will return an existing key vault - // that is not soft deleted, but the Deleted Vault properties will be nil - if props := softDel.Properties; props != nil { - var delDate interface{} - var purgeDate interface{} - - if dd := props.DeletionDate; dd != nil { - delDate = (*dd).Format(time.RFC3339) - } - if pg := props.ScheduledPurgeDate; pg != nil { - purgeDate = (*pg).Format(time.RFC3339) - } - if delDate != nil && purgeDate != nil { - return delDate, purgeDate, nil - } - } - - // this means we found an existing key vault that is not soft deleted - return nil, nil, nil -} - -func KeyVaultIsSoftDeleteAndPurgeProtected(ctx context.Context, client *keyvault.VaultsClient, keyVaultId string) bool { - name, resourceGroup, err := ParseNameAndResourceGroupFromResourceId(keyVaultId, "vaults") - if err != nil { - return false - } - - resp, err := client.Get(ctx, resourceGroup.(string), name.(string)) - - if props := resp.Properties; props != nil { - softDeleteEnabled := false - purgeProtectionEnabled := false - - if esd := props.EnableSoftDelete; esd != nil { - softDeleteEnabled = *esd - } - if epp := props.EnableSoftDelete; epp != nil { - purgeProtectionEnabled = *epp - } - - if softDeleteEnabled && purgeProtectionEnabled { - return true - } - } - - return false -} - -func ParseNameAndResourceGroupFromResourceId(resourceId string, namePathKey string) (resourceName interface{}, resourceGroup interface{}, err error) { - id, err := ParseAzureResourceID(resourceId) - if err != nil { - return nil, nil, err - } - - return id.Path[namePathKey], id.ResourceGroup, nil -} diff --git a/azurerm/internal/services/keyvault/parse/key_vault_id.go b/azurerm/internal/services/keyvault/parse/key_vault_id.go new file mode 100644 index 000000000000..65944f189241 --- /dev/null +++ b/azurerm/internal/services/keyvault/parse/key_vault_id.go @@ -0,0 +1,31 @@ +package parse + +import ( + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type KeyVaultId struct { + Name string + ResourceGroup string +} + +func KeyVaultID(input string) (*KeyVaultId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + account := KeyVaultId{ + ResourceGroup: id.ResourceGroup, + } + + if account.Name, err = id.PopSegment("vaults"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &account, nil +} diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go index e363995ed042..d0bf0cb67243 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go @@ -2,24 +2,30 @@ package storage import ( "fmt" + "log" "time" "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/parsers" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func resourceArmStorageAccountCustomerManagedKey() *schema.Resource { return &schema.Resource{ - Read: resourceArmStorageAccountCustomerManagedKeyRead, - Create: resourceArmStorageAccountCustomerManagedKeyCreateUpdate, - Update: resourceArmStorageAccountCustomerManagedKeyCreateUpdate, - Delete: resourceArmStorageAccountCustomerManagedKeyDelete, - SchemaVersion: 2, + Create: resourceArmStorageAccountCustomerManagedKeyCreateUpdate, + Read: resourceArmStorageAccountCustomerManagedKeyRead, + Update: resourceArmStorageAccountCustomerManagedKeyCreateUpdate, + Delete: resourceArmStorageAccountCustomerManagedKeyDelete, + + // TODO: this needs a custom ID validating importer Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -35,7 +41,8 @@ func resourceArmStorageAccountCustomerManagedKey() *schema.Resource { "storage_account_id": { Type: schema.TypeString, Required: true, - ValidateFunc: azure.ValidateResourceID, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, // TODO: validation }, "key_vault_id": { @@ -49,118 +56,176 @@ func resourceArmStorageAccountCustomerManagedKey() *schema.Resource { Required: true, ValidateFunc: validate.NoEmptyStrings, }, + "key_version": { Type: schema.TypeString, Required: true, ValidateFunc: validate.NoEmptyStrings, }, - "key_vault_uri": { - Type: schema.TypeString, - Computed: true, - }, }, } } func resourceArmStorageAccountCustomerManagedKeyCreateUpdate(d *schema.ResourceData, meta interface{}) error { - vaultClient := meta.(*clients.Client).KeyVault.VaultsClient storageClient := meta.(*clients.Client).Storage.AccountsClient - ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + vaultsClient := meta.(*clients.Client).KeyVault.VaultsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - keyVaultId := d.Get("key_vault_id").(string) - keyName := d.Get("key_name").(string) - keyVersion := d.Get("key_version").(string) - storageAccountId := d.Get("storage_account_id").(string) - encryptionServices := true - - storageAccountName, storageAccountResourceGroupName, err := azure.ParseNameAndResourceGroupFromResourceId(storageAccountId, "storageAccounts") + storageAccountIdRaw := d.Get("storage_account_id").(string) + storageAccountId, err := parsers.ParseAccountID(storageAccountIdRaw) if err != nil { return err } - keyVaultAccountName, keyVaultResourceGroupName, err := azure.ParseNameAndResourceGroupFromResourceId(keyVaultId, "vaults") + locks.ByName(storageAccountId.Name, storageAccountResourceName) + defer locks.UnlockByName(storageAccountId.Name, storageAccountResourceName) + + 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) + } + if storageAccount.AccountProperties == nil { + return fmt.Errorf("Error retrieving Storage Account %q (Resource Group %q): `properties` was nil", storageAccountId.Name, storageAccountId.ResourceGroup) + } + + // since we're mutating the storage account here, we can use that as the ID + resourceId := storageAccountIdRaw + + if d.IsNewResource() { + // whilst this looks superflurious 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.MicrosoftKeyvault { + return tf.ImportAsExistsError("azurerm_storage_account_customer_managed_key", resourceId) + } + } + } + + keyVaultIdRaw := d.Get("key_vault_id").(string) + keyVaultId, err := parse.KeyVaultID(keyVaultIdRaw) if err != nil { return err } - // First check to see if the key vault is configured correctly or not - if !azure.KeyVaultIsSoftDeleteAndPurgeProtected(ctx, vaultClient, keyVaultId) { - return fmt.Errorf("Key Vault %q (Resource Group %q) is not configured correctly, please make sure that both 'soft_delete_enabled' and 'purge_protection_enabled' arguments are set to 'true'", keyVaultAccountName, keyVaultResourceGroupName) + 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) } - pKeyVaultBaseURL, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultClient, keyVaultId) + softDeleteEnabled := false + purgeProtectionEnabled := false + if props := keyVault.Properties; props != nil { + if esd := props.EnableSoftDelete; esd != nil { + softDeleteEnabled = *esd + } + if epp := props.EnablePurgeProtection; epp != nil { + purgeProtectionEnabled = *epp + } + } + if !softDeleteEnabled || !purgeProtectionEnabled { + return fmt.Errorf("Key Vault %q (Resource Group %q) must be configured for both Purge Protection and Soft Delete", keyVaultId.Name, keyVaultId.ResourceGroup) + } + + keyVaultBaseURL, err := azure.GetKeyVaultBaseUrlFromID(ctx, vaultsClient, keyVaultIdRaw) if err != nil { - return fmt.Errorf("Error looking up Key Vault URI from Key Vault %q (Resource Group %q): %+v", keyVaultAccountName, keyVaultResourceGroupName, err) + return fmt.Errorf("Error looking up Key Vault URI from Key Vault %q (Resource Group %q): %+v", keyVaultId.Name, keyVaultId.ResourceGroup, err) } + keyName := d.Get("key_name").(string) + keyVersion := d.Get("key_version").(string) props := storage.AccountUpdateParameters{ AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ Encryption: &storage.Encryption{ Services: &storage.EncryptionServices{ Blob: &storage.EncryptionService{ - Enabled: utils.Bool(encryptionServices), + Enabled: utils.Bool(true), }, File: &storage.EncryptionService{ - Enabled: utils.Bool(encryptionServices), + Enabled: utils.Bool(true), }, }, KeySource: storage.MicrosoftKeyvault, KeyVaultProperties: &storage.KeyVaultProperties{ KeyName: utils.String(keyName), KeyVersion: utils.String(keyVersion), - KeyVaultURI: utils.String(pKeyVaultBaseURL), + KeyVaultURI: utils.String(keyVaultBaseURL), }, }, }, } - _, err = storageClient.Update(ctx, storageAccountResourceGroupName.(string), storageAccountName.(string), props) - if err != nil { - return fmt.Errorf("Error updating Azure Storage Account %q (Resource Group %q) Customer Managed Key: %+v", storageAccountName, storageAccountResourceGroupName, err) + if _, err = storageClient.Update(ctx, storageAccountId.ResourceGroup, storageAccountId.Name, props); err != nil { + return fmt.Errorf("Error updating Customer Managed Key for Storage Account %q (Resource Group %q): %+v", storageAccountId.Name, storageAccountId.ResourceGroup, err) } - resourceId := fmt.Sprintf("%s/customerManagedKey", storageAccountId) d.SetId(resourceId) - return resourceArmStorageAccountCustomerManagedKeyRead(d, meta) } func resourceArmStorageAccountCustomerManagedKeyRead(d *schema.ResourceData, meta interface{}) error { storageClient := meta.(*clients.Client).Storage.AccountsClient + vaultsClient := meta.(*clients.Client).KeyVault.VaultsClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - storageAccountId := d.Get("storage_account_id").(string) - keyVaultId := d.Get("key_vault_id").(string) - - d.Set("storage_account_id", storageAccountId) - d.Set("key_vault_id", keyVaultId) - - name, resGroup, err := azure.ParseNameAndResourceGroupFromResourceId(storageAccountId, "storageAccounts") + storageAccountId, err := parsers.ParseAccountID(d.Id()) if err != nil { return err } - resp, err := storageClient.GetProperties(ctx, resGroup.(string), name.(string), "") + storageAccount, err := storageClient.GetProperties(ctx, storageAccountId.ResourceGroup, storageAccountId.Name, "") if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if utils.ResponseWasNotFound(storageAccount.Response) { + log.Printf("[DEBUG] Storage Account %q could not be found in Resource Group %q - removing from state!", storageAccountId.Name, storageAccountId.ResourceGroup) d.SetId("") return nil } - return fmt.Errorf("Error reading the state of AzureRM Storage Account %q (Resource Group %q): %+v", name, resGroup, err) + + return fmt.Errorf("Error 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) + } + if storageAccount.AccountProperties.Encryption == nil || storageAccount.AccountProperties.Encryption.KeySource != storage.MicrosoftKeyvault { + log.Printf("[DEBUG] Customer Managed Key was not defined for Storage Account %q (Resource Group %q) - removing from state!", storageAccountId.Name, storageAccountId.ResourceGroup) + d.SetId("") + return nil } - if props := resp.AccountProperties; props != nil { - if encryption := props.Encryption; encryption != nil { - if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { - d.Set("key_name", *keyVaultProperties.KeyName) - d.Set("key_version", *keyVaultProperties.KeyVersion) - d.Set("key_vault_uri", *keyVaultProperties.KeyVaultURI) - } + encryption := *storageAccount.AccountProperties.Encryption + + keyName := "" + keyVaultUri := "" + keyVersion := "" + if props := encryption.KeyVaultProperties; props != nil { + if props.KeyName != nil { + keyName = *props.KeyName } + if props.KeyVaultURI != nil { + keyVaultUri = *props.KeyVaultURI + } + if props.KeyVersion != nil { + keyVersion = *props.KeyVersion + } + } + + if keyVaultUri == "" { + return fmt.Errorf("Error retrieving Storage Account %q (Resource Group %q): `properties.encryption.keyVaultProperties.keyVaultUri` was nil") } + keyVaultId, err := azure.GetKeyVaultIDFromBaseUrl(ctx, vaultsClient, keyVaultUri) + if err != nil { + return fmt.Errorf("Error retrieving Key Vault ID from the Base URI %q: %+v", keyVaultUri, err) + } + + // now we have the key vault uri we can look up the ID + + d.Set("storage_account_id", d.Id()) + d.Set("key_vault_id", keyVaultId) + d.Set("key_name", keyName) + d.Set("key_version", keyVersion) + return nil } @@ -169,14 +234,24 @@ func resourceArmStorageAccountCustomerManagedKeyDelete(d *schema.ResourceData, m ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - storageAccountId := d.Get("storage_account_id").(string) - encryptionServices := true - - storageAccountName, resourceGroupName, err := azure.ParseNameAndResourceGroupFromResourceId(storageAccountId, "storageAccounts") + storageAccountId, err := parsers.ParseAccountID(d.Id()) if err != nil { return err } + locks.ByName(storageAccountId.Name, storageAccountResourceName) + defer locks.UnlockByName(storageAccountId.Name, storageAccountResourceName) + + // confirm it still exists prior to trying to update it, else we'll get an error + storageAccount, err := storageClient.GetProperties(ctx, storageAccountId.ResourceGroup, storageAccountId.Name, "") + if err != nil { + if utils.ResponseWasNotFound(storageAccount.Response) { + return nil + } + + return fmt.Errorf("Error retrieving Storage Account %q (Resource Group %q): %+v", storageAccountId.Name, storageAccountId.ResourceGroup, err) + } + // Since this isn't a real object, just modifying an existing object // "Delete" doesn't really make sense it should really be a "Revert to Default" // So instead of the Delete func actually deleting the Storage Account I am @@ -186,10 +261,10 @@ func resourceArmStorageAccountCustomerManagedKeyDelete(d *schema.ResourceData, m Encryption: &storage.Encryption{ Services: &storage.EncryptionServices{ Blob: &storage.EncryptionService{ - Enabled: utils.Bool(encryptionServices), + Enabled: utils.Bool(true), }, File: &storage.EncryptionService{ - Enabled: utils.Bool(encryptionServices), + Enabled: utils.Bool(true), }, }, KeySource: storage.MicrosoftStorage, @@ -197,53 +272,9 @@ func resourceArmStorageAccountCustomerManagedKeyDelete(d *schema.ResourceData, m }, } - _, err = storageClient.Update(ctx, resourceGroupName.(string), storageAccountName.(string), props) - if err != nil { - return fmt.Errorf("Error deleting AzureRM Storage Account %q (Resource Group %q) %q: %+v", storageAccountName.(string), resourceGroupName.(string), err) + if _, err = storageClient.Update(ctx, storageAccountId.ResourceGroup, storageAccountId.Name, props); err != nil { + return fmt.Errorf("Error removing Customer Managed Key for Storage Account %q (Resource Group %q): %+v", storageAccountId.Name, storageAccountId.ResourceGroup, err) } return nil } - -func resourceArmStorageAccountCustomerManagedKeyImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - storageClient := meta.(*clients.Client).Storage.AccountsClient - ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) - defer cancel() - - id := d.Id() - - d.Set("storage_account_id", id) - d.Set("key_vault_id", "") - - name, resourceGroup, err := azure.ParseNameAndResourceGroupFromResourceId(id, "storageAccounts") - if err != nil { - return nil, err - } - - resp, err := storageClient.GetProperties(ctx, resourceGroup.(string), name.(string), "") - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - d.SetId("") - return nil, nil - } - return nil, fmt.Errorf("Error importing the state of AzureRM Storage Account %q (Resource Group %q): %+v", name.(string), resourceGroup.(string), err) - } - - if props := resp.AccountProperties; props != nil { - if encryption := props.Encryption; encryption != nil { - if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil { - d.Set("key_name", keyVaultProperties.KeyName) - d.Set("key_version", keyVaultProperties.KeyVersion) - d.Set("key_vault_uri", keyVaultProperties.KeyVaultURI) - } - } - } - - resourceId := fmt.Sprintf("%s/customerManagedKey", id) - d.SetId(resourceId) - - results := make([]*schema.ResourceData, 1) - - results[0] = d - return results, nil -} diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go index 711703307781..5b94b55c585a 100644 --- a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go @@ -15,60 +15,74 @@ import ( ) func TestAccAzureRMStorageAccountCustomerManagedKey_basic(t *testing.T) { - parentResourceName := "azurerm_storage_account.test" - data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "custom") - preConfig := testAccAzureRMStorageAccountCustomerManagedKey_basic(data) - postConfig := testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data) + data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroyed, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, Steps: []resource.TestStep{ { - Config: preConfig, + Config: testAccAzureRMStorageAccountCustomerManagedKey_basic(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountCustomerManagedKeyExists(data.ResourceName), ), }, + data.ImportStep(), { // Delete the encryption settings resource and verify it is gone - // Whilst making sure the encryption settings on the storage account - // have been reverted to their default state - Config: postConfig, + Config: testAccAzureRMStorageAccountCustomerManagedKey_template(data), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExistsWithDefaultSettings(parentResourceName), - testCheckAzureRMStorageAccountCustomerManagedKeyDestroyed(data.ResourceName), + // Then ensure the encryption settings on the storage account + // have been reverted to their default state + testCheckAzureRMStorageAccountExistsWithDefaultSettings("azurerm_storage_account.test"), ), }, }, }) } -func TestAccAzureRMStorageAccountCustomerManagedKey_disappears(t *testing.T) { - parentResourceName := "azurerm_storage_account.test" - data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "custom") - preConfig := testAccAzureRMStorageAccountCustomerManagedKey_basic(data) - postConfig := testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data) +func TestAccAzureRMStorageAccountCustomerManagedKey_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroyed, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, Steps: []resource.TestStep{ { - Config: preConfig, + Config: testAccAzureRMStorageAccountCustomerManagedKey_basic(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountCustomerManagedKeyExists(data.ResourceName), ), }, + data.RequiresImportErrorStep(testAccAzureRMStorageAccountCustomerManagedKey_requiresImport), + }, + }) +} + +func TestAccAzureRMStorageAccountCustomerManagedKey_updateKey(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_account_customer_managed_key", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageAccountCustomerManagedKey_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountCustomerManagedKeyExists(data.ResourceName), + ), + }, + data.ImportStep(), { - Config: postConfig, + Config: testAccAzureRMStorageAccountCustomerManagedKey_updated(data), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExistsWithDefaultSettings(parentResourceName), - testCheckAzureRMStorageAccountCustomerManagedKeyDestroyed(data.ResourceName), + testCheckAzureRMStorageAccountCustomerManagedKeyExists(data.ResourceName), ), }, + data.ImportStep(), }, }) } @@ -136,43 +150,58 @@ func testCheckAzureRMStorageAccountCustomerManagedKeyExists(resourceName string) } } -func testCheckAzureRMStorageAccountDestroyed(s *terraform.State) error { - ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext - conn := acceptance.AzureProvider.Meta().(*clients.Client).Storage.AccountsClient +func testAccAzureRMStorageAccountCustomerManagedKey_basic(data acceptance.TestData) string { + template := testAccAzureRMStorageAccountCustomerManagedKey_template(data) + return fmt.Sprintf(` +%s - for _, rs := range s.RootModule().Resources { - if rs.Type != "azurerm_storage_account" { - continue - } +resource "azurerm_storage_account_customer_managed_key" "test" { + storage_account_id = azurerm_storage_account.test.id + key_vault_id = azurerm_key_vault.test.id + key_name = azurerm_key_vault_key.first.name + key_version = azurerm_key_vault_key.first.version +} +`, template) +} - name := rs.Primary.Attributes["name"] - resourceGroup := rs.Primary.Attributes["resource_group_name"] +func testAccAzureRMStorageAccountCustomerManagedKey_requiresImport(data acceptance.TestData) string { + template := testAccAzureRMStorageAccountCustomerManagedKey_basic(data) + return fmt.Sprintf(` +%s - resp, err := conn.GetProperties(ctx, resourceGroup, name, "") - if err != nil { - return nil - } +resource "azurerm_storage_account_customer_managed_key" "import" { + storage_account_id = azurerm_storage_account_customer_managed_key.test.storage_account_id + key_vault_id = azurerm_storage_account_customer_managed_key.test.key_vault_id + key_name = azurerm_storage_account_customer_managed_key.test.key_name + key_version = azurerm_storage_account_customer_managed_key.test.key_version +} +`, template) +} - if resp.StatusCode != http.StatusNotFound { - return fmt.Errorf("Storage Account still exists:\n%#v", resp.AccountProperties) - } - } +func testAccAzureRMStorageAccountCustomerManagedKey_updated(data acceptance.TestData) string { + template := testAccAzureRMStorageAccountCustomerManagedKey_template(data) + return fmt.Sprintf(` +%s - return nil +resource "azurerm_key_vault_key" "second" { + name = "second" + key_vault_id = azurerm_key_vault.test.id + key_vault_access_policy_id = azurerm_key_vault_access_policy.storage.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] } -func testCheckAzureRMStorageAccountCustomerManagedKeyDestroyed(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[resourceName] - if !ok { - return nil - } - - return fmt.Errorf("Found: %s", resourceName) - } +resource "azurerm_storage_account_customer_managed_key" "test" { + storage_account_id = azurerm_storage_account.test.id + key_vault_id = azurerm_key_vault.test.id + key_name = azurerm_key_vault_key.second.name + key_version = azurerm_key_vault_key.second.version +} +`, template) } -func testAccAzureRMStorageAccountCustomerManagedKey_basic(data acceptance.TestData) string { +func testAccAzureRMStorageAccountCustomerManagedKey_template(data acceptance.TestData) string { return fmt.Sprintf(` data "azurerm_client_config" "current" {} @@ -184,33 +213,19 @@ provider "azurerm" { } } -resource "azurerm_resource_group" "testrg" { +resource "azurerm_resource_group" "test" { name = "acctestRG-%d" location = "%s" } resource "azurerm_key_vault" "test" { - name = "vault%d" - location = azurerm_resource_group.testrg.location - resource_group_name = azurerm_resource_group.testrg.name - tenant_id = data.azurerm_client_config.current.tenant_id - sku_name = "standard" - + name = "acctestkv%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" soft_delete_enabled = true purge_protection_enabled = true - - tags = { - environment = "testing" - } -} - -resource "azurerm_key_vault_key" "test" { - name = "key%d" - key_vault_id = azurerm_key_vault.test.id - key_vault_access_policy_id = azurerm_key_vault_access_policy.storage.id - key_type = "RSA" - key_size = 2048 - key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] } resource "azurerm_key_vault_access_policy" "storage" { @@ -225,55 +240,33 @@ resource "azurerm_key_vault_access_policy" "storage" { resource "azurerm_key_vault_access_policy" "client" { key_vault_id = azurerm_key_vault.test.id tenant_id = data.azurerm_client_config.current.tenant_id - object_id = data.azurerm_client_config.current.service_principal_application_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_storage_account" "test" { - name = "unlikely23exst2acct%s" - resource_group_name = azurerm_resource_group.testrg.name - location = azurerm_resource_group.testrg.location - account_tier = "Standard" - account_replication_type = "LRS" - - identity { - type = "SystemAssigned" - } - - tags = { - environment = "testing" - } -} - -resource "azurerm_storage_account_customer_managed_key" "custom" { - storage_account_id = azurerm_storage_account.test.id - key_vault_id = azurerm_key_vault.test.id - key_name = azurerm_key_vault_key.test.name - key_version = azurerm_key_vault_key.test.version -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomStringOfLength(4)) -} +resource "azurerm_key_vault_key" "first" { + name = "first" + key_vault_id = azurerm_key_vault.test.id + key_vault_access_policy_id = azurerm_key_vault_access_policy.storage.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] -func testAccAzureRMStorageAccountCustomerManagedKey_basicDelete(data acceptance.TestData) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "testrg" { - name = "acctestRG-%d" - location = "%s" + depends_on = [azurerm_key_vault_access_policy.client] } -resource "azurerm_storage_account" "testsa" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.testrg.name}" - - location = azurerm_resource_group.testrg.location +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location account_tier = "Standard" account_replication_type = "LRS" - tags = { - environment = "production" + identity { + type = "SystemAssigned" } } -`, data.RandomInteger, data.Locations.Primary, data.RandomStringOfLength(4)) +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString) } diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_test.go index 12f16a5ed1f2..23811ddb0629 100644 --- a/azurerm/internal/services/storage/tests/resource_arm_storage_account_test.go +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_test.go @@ -325,6 +325,7 @@ func TestAccAzureRMStorageAccount_fileStorageWithUpdate(t *testing.T) { }, }) } + func TestAccAzureRMStorageAccount_storageV2WithUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") diff --git a/website/docs/d/key_vault_key.html.markdown b/website/docs/d/key_vault_key.html.markdown index 8e9128f2ba81..d02dcc30b050 100644 --- a/website/docs/d/key_vault_key.html.markdown +++ b/website/docs/d/key_vault_key.html.markdown @@ -23,7 +23,7 @@ data "azurerm_key_vault_key" "example" { } output "key_type" { - value = data.azurerm_key_vault_secret.example.key_type + value = data.azurerm_key_vault_key.example.key_type } ``` diff --git a/website/docs/r/storage_account_customer_managed_key.html.markdown b/website/docs/r/storage_account_customer_managed_key.html.markdown index ce782079124d..9e5cb40195ac 100644 --- a/website/docs/r/storage_account_customer_managed_key.html.markdown +++ b/website/docs/r/storage_account_customer_managed_key.html.markdown @@ -3,94 +3,43 @@ subcategory: "Storage" layout: "azurerm" page_title: "Azure Resource Manager: azurerm_storage_account_customer_managed_key" description: |- - Manages the customer managed key of an Azure Storage Account. + Manages a Customer Managed Key for a Storage Account. --- # azurerm_storage_account_customer_managed_key -Manages the customer managed key of an Azure Storage Account. +Manages a Customer Managed Key for a Storage Account. ## Example Usage ```hcl -data "azurerm_client_config" "current" {} - -provider "azurerm" { - features { - key_vault { - purge_soft_delete_on_destroy = false - } - } -} - -resource "azurerm_resource_group" "tfex" { - name = "tfex-RG" - location = "westeurope" -} - -resource "azurerm_key_vault" "tfex" { - name = "tfex-key-vault" - location = azurerm_resource_group.tfex.location - resource_group_name = azurerm_resource_group.tfex.name - tenant_id = data.azurerm_client_config.current.tenant_id - sku_name = "standard" - - soft_delete_enabled = true - purge_protection_enabled = true - - tags = { - environment = "testing" - } -} - -resource "azurerm_key_vault_key" "tfex" { - name = "tfex-key" - key_vault_id = azurerm_key_vault.tfex.id - key_vault_access_policy_id = azurerm_key_vault_access_policy.storage.id - key_type = "RSA" - key_size = 2048 - key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] +data "azurerm_key_vault" "example" { + name = "mykeyvault" + resource_group_name = "some-resource-group" } -resource "azurerm_key_vault_access_policy" "storage" { - key_vault_id = azurerm_key_vault.tfex.id - tenant_id = data.azurerm_client_config.current.tenant_id - object_id = azurerm_storage_account.tfex.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" { - key_vault_id = azurerm_key_vault.tfex.id - tenant_id = data.azurerm_client_config.current.tenant_id - object_id = data.azurerm_client_config.current.service_principal_application_id - - key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] - secret_permissions = ["get"] +data "azurerm_key_vault_key" "example" { + name = "secret-sauce" + key_vault_id = data.azurerm_key_vault.example.id } resource "azurerm_storage_account" "tfex" { - name = "tfexstorageaccount" - resource_group_name = azurerm_resource_group.tfex.name - location = azurerm_resource_group.tfex.location + name = "exampleaccount" + resource_group_name = data.azurerm_key_vault.example.name + location = data.azurerm_key_vault.example.location account_tier = "Standard" account_replication_type = "GRS" identity { type = "SystemAssigned" } - - tags = { - environment = "testing" - } } -resource "azurerm_storage_account_customer_managed_key" "tfex" { - storage_account_id = azurerm_storage_account.tfex.id - key_vault_id = azurerm_key_vault.tfex.id - key_name = azurerm_key_vault_key.tfex.name - key_version = azurerm_key_vault_key.tfex.version +resource "azurerm_storage_account_customer_managed_key" "example" { + storage_account_id = azurerm_storage_account.example.id + key_vault_id = data.azurerm_key_vault.example.id + key_name = data.azurerm_key_vault_key.example.name + key_version = data.azurerm_key_vault_key.example.version } ``` @@ -98,24 +47,26 @@ resource "azurerm_storage_account_customer_managed_key" "tfex" { The following arguments are supported: -* `storage_account_id` - (Required) The id of the storage account to manage the encryption settings for. -* `key_vault_id` - (Required) The ID of the Key Vault. -* `key_name` - (Required) The name of Key Vault key. -* `key_version` - (Required) The version of Key Vault key. +* `storage_account_id` - (Required) The ID of the Storage Account. Changing this forces a new resource to be created. + +* `key_vault_id` - (Required) The ID of the Key Vault. Changing this forces a new resource to be created. + +* `key_name` - (Required) The name of Key Vault Key. + +* `key_version` - (Required) The version of Key Vault Key. ## Attributes Reference The following attributes are exported in addition to the arguments listed above: -* `id` - The storage account encryption settings Resource ID. -* `key_vault_uri` - The base URI of the Key Vault. +* `id` - The ID of the Storage Account. --- ## Import -Storage Accounts Encryption Settings can be imported using the `resource id`, e.g. +Customer Managed Keys for a Storage Account can be imported using the `resource id` of the Storage Account, e.g. ```shell -terraform import azurerm_storage_account_customer_managed_key.tfex /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Storage/storageAccounts/myaccount +terraform import azurerm_storage_account_customer_managed_key.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Storage/storageAccounts/myaccount ``` \ No newline at end of file From 675364aa11f2b982127e54448f83facc34347e44 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sun, 23 Feb 2020 22:16:55 +0100 Subject: [PATCH 17/23] r/storage_account_customer_managed_key: adding validation --- .../keyvault/parse/key_vault_id_test.go | 73 +++++++++++++++++++ .../keyvault/validate/key_vault_id.go | 22 ++++++ ...rm_storage_account_customer_managed_key.go | 18 +++-- .../storage/validate/storage_account.go | 22 ++++++ 4 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 azurerm/internal/services/keyvault/parse/key_vault_id_test.go create mode 100644 azurerm/internal/services/keyvault/validate/key_vault_id.go create mode 100644 azurerm/internal/services/storage/validate/storage_account.go diff --git a/azurerm/internal/services/keyvault/parse/key_vault_id_test.go b/azurerm/internal/services/keyvault/parse/key_vault_id_test.go new file mode 100644 index 000000000000..9c73991d5cf2 --- /dev/null +++ b/azurerm/internal/services/keyvault/parse/key_vault_id_test.go @@ -0,0 +1,73 @@ +package parse + +import ( + "testing" +) + +func TestKeyVaultID(t *testing.T) { + testData := []struct { + Name string + Input string + Expected *KeyVaultId + }{ + { + Name: "Empty", + Input: "", + Expected: nil, + }, + { + Name: "No Resource Groups Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Expected: nil, + }, + { + Name: "No Resource Groups Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Expected: nil, + }, + { + Name: "Resource Group ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/", + Expected: nil, + }, + { + Name: "Missing Vaults Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.KeyVault/vaults/", + Expected: nil, + }, + { + Name: "Key Vault ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.KeyVault/vaults/vault1", + Expected: &KeyVaultId{ + Name: "vault1", + ResourceGroup: "resGroup1", + }, + }, + { + Name: "Wrong Casing", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.KeyVault/Vaults/vault1", + Expected: nil, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := KeyVaultID(v.Input) + if err != nil { + if v.Expected == nil { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for Resource Group", v.Expected.ResourceGroup, actual.ResourceGroup) + } + } +} diff --git a/azurerm/internal/services/keyvault/validate/key_vault_id.go b/azurerm/internal/services/keyvault/validate/key_vault_id.go new file mode 100644 index 000000000000..5b365e71ce47 --- /dev/null +++ b/azurerm/internal/services/keyvault/validate/key_vault_id.go @@ -0,0 +1,22 @@ +package validate + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault/parse" +) + +func KeyVaultID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := parse.KeyVaultID(v); err != nil { + errors = append(errors, fmt.Errorf("Can not parse %q as a resource id: %v", k, err)) + return + } + + return warnings, errors +} diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go index d0bf0cb67243..d469bf8bee24 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go @@ -12,8 +12,10 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault/parse" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/parsers" + keyVaultParse "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault/parse" + keyVaultValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault/validate" + storageParse "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/parsers" + storageValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -42,13 +44,13 @@ func resourceArmStorageAccountCustomerManagedKey() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: azure.ValidateResourceID, // TODO: validation + ValidateFunc: storageValidate.StorageAccountID, }, "key_vault_id": { Type: schema.TypeString, Required: true, - ValidateFunc: azure.ValidateResourceID, + ValidateFunc: keyVaultValidate.KeyVaultID, }, "key_name": { @@ -73,7 +75,7 @@ func resourceArmStorageAccountCustomerManagedKeyCreateUpdate(d *schema.ResourceD defer cancel() storageAccountIdRaw := d.Get("storage_account_id").(string) - storageAccountId, err := parsers.ParseAccountID(storageAccountIdRaw) + storageAccountId, err := storageParse.ParseAccountID(storageAccountIdRaw) if err != nil { return err } @@ -103,7 +105,7 @@ func resourceArmStorageAccountCustomerManagedKeyCreateUpdate(d *schema.ResourceD } keyVaultIdRaw := d.Get("key_vault_id").(string) - keyVaultId, err := parse.KeyVaultID(keyVaultIdRaw) + keyVaultId, err := keyVaultParse.KeyVaultID(keyVaultIdRaw) if err != nil { return err } @@ -169,7 +171,7 @@ func resourceArmStorageAccountCustomerManagedKeyRead(d *schema.ResourceData, met ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - storageAccountId, err := parsers.ParseAccountID(d.Id()) + storageAccountId, err := storageParse.ParseAccountID(d.Id()) if err != nil { return err } @@ -234,7 +236,7 @@ func resourceArmStorageAccountCustomerManagedKeyDelete(d *schema.ResourceData, m ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - storageAccountId, err := parsers.ParseAccountID(d.Id()) + storageAccountId, err := storageParse.ParseAccountID(d.Id()) if err != nil { return err } diff --git a/azurerm/internal/services/storage/validate/storage_account.go b/azurerm/internal/services/storage/validate/storage_account.go new file mode 100644 index 000000000000..3fe3b6d2710b --- /dev/null +++ b/azurerm/internal/services/storage/validate/storage_account.go @@ -0,0 +1,22 @@ +package validate + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/parsers" +) + +func StorageAccountID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := parsers.ParseAccountID(v); err != nil { + errors = append(errors, fmt.Errorf("Can not parse %q as a resource id: %v", k, err)) + return + } + + return warnings, errors +} From c80abda2499e6ba4e5841845e7ea67ca70e2e11a Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sun, 23 Feb 2020 22:34:55 +0100 Subject: [PATCH 18/23] r/key_vault_key: removing the unused `key_vault_access_policy_id` field --- .../services/keyvault/resource_arm_key_vault_key.go | 10 ---------- ..._arm_storage_account_customer_managed_key_test.go | 12 +++++++++--- website/docs/r/key_vault_key.html.markdown | 2 -- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go b/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go index 88735f042eac..74f87464b3a8 100644 --- a/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go +++ b/azurerm/internal/services/keyvault/resource_arm_key_vault_key.go @@ -52,13 +52,6 @@ func resourceArmKeyVaultKey() *schema.Resource { ValidateFunc: azure.ValidateResourceID, }, - "key_vault_access_policy_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: azure.ValidateResourceID, - }, - "key_type": { Type: schema.TypeString, Required: true, @@ -309,8 +302,6 @@ func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - keyVaultAccessPolicyId := d.Get("key_vault_access_policy_id").(string) - id, err := azure.ParseKeyVaultChildID(d.Id()) if err != nil { return err @@ -348,7 +339,6 @@ func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error } d.Set("name", id.Name) - d.Set("key_vault_access_policy_id", keyVaultAccessPolicyId) if key := resp.Key; key != nil { d.Set("key_type", string(key.Kty)) diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go index 5b94b55c585a..91c4dcdea3af 100644 --- a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go @@ -186,10 +186,14 @@ func testAccAzureRMStorageAccountCustomerManagedKey_updated(data acceptance.Test resource "azurerm_key_vault_key" "second" { name = "second" key_vault_id = azurerm_key_vault.test.id - key_vault_access_policy_id = azurerm_key_vault_access_policy.storage.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_storage_account_customer_managed_key" "test" { @@ -249,12 +253,14 @@ resource "azurerm_key_vault_access_policy" "client" { resource "azurerm_key_vault_key" "first" { name = "first" key_vault_id = azurerm_key_vault.test.id - key_vault_access_policy_id = azurerm_key_vault_access_policy.storage.id key_type = "RSA" key_size = 2048 key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] - depends_on = [azurerm_key_vault_access_policy.client] + depends_on = [ + azurerm_key_vault_access_policy.client, + azurerm_key_vault_access_policy.storage, + ] } resource "azurerm_storage_account" "test" { diff --git a/website/docs/r/key_vault_key.html.markdown b/website/docs/r/key_vault_key.html.markdown index f850cb88abf8..9a6a8df1ec68 100644 --- a/website/docs/r/key_vault_key.html.markdown +++ b/website/docs/r/key_vault_key.html.markdown @@ -82,8 +82,6 @@ The following arguments are supported: * `key_vault_id` - (Required) The ID of the Key Vault where the Key should be created. Changing this forces a new resource to be created. -* `key_vault_access_policy_id` - (Optional) The ID of the Key Vault Access Policy which should exist before the Key should be created. Changing this forces a new resource to be created. - * `key_type` - (Required) Specifies the Key Type to use for this Key Vault Key. Possible values are `EC` (Elliptic Curve), `EC-HSM`, `Oct` (Octet), `RSA` and `RSA-HSM`. Changing this forces a new resource to be created. * `key_size` - (Optional) Specifies the Size of the RSA key to create in bytes. For example, 1024 or 2048. *Note*: This field is required if `key_type` is `RSA` or `RSA-HSM`. Changing this forces a new resource to be created. From 7edd1c9da7910e6c9c064f941a420d05c03e6902 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sun, 23 Feb 2020 22:35:17 +0100 Subject: [PATCH 19/23] r/storage_account_customer_managed_key: fixing the example --- ...account_customer_managed_key.html.markdown | 68 +++++++++++++++---- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/website/docs/r/storage_account_customer_managed_key.html.markdown b/website/docs/r/storage_account_customer_managed_key.html.markdown index 9e5cb40195ac..4ae7721d8e44 100644 --- a/website/docs/r/storage_account_customer_managed_key.html.markdown +++ b/website/docs/r/storage_account_customer_managed_key.html.markdown @@ -13,20 +13,62 @@ Manages a Customer Managed Key for a Storage Account. ## Example Usage ```hcl -data "azurerm_key_vault" "example" { - name = "mykeyvault" - resource_group_name = "some-resource-group" + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_key_vault" "example" { + name = "examplekv" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + + soft_delete_enabled = true + purge_protection_enabled = true +} + +resource "azurerm_key_vault_access_policy" "storage" { + key_vault_id = azurerm_key_vault.example.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = azurerm_storage_account.example.identity.0.principal_id + + key_permissions = ["get", "create", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] } -data "azurerm_key_vault_key" "example" { - name = "secret-sauce" - key_vault_id = data.azurerm_key_vault.example.id +resource "azurerm_key_vault_access_policy" "client" { + key_vault_id = azurerm_key_vault.example.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_storage_account" "tfex" { - name = "exampleaccount" - resource_group_name = data.azurerm_key_vault.example.name - location = data.azurerm_key_vault.example.location + +resource "azurerm_key_vault_key" "example" { + name = "tfex-key" + key_vault_id = azurerm_key_vault.example.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_storage_account" "example" { + name = "examplestor" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location account_tier = "Standard" account_replication_type = "GRS" @@ -37,9 +79,9 @@ resource "azurerm_storage_account" "tfex" { resource "azurerm_storage_account_customer_managed_key" "example" { storage_account_id = azurerm_storage_account.example.id - key_vault_id = data.azurerm_key_vault.example.id - key_name = data.azurerm_key_vault_key.example.name - key_version = data.azurerm_key_vault_key.example.version + key_vault_id = azurerm_key_vault.example.id + key_name = azurerm_key_vault_key.example.name + key_version = azurerm_key_vault_key.example.version } ``` From ab5b5b32427f449705f57599daeb0ee2bbe13288 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sun, 23 Feb 2020 23:12:13 +0100 Subject: [PATCH 20/23] d/key_vault_key: removing a dead field --- .../services/keyvault/data_source_key_vault_key.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/azurerm/internal/services/keyvault/data_source_key_vault_key.go b/azurerm/internal/services/keyvault/data_source_key_vault_key.go index 470d2c8c7e72..3b1277b7d91f 100644 --- a/azurerm/internal/services/keyvault/data_source_key_vault_key.go +++ b/azurerm/internal/services/keyvault/data_source_key_vault_key.go @@ -28,13 +28,9 @@ func dataSourceArmKeyVaultKey() *schema.Resource { }, "key_vault_id": { - Type: schema.TypeString, - Computed: true, - }, - - "key_vault_access_policy_id": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, }, "key_type": { @@ -89,8 +85,6 @@ func dataSourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("Error looking up Key %q vault url from id %q: %+v", name, keyVaultId, err) } - d.Set("key_vault_id", keyVaultId) - resp, err := client.GetKey(ctx, keyVaultBaseUri, name, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { From d7c87e2045e268b30dfa2ce8ea32949f4a816e0f Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sun, 23 Feb 2020 23:18:30 +0100 Subject: [PATCH 21/23] d/storage_account: removing removed fields --- website/docs/d/storage_account.html.markdown | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/website/docs/d/storage_account.html.markdown b/website/docs/d/storage_account.html.markdown index eec3c0a3686c..f20cdc38a725 100644 --- a/website/docs/d/storage_account.html.markdown +++ b/website/docs/d/storage_account.html.markdown @@ -43,18 +43,10 @@ output "storage_account_tier" { * `access_tier` - The access tier for `BlobStorage` accounts. -* `enable_blob_encryption` - Are Encryption Services are enabled for Blob storage? See [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) - for more information. - -* `enable_file_encryption` - Are Encryption Services are enabled for File storage? See [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) - for more information. - * `enable_https_traffic_only` - Is traffic only allowed via HTTPS? See [here](https://docs.microsoft.com/en-us/azure/storage/storage-require-secure-transfer/) for more information. - -* `is_hns_enabled` - Is Hierarchical Namespace enabled? -* `account_encryption_source` - The Encryption Source for this Storage Account. +* `is_hns_enabled` - Is Hierarchical Namespace enabled? * `custom_domain` - A `custom_domain` block as documented below. From fb837ad446661442fd832a7ecaf24360a2ba4d6c Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 24 Feb 2020 06:48:12 +0100 Subject: [PATCH 22/23] linting --- ...orage_account_customer_managed_key_test.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go index 91c4dcdea3af..8d4e45edc18e 100644 --- a/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go +++ b/azurerm/internal/services/storage/tests/resource_arm_storage_account_customer_managed_key_test.go @@ -184,11 +184,11 @@ func testAccAzureRMStorageAccountCustomerManagedKey_updated(data acceptance.Test %s resource "azurerm_key_vault_key" "second" { - name = "second" - key_vault_id = azurerm_key_vault.test.id - key_type = "RSA" - key_size = 2048 - key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] + name = "second" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] depends_on = [ azurerm_key_vault_access_policy.client, @@ -251,11 +251,11 @@ resource "azurerm_key_vault_access_policy" "client" { } resource "azurerm_key_vault_key" "first" { - name = "first" - key_vault_id = azurerm_key_vault.test.id - key_type = "RSA" - key_size = 2048 - key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] + name = "first" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] depends_on = [ azurerm_key_vault_access_policy.client, From ef0105db9cf0367e9bf1ff7fe2101c7b0f646fc8 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 24 Feb 2020 06:48:22 +0100 Subject: [PATCH 23/23] r/storage_account_customer_managed_key: adding args to the error --- .../resource_arm_storage_account_customer_managed_key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go index d469bf8bee24..5eecd10114ed 100644 --- a/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go +++ b/azurerm/internal/services/storage/resource_arm_storage_account_customer_managed_key.go @@ -213,7 +213,7 @@ func resourceArmStorageAccountCustomerManagedKeyRead(d *schema.ResourceData, met } if keyVaultUri == "" { - return fmt.Errorf("Error retrieving Storage Account %q (Resource Group %q): `properties.encryption.keyVaultProperties.keyVaultUri` was nil") + return fmt.Errorf("Error retrieving Storage Account %q (Resource Group %q): `properties.encryption.keyVaultProperties.keyVaultUri` was nil", storageAccountId.Name, storageAccountId.ResourceGroup) } keyVaultId, err := azure.GetKeyVaultIDFromBaseUrl(ctx, vaultsClient, keyVaultUri)