diff --git a/azurerm/internal/services/storage/storage_account_resource.go b/azurerm/internal/services/storage/storage_account_resource.go index 96d629c63952..0eee4b845b45 100644 --- a/azurerm/internal/services/storage/storage_account_resource.go +++ b/azurerm/internal/services/storage/storage_account_resource.go @@ -934,6 +934,18 @@ func resourceStorageAccountCreate(d *pluginsdk.ResourceData, meta interface{}) e blobProperties := expandBlobProperties(val.([]interface{})) + // last_access_time_enabled and container_delete_retention_policy are not supported in USGov + // Fix issue https://github.com/terraform-providers/terraform-provider-azurerm/issues/11772 + if v := d.Get("blob_properties.0.last_access_time_enabled").(bool); v { + blobProperties.LastAccessTimeTrackingPolicy = &storage.LastAccessTimeTrackingPolicy{ + Enable: utils.Bool(v), + } + } + + if v, ok := d.GetOk("blob_properties.0.container_delete_retention_policy"); ok { + blobProperties.ContainerDeleteRetentionPolicy = expandBlobPropertiesDeleteRetentionPolicy(v.([]interface{}), false) + } + if _, err = blobClient.SetServiceProperties(ctx, resourceGroupName, storageAccountName, *blobProperties); err != nil { return fmt.Errorf("Error updating Azure Storage Account `blob_properties` %q: %+v", storageAccountName, err) } @@ -1268,6 +1280,22 @@ func resourceStorageAccountUpdate(d *pluginsdk.ResourceData, meta interface{}) e blobClient := meta.(*clients.Client).Storage.BlobServicesClient blobProperties := expandBlobProperties(d.Get("blob_properties").([]interface{})) + // last_access_time_enabled and container_delete_retention_policy are not supported in USGov + // Fix issue https://github.com/terraform-providers/terraform-provider-azurerm/issues/11772 + if d.HasChange("blob_properties.0.last_access_time_enabled") { + lastAccessTimeTracking := false + if v := d.Get("blob_properties.0.last_access_time_enabled").(bool); v { + lastAccessTimeTracking = true + } + blobProperties.LastAccessTimeTrackingPolicy = &storage.LastAccessTimeTrackingPolicy{ + Enable: utils.Bool(lastAccessTimeTracking), + } + } + + if d.HasChange("blob_properties.0.container_delete_retention_policy") { + blobProperties.ContainerDeleteRetentionPolicy = expandBlobPropertiesDeleteRetentionPolicy(d.Get("blob_properties.0.container_delete_retention_policy").([]interface{}), true) + } + if _, err = blobClient.SetServiceProperties(ctx, resourceGroupName, storageAccountName, *blobProperties); err != nil { return fmt.Errorf("Error updating Azure Storage Account `blob_properties` %q: %+v", storageAccountName, err) } @@ -1806,10 +1834,6 @@ func expandBlobProperties(input []interface{}) *storage.BlobServiceProperties { ChangeFeed: &storage.ChangeFeed{ Enabled: utils.Bool(false), }, - LastAccessTimeTrackingPolicy: &storage.LastAccessTimeTrackingPolicy{ - Enable: utils.Bool(false), - }, - DeleteRetentionPolicy: &storage.DeleteRetentionPolicy{ Enabled: utils.Bool(false), }, @@ -1823,9 +1847,7 @@ func expandBlobProperties(input []interface{}) *storage.BlobServiceProperties { v := input[0].(map[string]interface{}) deletePolicyRaw := v["delete_retention_policy"].([]interface{}) - props.BlobServicePropertiesProperties.DeleteRetentionPolicy = expandBlobPropertiesDeleteRetentionPolicy(deletePolicyRaw) - containerDeletePolicyRaw := v["container_delete_retention_policy"].([]interface{}) - props.BlobServicePropertiesProperties.ContainerDeleteRetentionPolicy = expandBlobPropertiesDeleteRetentionPolicy(containerDeletePolicyRaw) + props.BlobServicePropertiesProperties.DeleteRetentionPolicy = expandBlobPropertiesDeleteRetentionPolicy(deletePolicyRaw, true) corsRaw := v["cors_rule"].([]interface{}) props.BlobServicePropertiesProperties.Cors = expandBlobPropertiesCors(corsRaw) @@ -1839,27 +1861,27 @@ func expandBlobProperties(input []interface{}) *storage.BlobServiceProperties { props.DefaultServiceVersion = utils.String(version) } - props.LastAccessTimeTrackingPolicy = &storage.LastAccessTimeTrackingPolicy{ - Enable: utils.Bool(v["last_access_time_enabled"].(bool)), - } return &props } -func expandBlobPropertiesDeleteRetentionPolicy(input []interface{}) *storage.DeleteRetentionPolicy { - deleteRetentionPolicy := storage.DeleteRetentionPolicy{ +func expandBlobPropertiesDeleteRetentionPolicy(input []interface{}, isupdate bool) *storage.DeleteRetentionPolicy { + result := storage.DeleteRetentionPolicy{ Enabled: utils.Bool(false), } + if (len(input) == 0 || input[0] == nil) && !isupdate { + return nil + } - if len(input) == 0 { - return &deleteRetentionPolicy + if (len(input) == 0 || input[0] == nil) && isupdate { + return &result } policy := input[0].(map[string]interface{}) - days := policy["days"].(int) - deleteRetentionPolicy.Enabled = utils.Bool(true) - deleteRetentionPolicy.Days = utils.Int32(int32(days)) - return &deleteRetentionPolicy + return &storage.DeleteRetentionPolicy{ + Enabled: utils.Bool(true), + Days: utils.Int32(int32(policy["days"].(int))), + } } func expandBlobPropertiesCors(input []interface{}) *storage.CorsRules { diff --git a/azurerm/internal/services/storage/storage_account_resource_test.go b/azurerm/internal/services/storage/storage_account_resource_test.go index fa5fe414d676..e49f8066401b 100644 --- a/azurerm/internal/services/storage/storage_account_resource_test.go +++ b/azurerm/internal/services/storage/storage_account_resource_test.go @@ -599,6 +599,28 @@ func TestAccStorageAccount_blobProperties(t *testing.T) { }) } +func TestAccStorageAccount_blobProperties_containerAndLastAccessTimeDisabled(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") + r := StorageAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.blobPropertiesContainerAndLastAccessTimeDisabled(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.blobPropertiesContainerAndLastAccessTimeDisabledUpdated(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccStorageAccount_blobPropertiesEmptyAllowedExposedHeaders(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") r := StorageAccountResource{} @@ -932,7 +954,7 @@ resource "azurerm_storage_account" "test" { account_replication_type = "LRS" tags = { - %s + %s } } `, data.RandomInteger, data.Locations.Primary, data.RandomString, tags) @@ -1928,6 +1950,89 @@ resource "azurerm_storage_account" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString) } +func (r StorageAccountResource) blobPropertiesContainerAndLastAccessTimeDisabled(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestAzureRMSA-%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" + + blob_properties { + cors_rule { + allowed_origins = ["http://www.example.com"] + exposed_headers = ["x-tempo-*"] + allowed_headers = ["x-tempo-*"] + allowed_methods = ["GET", "PUT", "PATCH"] + max_age_in_seconds = "500" + } + + delete_retention_policy { + days = 300 + } + + default_service_version = "2019-07-07" + versioning_enabled = true + change_feed_enabled = true + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} + +func (r StorageAccountResource) blobPropertiesContainerAndLastAccessTimeDisabledUpdated(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestAzureRMSA-%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" + + blob_properties { + cors_rule { + allowed_origins = ["http://www.example.com"] + exposed_headers = ["x-tempo-*", "x-method-*"] + allowed_headers = ["*"] + allowed_methods = ["GET"] + max_age_in_seconds = "2000000000" + } + + cors_rule { + allowed_origins = ["http://www.test.com"] + exposed_headers = ["x-tempo-*"] + allowed_headers = ["*"] + allowed_methods = ["PUT"] + max_age_in_seconds = "1000" + } + + delete_retention_policy { + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} + func (r StorageAccountResource) blobPropertiesUpdatedEmptyAllowedExposedHeaders(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/storage_account.html.markdown b/website/docs/r/storage_account.html.markdown index 004cd9567525..9ee394fdf102 100644 --- a/website/docs/r/storage_account.html.markdown +++ b/website/docs/r/storage_account.html.markdown @@ -155,6 +155,8 @@ A `blob_properties` block supports the following: * `container_delete_retention_policy` - (Optional) A `container_delete_retention_policy` block as defined below. +~> **Note:** Before setting `container_delete_retention_policy`, the feature `ContainerSoftDelete` needs to be enabled by [steps](https://docs.microsoft.com/en-us/azure/storage/blobs/soft-delete-container-overview?tabs=powershell#register-for-the-preview) + --- A `cors_rule` block supports the following: