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 +