From 596a61e5bfc21cdb7edafcb3a99618c744d652a0 Mon Sep 17 00:00:00 2001 From: Neil Ye Date: Fri, 20 Sep 2024 11:40:18 +0800 Subject: [PATCH] New Resource: `azurerm_data_protection_backup_policy_mysql_flexible_server` (#26955) * New Resource: azurerm_data_protection_backup_policy_mysql_flexible_server * update code * add validate for timezone * add validation for backup_repeating_time_intervals --- helpers/validate/time.go | 37 + helpers/validate/time_test.go | 60 ++ ...p_policy_mysql_flexible_server_resource.go | 697 ++++++++++++++++++ ...icy_mysql_flexible_server_resource_test.go | 201 +++++ .../services/dataprotection/registration.go | 1 + ...ackup_policy_mysql_flexible_server_name.go | 23 + ..._policy_mysql_flexible_server_name_test.go | 61 ++ ..._policy_mysql_flexible_server_time_zone.go | 157 ++++ ...policy_mysql_flexible_server.html.markdown | 166 +++++ 9 files changed, 1403 insertions(+) create mode 100644 internal/services/dataprotection/data_protection_backup_policy_mysql_flexible_server_resource.go create mode 100644 internal/services/dataprotection/data_protection_backup_policy_mysql_flexible_server_resource_test.go create mode 100644 internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_name.go create mode 100644 internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_name_test.go create mode 100644 internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_time_zone.go create mode 100644 website/docs/r/data_protection_backup_policy_mysql_flexible_server.html.markdown diff --git a/helpers/validate/time.go b/helpers/validate/time.go index 1c1b0a3f1c3a..d50f68b1e10f 100644 --- a/helpers/validate/time.go +++ b/helpers/validate/time.go @@ -5,6 +5,7 @@ package validate import ( "fmt" + "strings" "time" iso8601 "github.com/btubbs/datetime" @@ -65,6 +66,42 @@ func ISO8601DateTime(i interface{}, k string) (warnings []string, errors []error return warnings, errors } +func ISO8601RepeatingTime(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return + } + + if !strings.HasPrefix(v, "R/") { + errors = append(errors, fmt.Errorf("%s must start with 'R/'", k)) + return + } + + partsWithoutPrefix := strings.TrimPrefix(v, "R/") + + pIndex := strings.Index(partsWithoutPrefix, "/P") + if pIndex == -1 { + errors = append(errors, fmt.Errorf("%s must end with duration", k)) + return + } + + dateTime := partsWithoutPrefix[:pIndex] + duration := partsWithoutPrefix[pIndex+1:] + + if _, err := iso8601.Parse(dateTime, time.UTC); err != nil { + errors = append(errors, fmt.Errorf("%q has the invalid ISO8601 date format %q: %+v", k, i, err)) + return + } + + if _, err := period.Parse(duration); err != nil { + errors = append(errors, err) + return + } + + return warnings, errors +} + func AzureTimeZoneString() func(interface{}, string) ([]string, []error) { // List collected from https://support.microsoft.com/en-gb/help/973627/microsoft-time-zone-index-values // TODO look into programatic retrieval https://docs.microsoft.com/en-us/rest/api/maps/timezone/gettimezoneenumwindows diff --git a/helpers/validate/time_test.go b/helpers/validate/time_test.go index d275f76bfeb1..f6980acd1877 100644 --- a/helpers/validate/time_test.go +++ b/helpers/validate/time_test.go @@ -106,3 +106,63 @@ func TestISO8601Duration(t *testing.T) { } } } + +func TestISO8601RepeatingTime(t *testing.T) { + cases := []struct { + Value string + Errors int + }{ + { + Value: "R/2021-05-23T02:30:00+00:00/P1W", + Errors: 0, + }, + { + Value: "R/2021-05-23T02:30:00+00:00/PT20S", + Errors: 0, + }, + { + Value: "R/2021-05-23T02:30:00+00:00/PT0.5S", + Errors: 0, + }, + { + Value: "R", + Errors: 1, + }, + { + Value: "R/", + Errors: 1, + }, + { + Value: "2021-05-23T02:30:03+01:02", + Errors: 1, + }, + { + Value: "P1W", + Errors: 1, + }, + { + Value: "R/2021-05-23T02:30:00+00:00", + Errors: 1, + }, + { + Value: "C/2021-05-23T02:30:00+00:00", + Errors: 1, + }, + { + Value: "2021-05-23T02:30:00+00:00/D1W", + Errors: 1, + }, + { + Value: "2021-05-23T02:30:00+00:00/P1W", + Errors: 1, + }, + } + + for _, tc := range cases { + _, errors := ISO8601RepeatingTime(tc.Value, "example") + + if len(errors) != tc.Errors { + t.Fatalf("Expected ISO8601RepeatingTime to trigger '%d' errors for '%s' - got '%d'", tc.Errors, tc.Value, len(errors)) + } + } +} diff --git a/internal/services/dataprotection/data_protection_backup_policy_mysql_flexible_server_resource.go b/internal/services/dataprotection/data_protection_backup_policy_mysql_flexible_server_resource.go new file mode 100644 index 000000000000..c8b79ca2f841 --- /dev/null +++ b/internal/services/dataprotection/data_protection_backup_policy_mysql_flexible_server_resource.go @@ -0,0 +1,697 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataprotection + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2024-04-01/backuppolicies" + azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +type BackupPolicyMySQLFlexibleServerModel struct { + Name string `tfschema:"name"` + VaultId string `tfschema:"vault_id"` + BackupRepeatingTimeIntervals []string `tfschema:"backup_repeating_time_intervals"` + DefaultRetentionRule []BackupPolicyMySQLFlexibleServerDefaultRetentionRule `tfschema:"default_retention_rule"` + RetentionRules []BackupPolicyMySQLFlexibleServerRetentionRule `tfschema:"retention_rule"` + TimeZone string `tfschema:"time_zone"` +} + +type BackupPolicyMySQLFlexibleServerDefaultRetentionRule struct { + LifeCycle []BackupPolicyMySQLFlexibleServerLifeCycle `tfschema:"life_cycle"` +} + +type BackupPolicyMySQLFlexibleServerLifeCycle struct { + DataStoreType string `tfschema:"data_store_type"` + Duration string `tfschema:"duration"` +} + +type BackupPolicyMySQLFlexibleServerRetentionRule struct { + Name string `tfschema:"name"` + Criteria []BackupPolicyMySQLFlexibleServerCriteria `tfschema:"criteria"` + LifeCycle []BackupPolicyMySQLFlexibleServerLifeCycle `tfschema:"life_cycle"` + Priority int64 `tfschema:"priority"` +} + +type BackupPolicyMySQLFlexibleServerCriteria struct { + AbsoluteCriteria string `tfschema:"absolute_criteria"` + DaysOfWeek []string `tfschema:"days_of_week"` + MonthsOfYear []string `tfschema:"months_of_year"` + ScheduledBackupTimes []string `tfschema:"scheduled_backup_times"` + WeeksOfMonth []string `tfschema:"weeks_of_month"` +} + +type DataProtectionBackupPolicyMySQLFlexibleServerResource struct{} + +var _ sdk.Resource = DataProtectionBackupPolicyMySQLFlexibleServerResource{} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) ResourceType() string { + return "azurerm_data_protection_backup_policy_mysql_flexible_server" +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) ModelObject() interface{} { + return &BackupPolicyMySQLFlexibleServerModel{} +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return backuppolicies.ValidateBackupPolicyID +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) Arguments() map[string]*pluginsdk.Schema { + arguments := map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.BackupPolicyMySQLFlexibleServerName, + }, + + "vault_id": commonschema.ResourceIDReferenceRequiredForceNew(pointer.To(backuppolicies.BackupVaultId{})), + + "backup_repeating_time_intervals": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: azValidate.ISO8601RepeatingTime, + }, + }, + + "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 current possible value only support `VaultStore`. + // However, considering that `ArchiveStore` will be supported in the future, it would be exposed for user specification. + string(backuppolicies.DataStoreTypesVaultStore), + }, false), + }, + + "duration": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azValidate.ISO8601Duration, + }, + }, + }, + }, + }, + }, + }, + + "retention_rule": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "criteria": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "absolute_criteria": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(backuppolicies.PossibleValuesForAbsoluteMarker(), false), + }, + + "days_of_week": { + Type: pluginsdk.TypeSet, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice(backuppolicies.PossibleValuesForDayOfWeek(), false), + }, + }, + + "months_of_year": { + Type: pluginsdk.TypeSet, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice(backuppolicies.PossibleValuesForMonth(), false), + }, + }, + + "scheduled_backup_times": { + Type: pluginsdk.TypeSet, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.IsRFC3339Time, + }, + }, + + "weeks_of_month": { + Type: pluginsdk.TypeSet, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice(backuppolicies.PossibleValuesForWeekNumber(), false), + }, + }, + }, + }, + }, + + "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 currently only `VaultStore` is supported. + // However, considering that `ArchiveStore` will be supported in the future, it would be exposed for user specification. + string(backuppolicies.DataStoreTypesVaultStore), + }, false), + }, + + "duration": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azValidate.ISO8601Duration, + }, + }, + }, + }, + + "priority": { + Type: pluginsdk.TypeInt, + Required: true, + ForceNew: true, + }, + }, + }, + }, + + "time_zone": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validate.BackupPolicyMySQLFlexibleServerTimeZone(), + }, + } + return arguments +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model BackupPolicyMySQLFlexibleServerModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.DataProtection.BackupPolicyClient + subscriptionId := metadata.Client.Account.SubscriptionId + + vaultId, _ := backuppolicies.ParseBackupVaultID(model.VaultId) + id := backuppolicies.NewBackupPolicyID(subscriptionId, vaultId.ResourceGroupName, vaultId.BackupVaultName, model.Name) + + existing, err := client.Get(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + policyRules := make([]backuppolicies.BasePolicyRule, 0) + policyRules = append(policyRules, expandBackupPolicyMySQLFlexibleServerAzureBackupRules(model.BackupRepeatingTimeIntervals, model.TimeZone, expandBackupPolicyMySQLFlexibleServerTaggingCriteria(model.RetentionRules))...) + policyRules = append(policyRules, expandBackupPolicyMySQLFlexibleServerDefaultAzureRetentionRule(model.DefaultRetentionRule)) + policyRules = append(policyRules, expandBackupPolicyMySQLFlexibleServerAzureRetentionRules(model.RetentionRules)...) + + parameters := backuppolicies.BaseBackupPolicyResource{ + Properties: backuppolicies.BackupPolicy{ + PolicyRules: policyRules, + DatasourceTypes: []string{"Microsoft.DBforMySQL/flexibleServers"}, + }, + } + + if _, err := client.CreateOrUpdate(ctx, id, parameters); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + + return nil + }, + } +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.DataProtection.BackupPolicyClient + + id, err := backuppolicies.ParseBackupPolicyID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(*id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + vaultId := backuppolicies.NewBackupVaultID(id.SubscriptionId, id.ResourceGroupName, id.BackupVaultName) + state := BackupPolicyMySQLFlexibleServerModel{ + Name: id.BackupPolicyName, + VaultId: vaultId.ID(), + } + + if model := resp.Model; model != nil { + if properties, ok := model.Properties.(backuppolicies.BackupPolicy); ok { + state.DefaultRetentionRule = flattenBackupPolicyMySQLFlexibleServerDefaultRetentionRule(properties.PolicyRules) + state.RetentionRules = flattenBackupPolicyMySQLFlexibleServerRetentionRules(properties.PolicyRules) + state.BackupRepeatingTimeIntervals = flattenBackupPolicyMySQLFlexibleServerBackupRules(properties.PolicyRules) + state.TimeZone = flattenBackupPolicyMySQLFlexibleServerBackupTimeZone(properties.PolicyRules) + } + } + + return metadata.Encode(&state) + }, + } +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.DataProtection.BackupPolicyClient + + id, err := backuppolicies.ParseBackupPolicyID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if _, err := client.Delete(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil + }, + } +} + +func expandBackupPolicyMySQLFlexibleServerAzureBackupRules(input []string, timeZone string, taggingCriteria []backuppolicies.TaggingCriteria) []backuppolicies.BasePolicyRule { + results := make([]backuppolicies.BasePolicyRule, 0) + + results = append(results, backuppolicies.AzureBackupRule{ + Name: "BackupIntervals", + DataStore: backuppolicies.DataStoreInfoBase{ + DataStoreType: backuppolicies.DataStoreTypesVaultStore, + ObjectType: "DataStoreInfoBase", + }, + BackupParameters: backuppolicies.AzureBackupParams{ + BackupType: "Full", + }, + Trigger: backuppolicies.ScheduleBasedTriggerContext{ + Schedule: backuppolicies.BackupSchedule{ + RepeatingTimeIntervals: input, + TimeZone: pointer.To(timeZone), + }, + TaggingCriteria: taggingCriteria, + }, + }) + + return results +} + +func expandBackupPolicyMySQLFlexibleServerAzureRetentionRules(input []BackupPolicyMySQLFlexibleServerRetentionRule) []backuppolicies.BasePolicyRule { + results := make([]backuppolicies.BasePolicyRule, 0) + + for _, item := range input { + results = append(results, backuppolicies.AzureRetentionRule{ + Name: item.Name, + IsDefault: pointer.To(false), + Lifecycles: expandBackupPolicyMySQLFlexibleServerLifeCycle(item.LifeCycle), + }) + } + + return results +} + +func expandBackupPolicyMySQLFlexibleServerDefaultAzureRetentionRule(input []BackupPolicyMySQLFlexibleServerDefaultRetentionRule) backuppolicies.BasePolicyRule { + result := backuppolicies.AzureRetentionRule{ + Name: "Default", + IsDefault: pointer.To(true), + } + + if len(input) > 0 { + result.Lifecycles = expandBackupPolicyMySQLFlexibleServerLifeCycle(input[0].LifeCycle) + } + + return result +} + +func expandBackupPolicyMySQLFlexibleServerLifeCycle(input []BackupPolicyMySQLFlexibleServerLifeCycle) []backuppolicies.SourceLifeCycle { + results := make([]backuppolicies.SourceLifeCycle, 0) + + for _, item := range input { + sourceLifeCycle := backuppolicies.SourceLifeCycle{ + DeleteAfter: backuppolicies.AbsoluteDeleteOption{ + Duration: item.Duration, + }, + SourceDataStore: backuppolicies.DataStoreInfoBase{ + DataStoreType: backuppolicies.DataStoreTypes(item.DataStoreType), + ObjectType: "DataStoreInfoBase", + }, + TargetDataStoreCopySettings: &[]backuppolicies.TargetCopySetting{}, + } + + results = append(results, sourceLifeCycle) + } + + return results +} + +func expandBackupPolicyMySQLFlexibleServerTaggingCriteria(input []BackupPolicyMySQLFlexibleServerRetentionRule) []backuppolicies.TaggingCriteria { + results := []backuppolicies.TaggingCriteria{ + { + Criteria: nil, + IsDefault: true, + TaggingPriority: 99, + TagInfo: backuppolicies.RetentionTag{ + Id: pointer.To("Default_"), + TagName: "Default", + }, + }, + } + + for _, item := range input { + result := backuppolicies.TaggingCriteria{ + IsDefault: false, + Criteria: expandBackupPolicyMySQLFlexibleServerCriteria(item.Criteria), + TaggingPriority: item.Priority, + TagInfo: backuppolicies.RetentionTag{ + Id: pointer.To(item.Name + "_"), + TagName: item.Name, + }, + } + + results = append(results, result) + } + + return results +} + +func expandBackupPolicyMySQLFlexibleServerCriteria(input []BackupPolicyMySQLFlexibleServerCriteria) *[]backuppolicies.BackupCriteria { + if len(input) == 0 { + return nil + } + + results := make([]backuppolicies.BackupCriteria, 0) + + for _, item := range input { + var absoluteCriteria []backuppolicies.AbsoluteMarker + if absoluteCriteriaRaw := item.AbsoluteCriteria; len(absoluteCriteriaRaw) > 0 { + absoluteCriteria = []backuppolicies.AbsoluteMarker{backuppolicies.AbsoluteMarker(absoluteCriteriaRaw)} + } + + var daysOfWeek []backuppolicies.DayOfWeek + if len(item.DaysOfWeek) > 0 { + daysOfWeek = make([]backuppolicies.DayOfWeek, 0) + for _, value := range item.DaysOfWeek { + daysOfWeek = append(daysOfWeek, backuppolicies.DayOfWeek(value)) + } + } + + var monthsOfYear []backuppolicies.Month + if len(item.MonthsOfYear) > 0 { + monthsOfYear = make([]backuppolicies.Month, 0) + for _, value := range item.MonthsOfYear { + monthsOfYear = append(monthsOfYear, backuppolicies.Month(value)) + } + } + + var weeksOfMonth []backuppolicies.WeekNumber + if len(item.WeeksOfMonth) > 0 { + weeksOfMonth = make([]backuppolicies.WeekNumber, 0) + for _, value := range item.WeeksOfMonth { + weeksOfMonth = append(weeksOfMonth, backuppolicies.WeekNumber(value)) + } + } + + var scheduleTimes []string + if len(item.ScheduledBackupTimes) > 0 { + scheduleTimes = item.ScheduledBackupTimes + } + + results = append(results, backuppolicies.ScheduleBasedBackupCriteria{ + AbsoluteCriteria: pointer.To(absoluteCriteria), + DaysOfMonth: nil, + DaysOfTheWeek: pointer.To(daysOfWeek), + MonthsOfYear: pointer.To(monthsOfYear), + ScheduleTimes: pointer.To(scheduleTimes), + WeeksOfTheMonth: pointer.To(weeksOfMonth), + }) + } + + return &results +} + +func flattenBackupPolicyMySQLFlexibleServerBackupRules(input []backuppolicies.BasePolicyRule) []string { + backupRules := make([]string, 0) + + for _, item := range input { + if v, ok := item.(backuppolicies.AzureBackupRule); ok { + if v.Trigger != nil { + if scheduleBasedTrigger, ok := v.Trigger.(backuppolicies.ScheduleBasedTriggerContext); ok { + backupRules = scheduleBasedTrigger.Schedule.RepeatingTimeIntervals + return backupRules + } + } + } + } + + return backupRules +} + +func flattenBackupPolicyMySQLFlexibleServerBackupTimeZone(input []backuppolicies.BasePolicyRule) string { + var timeZone string + + for _, item := range input { + if backupRule, ok := item.(backuppolicies.AzureBackupRule); ok { + if backupRule.Trigger != nil { + if scheduleBasedTrigger, ok := backupRule.Trigger.(backuppolicies.ScheduleBasedTriggerContext); ok { + timeZone = pointer.From(scheduleBasedTrigger.Schedule.TimeZone) + return timeZone + } + } + } + } + + return timeZone +} + +func flattenBackupPolicyMySQLFlexibleServerDefaultRetentionRule(input []backuppolicies.BasePolicyRule) []BackupPolicyMySQLFlexibleServerDefaultRetentionRule { + results := make([]BackupPolicyMySQLFlexibleServerDefaultRetentionRule, 0) + + for _, item := range input { + if retentionRule, ok := item.(backuppolicies.AzureRetentionRule); ok { + if pointer.From(retentionRule.IsDefault) { + var lifeCycle []BackupPolicyMySQLFlexibleServerLifeCycle + if v := retentionRule.Lifecycles; len(v) > 0 { + lifeCycle = flattenBackupPolicyMySQLFlexibleServerLifeCycles(v) + } + + results = append(results, BackupPolicyMySQLFlexibleServerDefaultRetentionRule{ + LifeCycle: lifeCycle, + }) + } + } + } + + return results +} + +func flattenBackupPolicyMySQLFlexibleServerRetentionRules(input []backuppolicies.BasePolicyRule) []BackupPolicyMySQLFlexibleServerRetentionRule { + results := make([]BackupPolicyMySQLFlexibleServerRetentionRule, 0) + 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 []BackupPolicyMySQLFlexibleServerCriteria + + if !pointer.From(retentionRule.IsDefault) { + name = retentionRule.Name + + for _, criteria := range taggingCriterias { + if strings.EqualFold(criteria.TagInfo.TagName, name) { + taggingPriority = criteria.TaggingPriority + taggingCriteria = flattenBackupPolicyMySQLFlexibleServerBackupCriteria(criteria.Criteria) + break + } + } + + var lifeCycle []BackupPolicyMySQLFlexibleServerLifeCycle + if v := retentionRule.Lifecycles; len(v) > 0 { + lifeCycle = flattenBackupPolicyMySQLFlexibleServerLifeCycles(v) + } + + results = append(results, BackupPolicyMySQLFlexibleServerRetentionRule{ + Name: name, + Priority: taggingPriority, + Criteria: taggingCriteria, + LifeCycle: lifeCycle, + }) + } + } + } + + return results +} + +func flattenBackupPolicyMySQLFlexibleServerLifeCycles(input []backuppolicies.SourceLifeCycle) []BackupPolicyMySQLFlexibleServerLifeCycle { + results := make([]BackupPolicyMySQLFlexibleServerLifeCycle, 0) + + for _, item := range input { + var duration string + var dataStoreType string + + if deleteOption, ok := item.DeleteAfter.(backuppolicies.AbsoluteDeleteOption); ok { + duration = deleteOption.Duration + } + + dataStoreType = string(item.SourceDataStore.DataStoreType) + + results = append(results, BackupPolicyMySQLFlexibleServerLifeCycle{ + Duration: duration, + DataStoreType: dataStoreType, + }) + } + + return results +} + +func flattenBackupPolicyMySQLFlexibleServerBackupCriteria(input *[]backuppolicies.BackupCriteria) []BackupPolicyMySQLFlexibleServerCriteria { + results := make([]BackupPolicyMySQLFlexibleServerCriteria, 0) + if input == nil { + return results + } + + for _, item := range pointer.From(input) { + if criteria, ok := item.(backuppolicies.ScheduleBasedBackupCriteria); ok { + var absoluteCriteria string + if criteria.AbsoluteCriteria != nil && len(pointer.From(criteria.AbsoluteCriteria)) > 0 { + absoluteCriteria = string((pointer.From(criteria.AbsoluteCriteria))[0]) + } + + daysOfWeek := make([]string, 0) + if criteria.DaysOfTheWeek != nil { + for _, item := range pointer.From(criteria.DaysOfTheWeek) { + daysOfWeek = append(daysOfWeek, (string)(item)) + } + } + + monthsOfYear := make([]string, 0) + if criteria.MonthsOfYear != nil { + for _, item := range pointer.From(criteria.MonthsOfYear) { + monthsOfYear = append(monthsOfYear, (string)(item)) + } + } + + weeksOfMonth := make([]string, 0) + if criteria.WeeksOfTheMonth != nil { + for _, item := range pointer.From(criteria.WeeksOfTheMonth) { + weeksOfMonth = append(weeksOfMonth, (string)(item)) + } + } + + scheduleTimes := make([]string, 0) + if criteria.ScheduleTimes != nil { + scheduleTimes = append(scheduleTimes, pointer.From(criteria.ScheduleTimes)...) + } + + results = append(results, BackupPolicyMySQLFlexibleServerCriteria{ + AbsoluteCriteria: absoluteCriteria, + DaysOfWeek: daysOfWeek, + MonthsOfYear: monthsOfYear, + WeeksOfMonth: weeksOfMonth, + ScheduledBackupTimes: scheduleTimes, + }) + } + } + + return results +} diff --git a/internal/services/dataprotection/data_protection_backup_policy_mysql_flexible_server_resource_test.go b/internal/services/dataprotection/data_protection_backup_policy_mysql_flexible_server_resource_test.go new file mode 100644 index 000000000000..a8a3be1f2c35 --- /dev/null +++ b/internal/services/dataprotection/data_protection_backup_policy_mysql_flexible_server_resource_test.go @@ -0,0 +1,201 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataprotection_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2024-04-01/backuppolicies" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type DataProtectionBackupPolicyMySQLFlexibleServerResource struct{} + +func TestAccDataProtectionBackupPolicyMySQLFlexibleServer_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_mysql_flexible_server", "test") + r := DataProtectionBackupPolicyMySQLFlexibleServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDataProtectionBackupPolicyMySQLFlexibleServer_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_mysql_flexible_server", "test") + r := DataProtectionBackupPolicyMySQLFlexibleServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccDataProtectionBackupPolicyMySQLFlexibleServer_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_mysql_flexible_server", "test") + r := DataProtectionBackupPolicyMySQLFlexibleServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := backuppolicies.ParseBackupPolicyID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.DataProtection.BackupPolicyClient.Get(ctx, *id) + if err != nil { + return nil, fmt.Errorf("reading %s: %+v", *id, err) + } + return utils.Bool(resp.Model != nil), nil +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctest-dataprotection-%d" + location = "%s" +} + +resource "azurerm_data_protection_backup_vault" "test" { + name = "acctest-dbv-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + datastore_type = "VaultStore" + redundancy = "LocallyRedundant" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_data_protection_backup_policy_mysql_flexible_server" "test" { + name = "acctest-dbp-%d" + vault_id = azurerm_data_protection_backup_vault.test.id + backup_repeating_time_intervals = ["R/2021-05-23T02:30:00+00:00/P1W"] + + default_retention_rule { + life_cycle { + duration = "P4M" + data_store_type = "VaultStore" + } + } +} +`, r.template(data), data.RandomInteger) +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_data_protection_backup_policy_mysql_flexible_server" "import" { + name = azurerm_data_protection_backup_policy_mysql_flexible_server.test.name + vault_id = azurerm_data_protection_backup_policy_mysql_flexible_server.test.vault_id + backup_repeating_time_intervals = ["R/2021-05-23T02:30:00+00:00/P1W"] + + default_retention_rule { + life_cycle { + duration = "P4M" + data_store_type = "VaultStore" + } + } +} +`, r.basic(data)) +} + +func (r DataProtectionBackupPolicyMySQLFlexibleServerResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_data_protection_backup_policy_mysql_flexible_server" "test" { + name = "acctest-dbp-%d" + vault_id = azurerm_data_protection_backup_vault.test.id + backup_repeating_time_intervals = ["R/2021-05-23T02:30:00+00:00/P1W", "R/2021-05-24T03:40:00+00:00/P1W"] + time_zone = "India Standard Time" + + default_retention_rule { + life_cycle { + duration = "P4M" + data_store_type = "VaultStore" + } + } + + retention_rule { + name = "weekly" + priority = 20 + + life_cycle { + duration = "P6M" + data_store_type = "VaultStore" + } + + criteria { + absolute_criteria = "FirstOfWeek" + } + } + + retention_rule { + name = "thursday" + priority = 25 + + life_cycle { + duration = "P1W" + data_store_type = "VaultStore" + } + + criteria { + days_of_week = ["Thursday", "Friday"] + months_of_year = ["November", "December"] + scheduled_backup_times = ["2021-05-23T02:30:00Z"] + } + } + + retention_rule { + name = "monthly" + priority = 30 + + life_cycle { + duration = "P1D" + data_store_type = "VaultStore" + } + + criteria { + weeks_of_month = ["First", "Last"] + days_of_week = ["Tuesday"] + scheduled_backup_times = ["2021-05-23T02:30:00Z", "2021-05-24T03:40:00Z"] + } + } +} +`, r.template(data), data.RandomInteger) +} diff --git a/internal/services/dataprotection/registration.go b/internal/services/dataprotection/registration.go index 1a34a885793d..b2fbd92bb26d 100644 --- a/internal/services/dataprotection/registration.go +++ b/internal/services/dataprotection/registration.go @@ -59,6 +59,7 @@ func (r Registration) DataSources() []sdk.DataSource { func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ DataProtectionBackupPolicyKubernatesClusterResource{}, + DataProtectionBackupPolicyMySQLFlexibleServerResource{}, DataProtectionBackupPolicyPostgreSQLFlexibleServerResource{}, DataProtectionBackupInstanceKubernatesClusterResource{}, DataProtectionBackupInstancePostgreSQLFlexibleServerResource{}, diff --git a/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_name.go b/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_name.go new file mode 100644 index 000000000000..56f7a901e359 --- /dev/null +++ b/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_name.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "fmt" + "regexp" +) + +func BackupPolicyMySQLFlexibleServerName(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if !regexp.MustCompile("^[-a-zA-Z0-9]{3,150}$").MatchString(v) { + errors = append(errors, fmt.Errorf("%q must be 3 - 150 characters long, contain only letters, numbers and hyphens", k)) + } + + return warnings, errors +} diff --git a/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_name_test.go b/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_name_test.go new file mode 100644 index 000000000000..25c1a3fa8c5f --- /dev/null +++ b/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_name_test.go @@ -0,0 +1,61 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "strings" + "testing" +) + +func TestBackupPolicyMySQLFlexibleServerName(t *testing.T) { + testCases := []struct { + Input string + Expected bool + }{ + { + Input: "", + Expected: false, + }, + { + Input: "aaaa", + Expected: true, + }, + { + Input: "a8a", + Expected: true, + }, + { + Input: "a-8.a", + Expected: false, + }, + { + Input: "a-a", + Expected: true, + }, + { + Input: "a.a", + Expected: false, + }, + { + Input: strings.Repeat("s", 149), + Expected: true, + }, + { + Input: strings.Repeat("s", 150), + Expected: true, + }, + { + Input: strings.Repeat("s", 151), + Expected: false, + }, + } + + for _, v := range testCases { + _, errors := BackupPolicyMySQLFlexibleServerName(v.Input, "name") + result := len(errors) == 0 + if result != v.Expected { + t.Fatalf("Expected the result to be %t but got %t (and %d errors)", v.Expected, result, len(errors)) + } + } +} diff --git a/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_time_zone.go b/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_time_zone.go new file mode 100644 index 000000000000..700c5a57302d --- /dev/null +++ b/internal/services/dataprotection/validate/backup_policy_mysql_flexible_server_time_zone.go @@ -0,0 +1,157 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +func BackupPolicyMySQLFlexibleServerTimeZone() pluginsdk.SchemaValidateFunc { + // Output from [System.TimeZoneInfo]::GetSystemTimeZones() + candidates := []string{ + "Afghanistan Standard Time", + "Alaskan Standard Time", + "Aleutian Standard Time", + "Altai Standard Time", + "Arab Standard Time", + "Arabian Standard Time", + "Arabic Standard Time", + "Argentina Standard Time", + "Astrakhan Standard Time", + "Atlantic Standard Time", + "AUS Central Standard Time", + "Aus Central W. Standard Time", + "AUS Eastern Standard Time", + "Azerbaijan Standard Time", + "Azores Standard Time", + "Bahia Standard Time", + "Bangladesh Standard Time", + "Belarus Standard Time", + "Bougainville Standard Time", + "Canada Central Standard Time", + "Cape Verde Standard Time", + "Caucasus Standard Time", + "Cen. Australia Standard Time", + "Central America Standard Time", + "Central Asia Standard Time", + "Central Brazilian Standard Time", + "Central Europe Standard Time", + "Central European Standard Time", + "Central Pacific Standard Time", + "Central Standard Time", + "Central Standard Time (Mexico)", + "Chatham Islands Standard Time", + "China Standard Time", + "Cuba Standard Time", + "Dateline Standard Time", + "E. Africa Standard Time", + "E. Australia Standard Time", + "E. Europe Standard Time", + "E. South America Standard Time", + "Easter Island Standard Time", + "Eastern Standard Time", + "Eastern Standard Time (Mexico)", + "Egypt Standard Time", + "Ekaterinburg Standard Time", + "Fiji Standard Time", + "FLE Standard Time", + "Georgian Standard Time", + "GMT Standard Time", + "Greenland Standard Time", + "Greenwich Standard Time", + "GTB Standard Time", + "Haiti Standard Time", + "Hawaiian Standard Time", + "India Standard Time", + "Iran Standard Time", + "Israel Standard Time", + "Jordan Standard Time", + "Kaliningrad Standard Time", + "Kamchatka Standard Time", + "Korea Standard Time", + "Libya Standard Time", + "Line Islands Standard Time", + "Lord Howe Standard Time", + "Magadan Standard Time", + "Magallanes Standard Time", + "Marquesas Standard Time", + "Mauritius Standard Time", + "Mid-Atlantic Standard Time", + "Middle East Standard Time", + "Montevideo Standard Time", + "Morocco Standard Time", + "Mountain Standard Time", + "Mountain Standard Time (Mexico)", + "Myanmar Standard Time", + "N. Central Asia Standard Time", + "Namibia Standard Time", + "Nepal Standard Time", + "New Zealand Standard Time", + "Newfoundland Standard Time", + "Norfolk Standard Time", + "North Asia East Standard Time", + "North Asia Standard Time", + "North Korea Standard Time", + "Omsk Standard Time", + "Pacific SA Standard Time", + "Pacific Standard Time", + "Pacific Standard Time (Mexico)", + "Pakistan Standard Time", + "Paraguay Standard Time", + "Qyzylorda Standard Time", + "Romance Standard Time", + "Russia Time Zone 10", + "Russia Time Zone 11", + "Russia Time Zone 3", + "Russian Standard Time", + "SA Eastern Standard Time", + "SA Pacific Standard Time", + "SA Western Standard Time", + "Saint Pierre Standard Time", + "Sakhalin Standard Time", + "Samoa Standard Time", + "Sao Tome Standard Time", + "Saratov Standard Time", + "SE Asia Standard Time", + "Singapore Standard Time", + "South Africa Standard Time", + "South Sudan Standard Time", + "Sri Lanka Standard Time", + "Sudan Standard Time", + "Syria Standard Time", + "Taipei Standard Time", + "Tasmania Standard Time", + "Tocantins Standard Time", + "Tokyo Standard Time", + "Tomsk Standard Time", + "Tonga Standard Time", + "Transbaikal Standard Time", + "Turkey Standard Time", + "Turks And Caicos Standard Time", + "Ulaanbaatar Standard Time", + "US Eastern Standard Time", + "US Mountain Standard Time", + "UTC", + "UTC-02", + "UTC-08", + "UTC-09", + "UTC-11", + "UTC+12", + "UTC+13", + "Venezuela Standard Time", + "Vladivostok Standard Time", + "Volgograd Standard Time", + "W. Australia Standard Time", + "W. Central Africa Standard Time", + "W. Europe Standard Time", + "W. Mongolia Standard Time", + "West Asia Standard Time", + "West Bank Standard Time", + "West Pacific Standard Time", + "Yakutsk Standard Time", + "Yukon Standard Time", + } + return validation.StringInSlice(candidates, false) +} diff --git a/website/docs/r/data_protection_backup_policy_mysql_flexible_server.html.markdown b/website/docs/r/data_protection_backup_policy_mysql_flexible_server.html.markdown new file mode 100644 index 000000000000..0defc93b937a --- /dev/null +++ b/website/docs/r/data_protection_backup_policy_mysql_flexible_server.html.markdown @@ -0,0 +1,166 @@ +--- +subcategory: "DataProtection" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_data_protection_backup_policy_mysql_flexible_server" +description: |- + Manages a Backup Policy to back up MySQL Flexible Server. +--- + +# azurerm_data_protection_backup_policy_mysql_flexible_server + +Manages a Backup Policy to back up MySQL Flexible Server. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_data_protection_backup_vault" "example" { + name = "example-backup-vault" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + datastore_type = "VaultStore" + redundancy = "LocallyRedundant" + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_data_protection_backup_policy_mysql_flexible_server" "example" { + name = "example-backup-policy" + vault_id = azurerm_data_protection_backup_vault.example.id + backup_repeating_time_intervals = ["R/2021-05-23T02:30:00+00:00/P1W"] + time_zone = "India Standard Time" + + default_retention_rule { + life_cycle { + duration = "P4M" + data_store_type = "VaultStore" + } + } + + retention_rule { + name = "weekly" + life_cycle { + duration = "P6M" + data_store_type = "VaultStore" + } + priority = 20 + + criteria { + absolute_criteria = "FirstOfWeek" + } + } + + retention_rule { + name = "thursday" + life_cycle { + duration = "P1W" + data_store_type = "VaultStore" + } + priority = 25 + + criteria { + days_of_week = ["Thursday"] + scheduled_backup_times = ["2021-05-23T02:30:00Z"] + } + } + + retention_rule { + name = "monthly" + life_cycle { + duration = "P1D" + data_store_type = "VaultStore" + } + priority = 15 + + criteria { + weeks_of_month = ["First", "Last"] + days_of_week = ["Tuesday"] + scheduled_backup_times = ["2021-05-23T02:30:00Z"] + } + } +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Backup Policy for the MySQL Flexible Server. Changing this forces a new resource to be created. + +* `vault_id` - (Required) The ID of the Backup Vault where the Backup Policy MySQL Flexible Server should exist. Changing this forces a new resource to be created. + +* `backup_repeating_time_intervals` - (Required) Specifies a list of repeating time interval. It supports weekly back. It should follow `ISO 8601` repeating time interval format. Changing this forces a new resource to be created. + +* `default_retention_rule` - (Required) A `default_retention_rule` block as defined below. Changing this forces a new resource to be created. + +* `retention_rule` - (Optional) One or more `retention_rule` blocks as defined below. Changing this forces a new resource to be created. + +* `time_zone` - (Optional) Specifies the Time Zone which should be used by the backup schedule. Changing this forces a new resource to be created. + +--- + +A `default_retention_rule` block supports the following: + +* `life_cycle` - (Required) A `life_cycle` block as defined below. Changing this forces a new resource to be created. + +--- + +A `retention_rule` block supports the following: + +* `name` - (Required) Specifies the name of the retention rule. Changing this forces a new resource to be created. + +* `criteria` - (Required) A `criteria` block as defined below. Changing this forces a new resource to be created. + +* `life_cycle` - (Required) A `life_cycle` block as defined below. Changing this forces a new resource 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 resource to be created. + +--- + +A `life_cycle` block supports the following: + +* `data_store_type` - (Required) The type of data store. The only possible value is `VaultStore`. Changing this forces a new resource to be created. + +* `duration` - (Required) The retention duration up to which the backups are to be retained in the data stores. It should follow `ISO 8601` duration format. Changing this forces a new resource to be created. + +--- + +A `criteria` block supports the following: + +* `absolute_criteria` - (Optional) Possible values are `AllBackup`, `FirstOfDay`, `FirstOfWeek`, `FirstOfMonth` and `FirstOfYear`. These values mean the first successful backup of the day/week/month/year. Changing this forces a new resource to be created. + +* `days_of_week` - (Optional) Possible values are `Monday`, `Tuesday`, `Thursday`, `Friday`, `Saturday` and `Sunday`. Changing this forces a new resource to be created. + +* `months_of_year` - (Optional) Possible values are `January`, `February`, `March`, `April`, `May`, `June`, `July`, `August`, `September`, `October`, `November` and `December`. Changing this forces a new resource to be created. + +* `scheduled_backup_times` - (Optional) Specifies a list of backup times for backup in the `RFC3339` format. Changing this forces a new resource to be created. + +* `weeks_of_month` - (Optional) Possible values are `First`, `Second`, `Third`, `Fourth` and `Last`. Changing this forces a new resource to be created. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Backup Policy MySQL Flexible Server. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Backup Policy MySQL Flexible Server. +* `read` - (Defaults to 5 minutes) Used when retrieving the Backup Policy MySQL Flexible Server. +* `delete` - (Defaults to 30 minutes) Used when deleting the Backup Policy MySQL Flexible Server. + +## Import + +Backup Policy MySQL Flexible Server's can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_data_protection_backup_policy_mysql_flexible_server.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.DataProtection/backupVaults/vault1/backupPolicies/backupPolicy1 +```