diff --git a/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource.go b/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource.go index 1b6ba0aaa533..aab15de3c724 100644 --- a/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource.go +++ b/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource.go @@ -4,9 +4,12 @@ package dataprotection import ( + "context" + "encoding/json" "fmt" "log" "regexp" + "strconv" "strings" "time" @@ -17,6 +20,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -24,7 +28,7 @@ import ( ) func resourceDataProtectionBackupPolicyPostgreSQL() *pluginsdk.Resource { - return &pluginsdk.Resource{ + resource := &pluginsdk.Resource{ Create: resourceDataProtectionBackupPolicyPostgreSQLCreate, Read: resourceDataProtectionBackupPolicyPostgreSQLRead, Delete: resourceDataProtectionBackupPolicyPostgreSQLDelete, @@ -69,11 +73,69 @@ func resourceDataProtectionBackupPolicyPostgreSQL() *pluginsdk.Resource { }, }, - "default_retention_duration": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validate.ISO8601Duration, + "default_retention_rule": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "life_cycle": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "data_store_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + // confirmed with the service team that the possible values do not include `OperationalStore`. + string(backuppolicies.DataStoreTypesVaultStore), + string(backuppolicies.DataStoreTypesArchiveStore), + }, false), + }, + + "duration": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ISO8601Duration, + }, + + "target_copy": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "option_json": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsJSON, + }, + "data_store_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + // since the following feedback from the service team, the current possible values only support `ArchiveStore`. + // In view of possible support for `VaultStore` in the future, the `data_store_type` property is exposed for users to set in version 5.0. + // feedback from the service team: Theoretically all 3 values possible. But currently only logical combination is from VaultStore to ArchiveStore. So in target data store it can only be ArchiveStore. OperationalStore isn’t supported for this workload. + string(backuppolicies.DataStoreTypesArchiveStore), + }, false), + }, + }, + }, + }, + }, + }, + }, + }, + }, }, "retention_rule": { @@ -88,13 +150,6 @@ func resourceDataProtectionBackupPolicyPostgreSQL() *pluginsdk.Resource { ForceNew: true, }, - "duration": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validate.ISO8601Duration, - }, - "criteria": { Type: pluginsdk.TypeList, Required: true, @@ -168,6 +223,61 @@ func resourceDataProtectionBackupPolicyPostgreSQL() *pluginsdk.Resource { }, }, + "life_cycle": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "data_store_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + // confirmed with the service team that the possible values do not include `OperationalStore`. + string(backuppolicies.DataStoreTypesVaultStore), + string(backuppolicies.DataStoreTypesArchiveStore), + }, false), + }, + + "duration": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ISO8601Duration, + }, + + "target_copy": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "option_json": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsJSON, + }, + "data_store_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + // since the following feedback from the service team, the current possible values only support `ArchiveStore`. + // However, in view of possible support for `VaultStore` in the future, the `data_store_type` property is exposed for users to set in version 5.0. + // feedback from the service team: Theoretically all 3 values possible. But currently only logical combination is from VaultStore to ArchiveStore. So in target data store it can only be ArchiveStore. OperationalStore isn’t supported for this workload. + string(backuppolicies.DataStoreTypesArchiveStore), + }, false), + }, + }, + }, + }, + }, + }, + }, + "priority": { Type: pluginsdk.TypeInt, Required: true, @@ -185,6 +295,61 @@ func resourceDataProtectionBackupPolicyPostgreSQL() *pluginsdk.Resource { }, }, } + + if !features.FivePointOh() { + resource.Schema["default_retention_duration"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + ConflictsWith: []string{"default_retention_rule"}, + Deprecated: "`default_retention_duration` has been deprecated in favour of the `default_retention_rule.0.life_cycle.#.duration` and will be removed in version 5.0 of the AzureRM Provider.", + ValidateFunc: validate.ISO8601Duration, + } + + // Since the length of `retention_rule` may be greater than 1, it cannot be set via `ConflictsWith`, the conflict check is performed in CustomizeDiff. + resource.Schema["retention_rule"].Elem.(*pluginsdk.Resource).Schema["duration"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + Deprecated: "`retention_rule.#.duration` has been deprecated in favour of the `retention_rule.#.life_cycle.#.duration` and will be removed in version 5.0 of the AzureRM Provider.", + ValidateFunc: validate.ISO8601Duration, + } + + resource.Schema["retention_rule"].Elem.(*pluginsdk.Resource).Schema["life_cycle"].Required = false + resource.Schema["retention_rule"].Elem.(*pluginsdk.Resource).Schema["life_cycle"].Optional = true + resource.Schema["retention_rule"].Elem.(*pluginsdk.Resource).Schema["life_cycle"].Computed = true + + resource.Schema["default_retention_rule"].Required = false + resource.Schema["default_retention_rule"].Optional = true + resource.Schema["default_retention_rule"].Computed = true + resource.Schema["default_retention_rule"].ConflictsWith = []string{"default_retention_duration"} + + pluginsdk.CustomizeDiffShim(func(ctx context.Context, diff *pluginsdk.ResourceDiff, v interface{}) error { + retentionRules := diff.Get("retention_rule") + defaultRetentionDuration := diff.Get("default_retention_duration") + defaultRetentionRule := diff.Get("default_retention_rule") + + for i, rule := range retentionRules.([]interface{}) { + v := rule.(map[string]interface{}) + if v["duration"].(string) == "" && len(v["life_cycle"].([]interface{})) == 0 || v["duration"].(string) != "" && len(v["life_cycle"].([]interface{})) > 0 { + return fmt.Errorf(`one of "retention_rule.%s.duration", "retention_rule.%s.life_cycle" must be specified`, strconv.Itoa(i), strconv.Itoa(i)) + } + + if defaultRetentionDuration != "" && v["duration"].(string) == "" { + return fmt.Errorf(`"default_retention_duration", "retention_rule.%s.duration" must be specified at the same time`, strconv.Itoa(i)) + } + + if len(defaultRetentionRule.([]interface{})) > 0 && len(v["life_cycle"].([]interface{})) == 0 { + return fmt.Errorf(`"default_retention_rule", "retention_rule.%s.life_cycle" must be specified at the same time`, strconv.Itoa(i)) + } + } + return nil + }) + } + + return resource } func resourceDataProtectionBackupPolicyPostgreSQLCreate(d *pluginsdk.ResourceData, meta interface{}) error { @@ -216,8 +381,20 @@ func resourceDataProtectionBackupPolicyPostgreSQLCreate(d *pluginsdk.ResourceDat policyRules := make([]backuppolicies.BasePolicyRule, 0) policyRules = append(policyRules, expandBackupPolicyPostgreSQLAzureBackupRuleArray(d.Get("backup_repeating_time_intervals").([]interface{}), d.Get("time_zone").(string), taggingCriteria)...) - policyRules = append(policyRules, expandBackupPolicyPostgreSQLDefaultAzureRetentionRule(d.Get("default_retention_duration"))) - policyRules = append(policyRules, expandBackupPolicyPostgreSQLAzureRetentionRuleArray(d.Get("retention_rule").([]interface{}))...) + + if !features.FivePointOh() { + if v, ok := d.GetOk("default_retention_duration"); ok { + policyRules = append(policyRules, expandBackupPolicyPostgreSQLDefaultAzureRetentionRule(v)) + policyRules = append(policyRules, expandBackupPolicyPostgreSQLAzureRetentionRuleArray(d.Get("retention_rule").([]interface{}))...) + } else { + policyRules = append(policyRules, expandBackupPolicyPostgreSQLDefaultRetentionRule(d.Get("default_retention_rule").([]interface{}))) + policyRules = append(policyRules, expandBackupPolicyPostgreSQLAzureRetentionRules(d.Get("retention_rule").([]interface{}))...) + } + } else { + policyRules = append(policyRules, expandBackupPolicyPostgreSQLDefaultRetentionRule(d.Get("default_retention_rule").([]interface{}))) + policyRules = append(policyRules, expandBackupPolicyPostgreSQLAzureRetentionRules(d.Get("retention_rule").([]interface{}))...) + } + parameters := backuppolicies.BaseBackupPolicyResource{ Properties: &backuppolicies.BackupPolicy{ PolicyRules: policyRules, @@ -262,12 +439,31 @@ func resourceDataProtectionBackupPolicyPostgreSQLRead(d *pluginsdk.ResourceData, if err := d.Set("backup_repeating_time_intervals", flattenBackupPolicyPostgreSQLBackupRuleArray(&props.PolicyRules)); err != nil { return fmt.Errorf("setting `backup_rule`: %+v", err) } - if err := d.Set("default_retention_duration", flattenBackupPolicyPostgreSQLDefaultRetentionRuleDuration(&props.PolicyRules)); err != nil { - return fmt.Errorf("setting `default_retention_duration`: %+v", err) - } - if err := d.Set("retention_rule", flattenBackupPolicyPostgreSQLRetentionRuleArray(&props.PolicyRules)); err != nil { - return fmt.Errorf("setting `retention_rule`: %+v", err) + + if !features.FivePointOh() { + if err := d.Set("default_retention_duration", flattenBackupPolicyPostgreSQLDefaultRetentionRuleDuration(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `default_retention_duration`: %+v", err) + } + if err := d.Set("retention_rule", flattenBackupPolicyPostgreSQLRetentionRuleArray(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `retention_rule`: %+v", err) + } + + if err := d.Set("default_retention_rule", flattenBackupPolicyPostgreSQLDefaultRetentionRule(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `default_retention_rule`: %+v", err) + } + + if err := d.Set("retention_rule", flattenBackupPolicyPostgreSQLRetentionRules(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `retention_rule`: %+v", err) + } + } else { + if err := d.Set("default_retention_rule", flattenBackupPolicyPostgreSQLDefaultRetentionRule(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `default_retention_rule`: %+v", err) + } + if err := d.Set("retention_rule", flattenBackupPolicyPostgreSQLRetentionRules(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `retention_rule`: %+v", err) + } } + d.Set("time_zone", flattenBackupPolicyPostgreSQLBackupTimeZone(&props.PolicyRules)) } } @@ -342,6 +538,21 @@ func expandBackupPolicyPostgreSQLAzureRetentionRuleArray(input []interface{}) [] return results } +func expandBackupPolicyPostgreSQLAzureRetentionRules(input []interface{}) []backuppolicies.BasePolicyRule { + results := make([]backuppolicies.BasePolicyRule, 0) + for _, item := range input { + v := item.(map[string]interface{}) + lifeCycle := expandBackupPolicyPostgreSQLLifeCycle(v["life_cycle"].([]interface{})) + + results = append(results, backuppolicies.AzureRetentionRule{ + Name: v["name"].(string), + IsDefault: pointer.To(false), + Lifecycles: lifeCycle, + }) + } + return results +} + func expandBackupPolicyPostgreSQLDefaultAzureRetentionRule(input interface{}) backuppolicies.BasePolicyRule { return backuppolicies.AzureRetentionRule{ Name: "Default", @@ -361,6 +572,77 @@ func expandBackupPolicyPostgreSQLDefaultAzureRetentionRule(input interface{}) ba } } +func expandBackupPolicyPostgreSQLDefaultRetentionRule(input []interface{}) backuppolicies.BasePolicyRule { + results := backuppolicies.AzureRetentionRule{} + for _, item := range input { + v := item.(map[string]interface{}) + + lifeCycle := expandBackupPolicyPostgreSQLLifeCycle(v["life_cycle"].([]interface{})) + results.Name = "Default" + results.IsDefault = pointer.To(true) + results.Lifecycles = lifeCycle + } + return results +} + +func expandBackupPolicyPostgreSQLLifeCycle(input []interface{}) []backuppolicies.SourceLifeCycle { + results := make([]backuppolicies.SourceLifeCycle, 0) + for _, item := range input { + v := item.(map[string]interface{}) + targetCopySettingList := make([]backuppolicies.TargetCopySetting, 0) + if tcs := v["target_copy"].([]interface{}); len(tcs) > 0 { + tcsv := tcs[0].(map[string]interface{}) + copyAfter, err := expandTargetCopySettingFromJSON(tcsv["option_json"].(string)) + if err != nil { + return results + } + + targetCopySetting := backuppolicies.TargetCopySetting{ + CopyAfter: copyAfter, + DataStore: backuppolicies.DataStoreInfoBase{ + DataStoreType: backuppolicies.DataStoreTypes(tcsv["data_store_type"].(string)), + ObjectType: "DataStoreInfoBase", + }, + } + targetCopySettingList = append(targetCopySettingList, targetCopySetting) + } + + sourceLifeCycle := backuppolicies.SourceLifeCycle{ + DeleteAfter: backuppolicies.AbsoluteDeleteOption{ + Duration: v["duration"].(string), + }, + SourceDataStore: backuppolicies.DataStoreInfoBase{ + DataStoreType: backuppolicies.DataStoreTypes(v["data_store_type"].(string)), + ObjectType: "DataStoreInfoBase", + }, + TargetDataStoreCopySettings: pointer.To(targetCopySettingList), + } + results = append(results, sourceLifeCycle) + } + + return results +} + +func expandTargetCopySettingFromJSON(input string) (backuppolicies.CopyOption, error) { + if input == "" { + return nil, nil + } + targetCopySetting := &backuppolicies.TargetCopySetting{} + err := targetCopySetting.UnmarshalJSON([]byte(fmt.Sprintf(`{ "copyAfter": %s }`, input))) + if err != nil { + return nil, err + } + return targetCopySetting.CopyAfter, nil +} + +func flattenTargetCopySettingFromJSON(input backuppolicies.CopyOption) (string, error) { + if input == nil { + return "", nil + } + result, err := json.Marshal(input) + return string(result), err +} + func expandBackupPolicyPostgreSQLTaggingCriteriaArray(input []interface{}) (*[]backuppolicies.TaggingCriteria, error) { results := []backuppolicies.TaggingCriteria{ { @@ -389,7 +671,6 @@ func expandBackupPolicyPostgreSQLTaggingCriteriaArray(input []interface{}) (*[]b return nil, err } result.Criteria = criteria - results = append(results, result) } return &results, nil @@ -399,6 +680,7 @@ func expandBackupPolicyPostgreSQLCriteriaArray(input []interface{}) (*[]backuppo if len(input) == 0 || input[0] == nil { return nil, fmt.Errorf("criteria is a required field, cannot leave blank") } + results := make([]backuppolicies.BackupCriteria, 0) for _, item := range input { @@ -542,6 +824,75 @@ func flattenBackupPolicyPostgreSQLRetentionRuleArray(input *[]backuppolicies.Bas return results } +func flattenBackupPolicyPostgreSQLRetentionRules(input *[]backuppolicies.BasePolicyRule) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + var taggingCriterias []backuppolicies.TaggingCriteria + for _, item := range *input { + if backupRule, ok := item.(backuppolicies.AzureBackupRule); ok { + if trigger, ok := backupRule.Trigger.(backuppolicies.ScheduleBasedTriggerContext); ok { + if trigger.TaggingCriteria != nil { + taggingCriterias = trigger.TaggingCriteria + } + } + } + } + + for _, item := range *input { + if retentionRule, ok := item.(backuppolicies.AzureRetentionRule); ok { + var name string + var taggingPriority int64 + var taggingCriteria []interface{} + if retentionRule.IsDefault == nil || !*retentionRule.IsDefault { + name = retentionRule.Name + for _, criteria := range taggingCriterias { + if strings.EqualFold(criteria.TagInfo.TagName, name) { + taggingPriority = criteria.TaggingPriority + taggingCriteria = flattenBackupPolicyPostgreSQLBackupCriteriaArray(criteria.Criteria) + } + } + + var lifeCycle []interface{} + if v := retentionRule.Lifecycles; len(v) > 0 { + lifeCycle = flattenBackupPolicyPostgreSQLBackupLifeCycleArray(v) + } + results = append(results, map[string]interface{}{ + "name": name, + "priority": taggingPriority, + "criteria": taggingCriteria, + "life_cycle": lifeCycle, + }) + } + } + } + return results +} + +func flattenBackupPolicyPostgreSQLDefaultRetentionRule(input *[]backuppolicies.BasePolicyRule) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + for _, item := range *input { + if retentionRule, ok := item.(backuppolicies.AzureRetentionRule); ok { + if pointer.From(retentionRule.IsDefault) { + var lifeCycle []interface{} + if v := retentionRule.Lifecycles; len(v) > 0 { + lifeCycle = flattenBackupPolicyPostgreSQLBackupLifeCycleArray(v) + } + results = append(results, map[string]interface{}{ + "life_cycle": lifeCycle, + }) + } + } + } + return results +} + func flattenBackupPolicyPostgreSQLBackupCriteriaArray(input *[]backuppolicies.BackupCriteria) []interface{} { results := make([]interface{}, 0) if input == nil { @@ -592,3 +943,51 @@ func flattenBackupPolicyPostgreSQLBackupCriteriaArray(input *[]backuppolicies.Ba } return results } + +func flattenBackupPolicyPostgreSQLBackupLifeCycleArray(input []backuppolicies.SourceLifeCycle) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + for _, item := range input { + var targetDataStoreCopySetting []interface{} + var duration string + var dataStoreType string + if v := item.TargetDataStoreCopySettings; v != nil && len(*v) > 0 { + targetDataStoreCopySetting = flattenBackupPolicyPostgreSQLBackupTargetDataStoreCopySettingArray(v) + } + if deleteOption, ok := item.DeleteAfter.(backuppolicies.AbsoluteDeleteOption); ok { + duration = deleteOption.Duration + } + dataStoreType = string(item.SourceDataStore.DataStoreType) + + results = append(results, map[string]interface{}{ + "duration": duration, + "target_copy": targetDataStoreCopySetting, + "data_store_type": dataStoreType, + }) + } + return results +} + +func flattenBackupPolicyPostgreSQLBackupTargetDataStoreCopySettingArray(input *[]backuppolicies.TargetCopySetting) []interface{} { + results := make([]interface{}, 0) + if input == nil || len(*input) == 0 { + return results + } + + for _, item := range *input { + copyAfter, err := flattenTargetCopySettingFromJSON(item.CopyAfter) + if err != nil { + return nil + } + dataStoreType := string(item.DataStore.DataStoreType) + + results = append(results, map[string]interface{}{ + "option_json": copyAfter, + "data_store_type": dataStoreType, + }) + } + return results +} diff --git a/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource_test.go b/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource_test.go index e3793089be40..1bd407310ccd 100644 --- a/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource_test.go +++ b/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource_test.go @@ -19,12 +19,12 @@ import ( type DataProtectionBackupPolicyPostgreSQLResource struct{} -func TestAccDataProtectionBackupPolicyPostgreSQL_basic(t *testing.T) { +func TestAccDataProtectionBackupPolicyPostgreSQL_basicFourPointOh(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_postgresql", "test") r := DataProtectionBackupPolicyPostgreSQLResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data), + Config: r.basicFourPointOh(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -33,26 +33,68 @@ func TestAccDataProtectionBackupPolicyPostgreSQL_basic(t *testing.T) { }) } -func TestAccDataProtectionBackupPolicyPostgreSQL_requiresImport(t *testing.T) { +func TestAccDataProtectionBackupPolicyPostgreSQL_basicFivePointOh(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_postgresql", "test") r := DataProtectionBackupPolicyPostgreSQLResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data), + Config: r.basicFivePointOh(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.RequiresImportErrorStep(r.requiresImport), + data.ImportStep(), + }) +} + +func TestAccDataProtectionBackupPolicyPostgreSQL_requiresImportFourPointOh(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_postgresql", "test") + r := DataProtectionBackupPolicyPostgreSQLResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicFourPointOh(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImportFourPointOh), + }) +} + +func TestAccDataProtectionBackupPolicyPostgreSQL_requiresImportFivePointOh(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_postgresql", "test") + r := DataProtectionBackupPolicyPostgreSQLResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicFivePointOh(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImportFivePointOh), + }) +} + +func TestAccDataProtectionBackupPolicyPostgreSQL_completeFourPointOh(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_postgresql", "test") + r := DataProtectionBackupPolicyPostgreSQLResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.completeFourPointOh(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), }) } -func TestAccDataProtectionBackupPolicyPostgreSQL_complete(t *testing.T) { +func TestAccDataProtectionBackupPolicyPostgreSQL_completeFivePointOh(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_postgresql", "test") r := DataProtectionBackupPolicyPostgreSQLResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.complete(data), + Config: r.completeFivePointOh(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -97,7 +139,7 @@ resource "azurerm_data_protection_backup_vault" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } -func (r DataProtectionBackupPolicyPostgreSQLResource) basic(data acceptance.TestData) string { +func (r DataProtectionBackupPolicyPostgreSQLResource) basicFourPointOh(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` %s @@ -113,8 +155,30 @@ resource "azurerm_data_protection_backup_policy_postgresql" "test" { `, template, data.RandomInteger) } -func (r DataProtectionBackupPolicyPostgreSQLResource) requiresImport(data acceptance.TestData) string { - config := r.basic(data) +func (r DataProtectionBackupPolicyPostgreSQLResource) basicFivePointOh(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_data_protection_backup_policy_postgresql" "test" { + name = "acctest-dbp-%d" + resource_group_name = azurerm_resource_group.test.name + vault_name = azurerm_data_protection_backup_vault.test.name + + backup_repeating_time_intervals = ["R/2021-05-23T02:30:00+00:00/P1W"] + + default_retention_rule { + life_cycle { + duration = "P4M" + data_store_type = "VaultStore" + } + } +} +`, template, data.RandomInteger) +} + +func (r DataProtectionBackupPolicyPostgreSQLResource) requiresImportFourPointOh(data acceptance.TestData) string { + config := r.basicFourPointOh(data) return fmt.Sprintf(` %s @@ -129,7 +193,30 @@ resource "azurerm_data_protection_backup_policy_postgresql" "import" { `, config) } -func (r DataProtectionBackupPolicyPostgreSQLResource) complete(data acceptance.TestData) string { +func (r DataProtectionBackupPolicyPostgreSQLResource) requiresImportFivePointOh(data acceptance.TestData) string { + config := r.basicFivePointOh(data) + + return fmt.Sprintf(` +%s + +resource "azurerm_data_protection_backup_policy_postgresql" "import" { + name = azurerm_data_protection_backup_policy_postgresql.test.name + resource_group_name = azurerm_data_protection_backup_policy_postgresql.test.resource_group_name + vault_name = azurerm_data_protection_backup_policy_postgresql.test.vault_name + + backup_repeating_time_intervals = ["R/2021-05-23T02:30:00+00:00/P1W"] + + default_retention_rule { + life_cycle { + duration = "P4M" + data_store_type = "VaultStore" + } + } +} +`, config) +} + +func (r DataProtectionBackupPolicyPostgreSQLResource) completeFourPointOh(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` %s @@ -173,6 +260,65 @@ resource "azurerm_data_protection_backup_policy_postgresql" "test" { scheduled_backup_times = ["2021-05-23T02:30:00Z"] } } + + lifecycle { + ignore_changes = ["default_retention_rule", "retention_rule"] + } +} +`, template, data.RandomInteger) +} + +func (r DataProtectionBackupPolicyPostgreSQLResource) completeFivePointOh(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_data_protection_backup_policy_postgresql" "test" { + name = "acctest-dbp-%d" + resource_group_name = azurerm_resource_group.test.name + vault_name = azurerm_data_protection_backup_vault.test.name + backup_repeating_time_intervals = ["R/2023-12-31T10:00:00+05:30/P1W"] + + retention_rule { + name = "Weekly" + priority = 30 + life_cycle { + duration = "P12W" + data_store_type = "VaultStore" + target_copy { + option_json = jsonencode({ + objectType = "CopyOnExpiryOption" + }) + data_store_type = "ArchiveStore" + } + } + life_cycle { + duration = "P27W" + data_store_type = "ArchiveStore" + } + criteria { + weeks_of_month = ["First", "Last"] + days_of_week = ["Tuesday"] + scheduled_backup_times = ["2021-05-23T02:30:00Z"] + } + } + + default_retention_rule { + life_cycle { + duration = "P12M" + data_store_type = "VaultStore" + target_copy { + option_json = jsonencode({ + objectType = "CopyOnExpiryOption" + }) + data_store_type = "ArchiveStore" + } + } + life_cycle { + duration = "P27M" + data_store_type = "ArchiveStore" + } + } } `, template, data.RandomInteger) } diff --git a/website/docs/5.0-upgrade-guide.html.markdown b/website/docs/5.0-upgrade-guide.html.markdown index ed27618bafc2..17f625ffc0b1 100644 --- a/website/docs/5.0-upgrade-guide.html.markdown +++ b/website/docs/5.0-upgrade-guide.html.markdown @@ -148,6 +148,11 @@ Please follow the format in the example below for listing breaking changes in re * The deprecated `example_old_property` property has been removed in favour of the `example_new_property` property. * The deprecated `example_property_with_no_replacement` property has been removed. * The `example_property_with_changed_default` property now defaults to `NewDefault`. + +### `azurerm_data_protection_backup_policy_postgresql` + +* The deprecated `default_retention_duration` property has been removed in favour of the `default_retention_rule.0.life_cycle.#.duration` property. +* The deprecated `retention_rule.#.duration` property has been removed in favour of the `retention_rule.#.life_cycle.#.duration` property. ``` ### `azurerm_api_management` diff --git a/website/docs/r/data_protection_backup_policy_postgresql.html.markdown b/website/docs/r/data_protection_backup_policy_postgresql.html.markdown index 99c8ff38dfe0..cd754b40f150 100644 --- a/website/docs/r/data_protection_backup_policy_postgresql.html.markdown +++ b/website/docs/r/data_protection_backup_policy_postgresql.html.markdown @@ -79,7 +79,9 @@ The following arguments are supported: * `backup_repeating_time_intervals` - (Required) Specifies a list of repeating time interval. It supports weekly back. It should follow `ISO 8601` repeating time interval. Changing this forces a new Backup Policy PostgreSQL to be created. -* `default_retention_duration` - (Required) The duration of default retention rule. It should follow `ISO 8601` duration format. Changing this forces a new Backup Policy PostgreSQL to be created. +* `default_retention_duration` - (Optional) The duration of default retention rule. It should follow `ISO 8601` duration format. Changing this forces a new Backup Policy PostgreSQL to be created. + +* `default_retention_rule` - (Optional) A `default_retention_rule` blocks as defined below. Changing this forces a new Backup Policy PostgreSQL to be created. * `retention_rule` - (Optional) One or more `retention_rule` blocks as defined below. Changing this forces a new Backup Policy PostgreSQL to be created. @@ -87,16 +89,24 @@ The following arguments are supported: --- +A `default_retention_rule` block supports the following: + +* `life_cycle` - (Optional) One or more `life_cycle` blocks as defined below. Changing this forces a new Backup Policy PostgreSQL to be created. + +--- + A `retention_rule` block supports the following: * `name` - (Required) The name which should be used for this retention rule. Changing this forces a new Backup Policy PostgreSQL to be created. -* `duration` - (Required) Duration after which the backup is deleted. It should follow `ISO 8601` duration format. Changing this forces a new Backup Policy PostgreSQL to be created. - * `criteria` - (Required) A `criteria` block as defined below. Changing this forces a new Backup Policy PostgreSQL to be created. * `priority` - (Required) Specifies the priority of the rule. The priority number must be unique for each rule. The lower the priority number, the higher the priority of the rule. Changing this forces a new Backup Policy PostgreSQL to be created. +* `duration` - (Optional) Duration after which the backup is deleted. It should follow `ISO 8601` duration format. Changing this forces a new Backup Policy PostgreSQL to be created. + +* `life_cycle` - (Optional) One or more `life_cycle` blocks as defined below. Changing this forces a new Backup Policy PostgreSQL to be created. + --- A `criteria` block supports the following: @@ -113,6 +123,24 @@ A `criteria` block supports the following: -> **Note:** When not using `absolute_criteria`, you must use exactly one of `days_of_month` or `days_of_week`. Regarding the remaining two properties, `weeks_of_month` and `months_of_year`, you may use either, both, or neither. If you would like to set multiple intervals, you may do so by using multiple `retention_rule` blocks. +--- + +A `life_cycle` block supports the following: + +* `data_store_type` - (Required) The type of data store. Possible values are `VaultStore`, `ArchiveStore`. Changing this forces a new Backup Policy PostgreSQL to be created. + +* `duration` - (Required) Duration after which the backup is deleted. It should follow `ISO 8601` duration format. Changing this forces a new Backup Policy PostgreSQL to be created. + +* `target_copy` - (Optional) A `target_copy` block as defined below. Changing this forces a new Backup Policy PostgreSQL to be created. + +--- + +A `target_copy` block supports the following: + +* `option_json` - (Required) Specifies when the backups are tiered across two or more selected data stores as a json encoded string. Changing this forces a new Backup Policy PostgreSQL to be created. + +* `data_store_type` - (Required) The type of data store. The only possible value is `ArchiveStore`. Changing this forces a new Backup Policy PostgreSQL to be created. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: