diff --git a/azurerm/import_arm_storage_account_test.go b/azurerm/import_arm_storage_account_test.go index 48767f613561..296b027f99a7 100644 --- a/azurerm/import_arm_storage_account_test.go +++ b/azurerm/import_arm_storage_account_test.go @@ -31,3 +31,128 @@ func TestAccAzureRMStorageAccount_importBasic(t *testing.T) { }, }) } + +func TestAccAzureRMStorageAccount_importPremium(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + + ri := acctest.RandInt() + rs := acctest.RandString(4) + config := testAccAzureRMStorageAccount_premium(ri, rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccount_importNonStandardCasing(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + + ri := acctest.RandInt() + rs := acctest.RandString(4) + config := testAccAzureRMStorageAccount_nonStandardCasing(ri, rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccount_importBlobEncryption(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + + ri := acctest.RandInt() + rs := acctest.RandString(4) + config := testAccAzureRMStorageAccount_blobEncryption(ri, rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccount_importFileEncryption(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + + ri := acctest.RandInt() + rs := acctest.RandString(4) + config := testAccAzureRMStorageAccount_fileEncryption(ri, rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccount_importEnableHttpsTrafficOnly(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + + ri := acctest.RandInt() + rs := acctest.RandString(4) + config := testAccAzureRMStorageAccount_enableHttpsTrafficOnly(ri, rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/resource_arm_storage_account.go b/azurerm/resource_arm_storage_account.go index 9e1d993f1770..0f0e9c982933 100644 --- a/azurerm/resource_arm_storage_account.go +++ b/azurerm/resource_arm_storage_account.go @@ -29,6 +29,8 @@ func resourceArmStorageAccount() *schema.Resource { Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, + MigrateState: resourceStorageAccountMigrateState, + SchemaVersion: 1, Schema: map[string]*schema.Schema{ "name": { @@ -55,11 +57,36 @@ func resourceArmStorageAccount() *schema.Resource { "account_type": { Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, + Deprecated: "This field has been split into `account_tier` and `account_replication_type`", ValidateFunc: validateArmStorageAccountType, DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, + "account_tier": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "Standard", + "Premium", + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + + "account_replication_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "LRS", + "ZRS", + "GRS", + "RAGRS", + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + // Only valid for BlobStorage accounts, defaults to "Hot" in create function "access_tier": { Type: schema.TypeString, @@ -71,11 +98,36 @@ func resourceArmStorageAccount() *schema.Resource { }, true), }, + "custom_domain": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "use_subdomain": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + "enable_blob_encryption": { Type: schema.TypeBool, Optional: true, }, + "enable_file_encryption": { + Type: schema.TypeBool, + Optional: true, + }, + "enable_https_traffic_only": { Type: schema.TypeBool, Optional: true, @@ -150,6 +202,7 @@ func resourceArmStorageAccount() *schema.Resource { "tags": tagsSchema(), }, } + } func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error { @@ -159,35 +212,45 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e resourceGroupName := d.Get("resource_group_name").(string) storageAccountName := d.Get("name").(string) accountKind := d.Get("account_kind").(string) - accountType := d.Get("account_type").(string) location := d.Get("location").(string) tags := d.Get("tags").(map[string]interface{}) enableBlobEncryption := d.Get("enable_blob_encryption").(bool) enableHTTPSTrafficOnly := d.Get("enable_https_traffic_only").(bool) - sku := storage.Sku{ - Name: storage.SkuName(accountType), - } + accountTier := d.Get("account_tier").(string) + replicationType := d.Get("account_replication_type").(string) + storageType := fmt.Sprintf("%s_%s", accountTier, replicationType) - opts := storage.AccountCreateParameters{ + parameters := storage.AccountCreateParameters{ Location: &location, - Sku: &sku, - Tags: expandTags(tags), - Kind: storage.Kind(accountKind), + Sku: &storage.Sku{ + Name: storage.SkuName(storageType), + }, + Tags: expandTags(tags), + Kind: storage.Kind(accountKind), AccountPropertiesCreateParameters: &storage.AccountPropertiesCreateParameters{ Encryption: &storage.Encryption{ Services: &storage.EncryptionServices{ Blob: &storage.EncryptionService{ - Enabled: &enableBlobEncryption, - }, - }, + Enabled: utils.Bool(enableBlobEncryption), + }}, KeySource: &storageAccountEncryptionSource, }, EnableHTTPSTrafficOnly: &enableHTTPSTrafficOnly, }, } + if v, ok := d.GetOk("enable_file_encryption"); ok { + parameters.Encryption.Services.File = &storage.EncryptionService{ + Enabled: utils.Bool(v.(bool)), + } + } + + if _, ok := d.GetOk("custom_domain"); ok { + parameters.CustomDomain = expandStorageAccountCustomDomain(d) + } + // AccessTier is only valid for BlobStorage accounts if accountKind == string(storage.BlobStorage) { accessTier, ok := d.GetOk("access_tier") @@ -196,11 +259,11 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e accessTier = blobStorageAccountDefaultAccessTier } - opts.AccountPropertiesCreateParameters.AccessTier = storage.AccessTier(accessTier.(string)) + parameters.AccountPropertiesCreateParameters.AccessTier = storage.AccessTier(accessTier.(string)) } // Create - _, createError := storageClient.Create(resourceGroupName, storageAccountName, opts, make(chan struct{})) + _, createError := storageClient.Create(resourceGroupName, storageAccountName, parameters, make(chan struct{})) createErr := <-createError // The only way to get the ID back apparently is to read the resource again @@ -216,7 +279,7 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e // We do this later here so that we can grab the ID above is possible. if createErr != nil { return fmt.Errorf( - "Error creating Azure Storage Account '%s': %s", + "Error creating Azure Storage Account %q: %+v", storageAccountName, createErr) } @@ -227,7 +290,7 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e // If we got no ID then the resource group doesn't yet exist if read.ID == nil { - return fmt.Errorf("Cannot read Storage Account %s (resource group %s) ID", + return fmt.Errorf("Cannot read Storage Account %q (resource group %q) ID", storageAccountName, resourceGroupName) } @@ -260,11 +323,13 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e d.Partial(true) - if d.HasChange("account_type") { - accountType := d.Get("account_type").(string) + if d.HasChange("account_replication_type") { + accountTier := d.Get("account_tier").(string) + replicationType := d.Get("account_replication_type").(string) + storageType := fmt.Sprintf("%s_%s", accountTier, replicationType) sku := storage.Sku{ - Name: storage.SkuName(accountType), + Name: storage.SkuName(storageType), } opts := storage.AccountUpdateParameters{ @@ -272,10 +337,10 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e } _, err := client.Update(resourceGroupName, storageAccountName, opts) if err != nil { - return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err) + return fmt.Errorf("Error updating Azure Storage Account type %q: %+v", storageAccountName, err) } - d.SetPartial("account_type") + d.SetPartial("account_replication_type") } if d.HasChange("access_tier") { @@ -288,7 +353,7 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e } _, err := client.Update(resourceGroupName, storageAccountName, opts) if err != nil { - return fmt.Errorf("Error updating Azure Storage Account access_tier %q: %s", storageAccountName, err) + return fmt.Errorf("Error updating Azure Storage Account access_tier %q: %+v", storageAccountName, err) } d.SetPartial("access_tier") @@ -302,33 +367,58 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e } _, err := client.Update(resourceGroupName, storageAccountName, opts) if err != nil { - return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err) + return fmt.Errorf("Error updating Azure Storage Account tags %q: %+v", storageAccountName, err) } d.SetPartial("tags") } - if d.HasChange("enable_blob_encryption") { - enableBlobEncryption := d.Get("enable_blob_encryption").(bool) + if d.HasChange("enable_blob_encryption") || d.HasChange("enable_file_encryption") { opts := storage.AccountUpdateParameters{ AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ Encryption: &storage.Encryption{ - Services: &storage.EncryptionServices{ - Blob: &storage.EncryptionService{ - Enabled: &enableBlobEncryption, - }, - }, + Services: &storage.EncryptionServices{}, KeySource: &storageAccountEncryptionSource, }, }, } + + 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") + } + _, err := client.Update(resourceGroupName, storageAccountName, opts) if err != nil { - return fmt.Errorf("Error updating Azure Storage Account enable_blob_encryption %q: %s", storageAccountName, err) + return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) + } + } + + if d.HasChange("custom_domain") { + customDomain := expandStorageAccountCustomDomain(d) + opts := storage.AccountUpdateParameters{ + AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ + CustomDomain: customDomain, + }, } - d.SetPartial("enable_blob_encryption") + _, err := client.Update(resourceGroupName, storageAccountName, opts) + if err != nil { + return fmt.Errorf("Error updating Azure Storage Account Custom Domain %q: %+v", storageAccountName, err) + } } if d.HasChange("enable_https_traffic_only") { @@ -341,7 +431,7 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e } _, err := client.Update(resourceGroupName, storageAccountName, opts) if err != nil { - return fmt.Errorf("Error updating Azure Storage Account enable_https_traffic_only %q: %s", storageAccountName, err) + return fmt.Errorf("Error updating Azure Storage Account enable_https_traffic_only %q: %+v", storageAccountName, err) } d.SetPartial("enable_https_traffic_only") @@ -367,7 +457,7 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err d.SetId("") return nil } - return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %s", name, err) + return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %+v", name, err) } keys, err := client.ListKeys(resGroup, name) @@ -376,60 +466,80 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err } accessKeys := *keys.Keys + d.Set("name", resp.Name) d.Set("resource_group_name", resGroup) - d.Set("primary_access_key", accessKeys[0].Value) - d.Set("secondary_access_key", accessKeys[1].Value) d.Set("location", azureRMNormalizeLocation(*resp.Location)) d.Set("account_kind", resp.Kind) - d.Set("account_type", resp.Sku.Name) - d.Set("primary_location", resp.AccountProperties.PrimaryLocation) - d.Set("secondary_location", resp.AccountProperties.SecondaryLocation) - d.Set("enable_https_traffic_only", resp.AccountProperties.EnableHTTPSTrafficOnly) - if resp.AccountProperties.AccessTier != "" { - d.Set("access_tier", resp.AccountProperties.AccessTier) + if sku := resp.Sku; sku != nil { + d.Set("account_type", sku.Name) + d.Set("account_tier", sku.Tier) + d.Set("account_replication_type", strings.Split(fmt.Sprintf("%v", sku.Name), "_")[1]) } - if resp.AccountProperties.PrimaryEndpoints != nil { - d.Set("primary_blob_endpoint", resp.AccountProperties.PrimaryEndpoints.Blob) - d.Set("primary_queue_endpoint", resp.AccountProperties.PrimaryEndpoints.Queue) - d.Set("primary_table_endpoint", resp.AccountProperties.PrimaryEndpoints.Table) - d.Set("primary_file_endpoint", resp.AccountProperties.PrimaryEndpoints.File) - - pscs := fmt.Sprintf("DefaultEndpointsProtocol=https;BlobEndpoint=%s;AccountName=%s;AccountKey=%s", - *resp.AccountProperties.PrimaryEndpoints.Blob, *resp.Name, *accessKeys[0].Value) - d.Set("primary_blob_connection_string", pscs) - } + if props := resp.AccountProperties; props != nil { + d.Set("access_tier", props.AccessTier) + d.Set("enable_https_traffic_only", props.EnableHTTPSTrafficOnly) - if resp.AccountProperties.SecondaryEndpoints != nil { - if resp.AccountProperties.SecondaryEndpoints.Blob != nil { - d.Set("secondary_blob_endpoint", resp.AccountProperties.SecondaryEndpoints.Blob) - sscs := fmt.Sprintf("DefaultEndpointsProtocol=https;BlobEndpoint=%s;AccountName=%s;AccountKey=%s", - *resp.AccountProperties.SecondaryEndpoints.Blob, *resp.Name, *accessKeys[1].Value) - d.Set("secondary_blob_connection_string", sscs) - } else { - d.Set("secondary_blob_endpoint", "") - d.Set("secondary_blob_connection_string", "") + if customDomain := props.CustomDomain; customDomain != nil { + if err := d.Set("custom_domain", flattenStorageAccountCustomDomain(customDomain)); err != nil { + return fmt.Errorf("Error flattening `custom_domain`: %+v", err) + } } - if resp.AccountProperties.SecondaryEndpoints.Queue != nil { - d.Set("secondary_queue_endpoint", resp.AccountProperties.SecondaryEndpoints.Queue) - } else { - d.Set("secondary_queue_endpoint", "") + + 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 resp.AccountProperties.SecondaryEndpoints.Table != nil { - d.Set("secondary_table_endpoint", resp.AccountProperties.SecondaryEndpoints.Table) - } else { - d.Set("secondary_table_endpoint", "") + + // Computed + d.Set("primary_location", props.PrimaryLocation) + d.Set("secondary_location", props.SecondaryLocation) + + if endpoints := props.PrimaryEndpoints; endpoints != nil { + d.Set("primary_blob_endpoint", endpoints.Blob) + d.Set("primary_queue_endpoint", endpoints.Queue) + d.Set("primary_table_endpoint", endpoints.Table) + d.Set("primary_file_endpoint", endpoints.File) + + pscs := fmt.Sprintf("DefaultEndpointsProtocol=https;BlobEndpoint=%s;AccountName=%s;AccountKey=%s", + *endpoints.Blob, *resp.Name, *accessKeys[0].Value) + d.Set("primary_blob_connection_string", pscs) } - } - if resp.AccountProperties.Encryption != nil { - if resp.AccountProperties.Encryption.Services.Blob != nil { - d.Set("enable_blob_encryption", resp.AccountProperties.Encryption.Services.Blob.Enabled) + if endpoints := props.SecondaryEndpoints; endpoints != nil { + if blob := endpoints.Blob; blob != nil { + d.Set("secondary_blob_endpoint", blob) + sscs := fmt.Sprintf("DefaultEndpointsProtocol=https;BlobEndpoint=%s;AccountName=%s;AccountKey=%s", + *blob, *resp.Name, *accessKeys[1].Value) + d.Set("secondary_blob_connection_string", sscs) + } else { + d.Set("secondary_blob_endpoint", "") + d.Set("secondary_blob_connection_string", "") + } + + if endpoints.Queue != nil { + d.Set("secondary_queue_endpoint", endpoints.Queue) + } else { + d.Set("secondary_queue_endpoint", "") + } + + if endpoints.Table != nil { + d.Set("secondary_table_endpoint", endpoints.Table) + } else { + d.Set("secondary_table_endpoint", "") + } } } - d.Set("name", resp.Name) + d.Set("primary_access_key", accessKeys[0].Value) + d.Set("secondary_access_key", accessKeys[1].Value) flattenAndSetTags(d, resp.Tags) @@ -448,12 +558,32 @@ func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) e _, err = client.Delete(resGroup, name) if err != nil { - return fmt.Errorf("Error issuing AzureRM delete request for storage account %q: %s", name, err) + return fmt.Errorf("Error issuing AzureRM delete request for storage account %q: %+v", name, err) } return nil } +func expandStorageAccountCustomDomain(d *schema.ResourceData) *storage.CustomDomain { + domains := d.Get("custom_domain").([]interface{}) + domain := domains[0].(map[string]interface{}) + name := domain["name"].(string) + useSubDomain := domain["use_subdomain"].(bool) + return &storage.CustomDomain{ + Name: utils.String(name), + UseSubDomain: utils.Bool(useSubDomain), + } +} + +func flattenStorageAccountCustomDomain(input *storage.CustomDomain) []interface{} { + domain := make(map[string]interface{}, 0) + + domain["name"] = *input.Name + // use_subdomain isn't returned + + return []interface{}{domain} +} + func validateArmStorageAccountName(v interface{}, k string) (ws []string, es []error) { input := v.(string) diff --git a/azurerm/resource_arm_storage_account_migration.go b/azurerm/resource_arm_storage_account_migration.go new file mode 100644 index 000000000000..694e05756d89 --- /dev/null +++ b/azurerm/resource_arm_storage_account_migration.go @@ -0,0 +1,38 @@ +package azurerm + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/terraform" +) + +func resourceStorageAccountMigrateState( + v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { + switch v { + case 0: + log.Println("[INFO] Found AzureRM Storage Account State v0; migrating to v1") + return migrateStorageAccountStateV0toV1(is) + default: + return is, fmt.Errorf("Unexpected schema version: %d", v) + } +} + +func migrateStorageAccountStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) { + if is.Empty() { + log.Println("[DEBUG] Empty InstanceState; nothing to migrate.") + return is, nil + } + + log.Printf("[DEBUG] ARM Storage Account Attributes before Migration: %#v", is.Attributes) + + accountType := is.Attributes["account_type"] + split := strings.Split(accountType, "_") + is.Attributes["account_tier"] = split[0] + is.Attributes["account_replication_type"] = split[1] + + log.Printf("[DEBUG] ARM Storage Account Attributes after State Migration: %#v", is.Attributes) + + return is, nil +} diff --git a/azurerm/resource_arm_storage_account_migration_test.go b/azurerm/resource_arm_storage_account_migration_test.go new file mode 100644 index 000000000000..25469557ff8b --- /dev/null +++ b/azurerm/resource_arm_storage_account_migration_test.go @@ -0,0 +1,59 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestAzureRMStorageAccountMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + ID string + InputAttributes map[string]string + ExpectedAttributes map[string]string + Meta interface{} + }{ + "v0_1_with_standard": { + StateVersion: 0, + ID: "some_id", + InputAttributes: map[string]string{ + "account_type": "Standard_LRS", + }, + ExpectedAttributes: map[string]string{ + "account_tier": "Standard", + "account_replication_type": "LRS", + }, + }, + "v0_1_with_premium": { + StateVersion: 0, + ID: "some_id", + InputAttributes: map[string]string{ + "account_type": "Premium_GRS", + }, + ExpectedAttributes: map[string]string{ + "account_tier": "Premium", + "account_replication_type": "GRS", + }, + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: tc.ID, + Attributes: tc.InputAttributes, + } + is, err := resourceStorageAccountMigrateState(tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + for k, v := range tc.ExpectedAttributes { + actual := is.Attributes[k] + if actual != v { + t.Fatalf("Bad Storage Account Migrate for %q: %q\n\n expected: %q", k, actual, v) + } + } + } +} diff --git a/azurerm/resource_arm_storage_account_test.go b/azurerm/resource_arm_storage_account_test.go index 33b33368f98e..fb3fe5bdc862 100644 --- a/azurerm/resource_arm_storage_account_test.go +++ b/azurerm/resource_arm_storage_account_test.go @@ -67,7 +67,8 @@ func TestAccAzureRMStorageAccount_basic(t *testing.T) { Config: preConfig, Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "account_type", "Standard_LRS"), + resource.TestCheckResourceAttr(resourceName, "account_tier", "Standard"), + resource.TestCheckResourceAttr(resourceName, "account_replication_type", "LRS"), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.environment", "production"), ), @@ -77,7 +78,8 @@ func TestAccAzureRMStorageAccount_basic(t *testing.T) { Config: postConfig, Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "account_type", "Standard_GRS"), + resource.TestCheckResourceAttr(resourceName, "account_tier", "Standard"), + resource.TestCheckResourceAttr(resourceName, "account_replication_type", "GRS"), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.environment", "staging"), ), @@ -86,6 +88,32 @@ func TestAccAzureRMStorageAccount_basic(t *testing.T) { }) } +func TestAccAzureRMStorageAccount_premium(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + ri := acctest.RandInt() + rs := acctest.RandString(4) + location := testLocation() + preConfig := testAccAzureRMStorageAccount_premium(ri, rs, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "account_tier", "Premium"), + resource.TestCheckResourceAttr(resourceName, "account_replication_type", "LRS"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "production"), + ), + }, + }, + }) +} + func TestAccAzureRMStorageAccount_disappears(t *testing.T) { resourceName := "azurerm_storage_account.testsa" ri := acctest.RandInt() @@ -101,7 +129,8 @@ func TestAccAzureRMStorageAccount_disappears(t *testing.T) { Config: preConfig, Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "account_type", "Standard_LRS"), + resource.TestCheckResourceAttr(resourceName, "account_tier", "Standard"), + resource.TestCheckResourceAttr(resourceName, "account_replication_type", "LRS"), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.environment", "production"), testCheckAzureRMStorageAccountDisappears(resourceName), @@ -164,6 +193,37 @@ func TestAccAzureRMStorageAccount_blobEncryption(t *testing.T) { }) } +func TestAccAzureRMStorageAccount_fileEncryption(t *testing.T) { + ri := acctest.RandInt() + rs := acctest.RandString(4) + location := testLocation() + preConfig := testAccAzureRMStorageAccount_fileEncryption(ri, rs, location) + postConfig := testAccAzureRMStorageAccount_fileEncryptionDisabled(ri, rs, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists("azurerm_storage_account.testsa"), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "enable_file_encryption", "true"), + ), + }, + + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists("azurerm_storage_account.testsa"), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "enable_file_encryption", "false"), + ), + }, + }, + }) +} + func TestAccAzureRMStorageAccount_enableHttpsTrafficOnly(t *testing.T) { ri := acctest.RandInt() rs := acctest.RandString(4) @@ -230,7 +290,7 @@ func TestAccAzureRMStorageAccount_blobStorageWithUpdate(t *testing.T) { func TestAccAzureRMStorageAccount_NonStandardCasing(t *testing.T) { ri := acctest.RandInt() rs := acctest.RandString(4) - preConfig := testAccAzureRMStorageAccountNonStandardCasing(ri, rs, testLocation()) + preConfig := testAccAzureRMStorageAccount_nonStandardCasing(ri, rs, testLocation()) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -339,12 +399,36 @@ resource "azurerm_storage_account" "testsa" { resource_group_name = "${azurerm_resource_group.testrg.name}" location = "${azurerm_resource_group.testrg.location}" - account_type = "Standard_LRS" + account_tier = "Standard" + account_replication_type = "LRS" tags { environment = "production" } -}`, rInt, location, rString) +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_premium(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "testAccAzureRMSA-%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 = "Premium" + account_replication_type = "LRS" + + tags { + environment = "production" + } +} +`, rInt, location, rString) } func testAccAzureRMStorageAccount_update(rInt int, rString string, location string) string { @@ -359,12 +443,14 @@ resource "azurerm_storage_account" "testsa" { resource_group_name = "${azurerm_resource_group.testrg.name}" location = "${azurerm_resource_group.testrg.location}" - account_type = "Standard_GRS" + account_tier = "Standard" + account_replication_type = "GRS" tags { environment = "staging" } -}`, rInt, location, rString) +} +`, rInt, location, rString) } func testAccAzureRMStorageAccount_blobEncryption(rInt int, rString string, location string) string { @@ -379,13 +465,15 @@ resource "azurerm_storage_account" "testsa" { resource_group_name = "${azurerm_resource_group.testrg.name}" location = "${azurerm_resource_group.testrg.location}" - account_type = "Standard_LRS" + account_tier = "Standard" + account_replication_type = "LRS" enable_blob_encryption = true tags { environment = "production" } -}`, rInt, location, rString) +} +`, rInt, location, rString) } func testAccAzureRMStorageAccount_blobEncryptionDisabled(rInt int, rString string, location string) string { @@ -400,13 +488,61 @@ resource "azurerm_storage_account" "testsa" { resource_group_name = "${azurerm_resource_group.testrg.name}" location = "${azurerm_resource_group.testrg.location}" - account_type = "Standard_LRS" + account_tier = "Standard" + account_replication_type = "LRS" enable_blob_encryption = false tags { environment = "production" } -}`, rInt, location, rString) +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_fileEncryption(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "testAccAzureRMSA-%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" + enable_file_encryption = true + + tags { + environment = "production" + } +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_fileEncryptionDisabled(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "testAccAzureRMSA-%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" + enable_file_encryption = false + + tags { + environment = "production" + } +} +`, rInt, location, rString) } func testAccAzureRMStorageAccount_enableHttpsTrafficOnly(rInt int, rString string, location string) string { @@ -421,13 +557,15 @@ resource "azurerm_storage_account" "testsa" { resource_group_name = "${azurerm_resource_group.testrg.name}" location = "${azurerm_resource_group.testrg.location}" - account_type = "Standard_LRS" + account_tier = "Standard" + account_replication_type = "LRS" enable_https_traffic_only = true tags { environment = "production" } -}`, rInt, location, rString) +} +`, rInt, location, rString) } func testAccAzureRMStorageAccount_enableHttpsTrafficOnlyDisabled(rInt int, rString string, location string) string { @@ -442,13 +580,15 @@ resource "azurerm_storage_account" "testsa" { resource_group_name = "${azurerm_resource_group.testrg.name}" location = "${azurerm_resource_group.testrg.location}" - account_type = "Standard_LRS" + account_tier = "Standard" + account_replication_type = "LRS" enable_https_traffic_only = false tags { environment = "production" } -}`, rInt, location, rString) +} +`, rInt, location, rString) } func testAccAzureRMStorageAccount_blobStorage(rInt int, rString string, location string) string { @@ -464,7 +604,8 @@ resource "azurerm_storage_account" "testsa" { location = "${azurerm_resource_group.testrg.location}" account_kind = "BlobStorage" - account_type = "Standard_LRS" + account_tier = "Standard" + account_replication_type = "LRS" tags { environment = "production" @@ -486,7 +627,8 @@ resource "azurerm_storage_account" "testsa" { location = "${azurerm_resource_group.testrg.location}" account_kind = "BlobStorage" - account_type = "Standard_LRS" + account_tier = "Standard" + account_replication_type = "LRS" access_tier = "Cool" tags { @@ -496,7 +638,7 @@ resource "azurerm_storage_account" "testsa" { `, rInt, location, rString) } -func testAccAzureRMStorageAccountNonStandardCasing(rInt int, rString string, location string) string { +func testAccAzureRMStorageAccount_nonStandardCasing(rInt int, rString string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "testrg" { name = "testAccAzureRMSA-%d" @@ -506,9 +648,11 @@ resource "azurerm_storage_account" "testsa" { name = "unlikely23exst2acct%s" resource_group_name = "${azurerm_resource_group.testrg.name}" location = "${azurerm_resource_group.testrg.location}" - account_type = "standard_LRS" + account_tier = "standard" + account_replication_type = "lrs" tags { environment = "production" } -}`, rInt, location, rString) +} +`, rInt, location, rString) } diff --git a/website/docs/r/storage_account.html.markdown b/website/docs/r/storage_account.html.markdown index 900ce9f463fe..dc02ec3f9548 100644 --- a/website/docs/r/storage_account.html.markdown +++ b/website/docs/r/storage_account.html.markdown @@ -49,11 +49,9 @@ The following arguments are supported: and `BlobStorage`. Changing this forces a new resource to be created. Defaults to `Storage`. -* `account_type` - (Required) Defines the type of storage account to be - created. Valid options are `Standard_LRS`, `Standard_ZRS`, `Standard_GRS`, - `Standard_RAGRS`, `Premium_LRS`. Changing this is sometimes valid - see the Azure - documentation for more information on which types of accounts can be converted - into other types. +* `account_tier` - (Required) Defines the Tier to use for this storage account. Valid options are `Standard` and `Premium`. Changing this forces a new resource to be created + +* `account_replication_type` - (Required) Defines the type of replication to use for this storage account. Valid options are `LRS`, `GRS`, `RAGRS` and `ZRS`. * `access_tier` - (Required for `BlobStorage` accounts) Defines the access tier for `BlobStorage` accounts. Valid options are `Hot` and `Cold`, defaults to @@ -63,13 +61,25 @@ The following arguments are supported: Services are enabled for Blob storage, see [here](https://azure.microsoft.com/en-us/documentation/articles/storage-service-encryption/) for more information. +* `enable_file_encryption` - (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. + * `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. +* `custom_domain` - (Optional) A `custom_domain` block as documented below. + * `tags` - (Optional) A mapping of tags to assign to the resource. -Note that although the Azure API supports setting custom domain names for -storage accounts, this is not currently supported. +--- + +* `custom_domain` supports the following: + +* `name` - (Optional) The Custom Domain Name to use for the Storage Account, which will be validated by Azure. +* `use_subdomain` - (Optional) Should the Custom Domain Name be validated by using indirect CNAME validation? + +~> **Note:** [More information on Validation is available here](https://docs.microsoft.com/en-gb/azure/storage/blobs/storage-custom-domain-name) ## Attributes Reference