diff --git a/internal/services/dataprotection/custompollers/data_protection_backup_vault_poller.go b/internal/services/dataprotection/custompollers/data_protection_backup_vault_poller.go new file mode 100644 index 000000000000..b8b6037ad04c --- /dev/null +++ b/internal/services/dataprotection/custompollers/data_protection_backup_vault_poller.go @@ -0,0 +1,51 @@ +// Copyright IBM Corp. 2014, 2025 +// SPDX-License-Identifier: MPL-2.0 + +package custompollers + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2025-07-01/backupvaultresources" + "github.com/hashicorp/go-azure-sdk/sdk/client/pollers" +) + +var _ pollers.PollerType = &dataProtectionBackupVaultPoller{} + +type dataProtectionBackupVaultPoller struct { + client *backupvaultresources.BackupVaultResourcesClient + id backupvaultresources.BackupVaultId +} + +var ( + pollingSuccess = pollers.PollResult{ + PollInterval: 10 * time.Second, + Status: pollers.PollingStatusSucceeded, + } + pollingInProgress = pollers.PollResult{ + PollInterval: 10 * time.Second, + Status: pollers.PollingStatusInProgress, + } +) + +func NewDataProtectionBackupVaultPoller(client *backupvaultresources.BackupVaultResourcesClient, id backupvaultresources.BackupVaultId) *dataProtectionBackupVaultPoller { + return &dataProtectionBackupVaultPoller{ + client: client, + id: id, + } +} + +func (p dataProtectionBackupVaultPoller) Poll(ctx context.Context) (*pollers.PollResult, error) { + resp, err := p.client.BackupVaultsGet(ctx, p.id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return &pollingSuccess, nil + } + return nil, fmt.Errorf("retrieving %s: %+v", p.id, err) + } + + return &pollingInProgress, nil +} diff --git a/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource.go b/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource.go new file mode 100644 index 000000000000..c35b25894ba5 --- /dev/null +++ b/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource.go @@ -0,0 +1,546 @@ +// Copyright IBM Corp. 2014, 2025 +// SPDX-License-Identifier: MPL-2.0 + +package dataprotection + +import ( + "context" + "fmt" + "regexp" + "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-helpers/resourcemanager/resourceids" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2025-07-01/basebackuppolicyresources" + 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" +) + +//go:generate go run ../../tools/generator-tests resourceidentity -resource-name data_protection_backup_policy_data_lake_storage -service-package-name dataprotection -properties "name" -compare-values "subscription_id:data_protection_backup_vault_id,resource_group_name:data_protection_backup_vault_id,backup_vault_name:data_protection_backup_vault_id" + +type BackupPolicyDataLakeStorageModel struct { + Name string `tfschema:"name"` + DataProtectionBackupVaultId string `tfschema:"data_protection_backup_vault_id"` + BackupSchedule []string `tfschema:"backup_schedule"` + DefaultRetentionDuration string `tfschema:"default_retention_duration"` + RetentionRules []BackupPolicyDataLakeStorageRetentionRule `tfschema:"retention_rule"` + TimeZone string `tfschema:"time_zone"` +} + +type BackupPolicyDataLakeStorageRetentionRule struct { + Name string `tfschema:"name"` + Duration string `tfschema:"duration"` + 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 DataProtectionBackupPolicyDataLakeStorageResource struct{} + +var ( + _ sdk.Resource = DataProtectionBackupPolicyDataLakeStorageResource{} + _ sdk.ResourceWithIdentity = DataProtectionBackupPolicyDataLakeStorageResource{} +) + +func (r DataProtectionBackupPolicyDataLakeStorageResource) Identity() resourceids.ResourceId { + return &basebackuppolicyresources.BackupPolicyId{} +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) ResourceType() string { + return "azurerm_data_protection_backup_policy_data_lake_storage" +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) ModelObject() interface{} { + return &BackupPolicyDataLakeStorageModel{} +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return basebackuppolicyresources.ValidateBackupPolicyID +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^[a-zA-Z][-a-zA-Z0-9]{2,149}$"), + "`name` must be 3 - 150 characters long, contain only letters, numbers and hyphens(-), and cannot start with a number or hyphen.", + ), + }, + + "data_protection_backup_vault_id": commonschema.ResourceIDReferenceRequiredForceNew(pointer.To(basebackuppolicyresources.BackupVaultId{})), + + "backup_schedule": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + MaxItems: 5, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: azValidate.ISO8601RepeatingTime, + }, + }, + + "default_retention_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, + }, + + "duration": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azValidate.ISO8601Duration, + }, + + "absolute_criteria": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(basebackuppolicyresources.PossibleValuesForAbsoluteMarker(), false), + }, + + "days_of_week": { + Type: pluginsdk.TypeSet, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice(basebackuppolicyresources.PossibleValuesForDayOfWeek(), false), + }, + }, + + "months_of_year": { + Type: pluginsdk.TypeSet, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice(basebackuppolicyresources.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(basebackuppolicyresources.PossibleValuesForWeekNumber(), false), + }, + }, + }, + }, + }, + + "time_zone": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validate.BackupPolicyDataLakeStorageTimeZone(), + }, + } +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.DataProtection.BackupPolicyClient + subscriptionId := metadata.Client.Account.SubscriptionId + + var model BackupPolicyDataLakeStorageModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + for _, rule := range model.RetentionRules { + if rule.AbsoluteCriteria == "" && len(rule.DaysOfWeek) == 0 { + return fmt.Errorf("`retention_rule` %q requires at least one of `absolute_criteria` and `days_of_week` to be specified", rule.Name) + } + } + + vaultId, _ := basebackuppolicyresources.ParseBackupVaultID(model.DataProtectionBackupVaultId) + id := basebackuppolicyresources.NewBackupPolicyID(subscriptionId, vaultId.ResourceGroupName, vaultId.BackupVaultName, model.Name) + + existing, err := client.BackupPoliciesGet(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + policyRules := make([]basebackuppolicyresources.BasePolicyRule, 0) + policyRules = append(policyRules, expandBackupPolicyDataLakeStorageAzureRetentionRules(model.RetentionRules)...) + policyRules = append(policyRules, expandBackupPolicyDataLakeStorageDefaultAzureRetentionRule(model.DefaultRetentionDuration)) + policyRules = append(policyRules, expandBackupPolicyDataLakeStorageAzureBackupRules(model.BackupSchedule, model.TimeZone, expandBackupPolicyDataLakeStorageTaggingCriteria(model.RetentionRules))...) + + parameters := basebackuppolicyresources.BaseBackupPolicyResource{ + Properties: &basebackuppolicyresources.BackupPolicy{ + ObjectType: "BackupPolicy", + PolicyRules: policyRules, + DatasourceTypes: []string{"Microsoft.Storage/storageAccounts/adlsBlobServices"}, + }, + } + + if _, err := client.BackupPoliciesCreateOrUpdate(ctx, id, parameters); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, &id); err != nil { + return err + } + + return nil + }, + } +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) 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 := basebackuppolicyresources.ParseBackupPolicyID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.BackupPoliciesGet(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(*id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + vaultId := basebackuppolicyresources.NewBackupVaultID(id.SubscriptionId, id.ResourceGroupName, id.BackupVaultName) + state := BackupPolicyDataLakeStorageModel{ + Name: id.BackupPolicyName, + DataProtectionBackupVaultId: vaultId.ID(), + } + + if model := resp.Model; model != nil { + if properties, ok := model.Properties.(basebackuppolicyresources.BackupPolicy); ok { + state.DefaultRetentionDuration, state.RetentionRules, state.BackupSchedule, state.TimeZone = flattenBackupPolicyDataLakeStoragePolicyRules(properties.PolicyRules) + } + } + + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, id); err != nil { + return err + } + + return metadata.Encode(&state) + }, + } +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) 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 := basebackuppolicyresources.ParseBackupPolicyID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if _, err := client.BackupPoliciesDelete(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil + }, + } +} + +func expandBackupPolicyDataLakeStorageAzureBackupRules(input []string, timeZone string, taggingCriteria []basebackuppolicyresources.TaggingCriteria) []basebackuppolicyresources.BasePolicyRule { + results := make([]basebackuppolicyresources.BasePolicyRule, 0) + + results = append(results, basebackuppolicyresources.AzureBackupRule{ + Name: "BackupRule", + DataStore: basebackuppolicyresources.DataStoreInfoBase{ + DataStoreType: basebackuppolicyresources.DataStoreTypesVaultStore, + ObjectType: "DataStoreInfoBase", + }, + BackupParameters: basebackuppolicyresources.AzureBackupParams{ + BackupType: "Discrete", + }, + Trigger: basebackuppolicyresources.ScheduleBasedTriggerContext{ + Schedule: basebackuppolicyresources.BackupSchedule{ + RepeatingTimeIntervals: input, + TimeZone: pointer.To(timeZone), + }, + TaggingCriteria: taggingCriteria, + }, + }) + + return results +} + +func expandBackupPolicyDataLakeStorageAzureRetentionRules(input []BackupPolicyDataLakeStorageRetentionRule) []basebackuppolicyresources.BasePolicyRule { + results := make([]basebackuppolicyresources.BasePolicyRule, 0) + + for _, item := range input { + results = append(results, basebackuppolicyresources.AzureRetentionRule{ + Name: item.Name, + IsDefault: pointer.To(false), + Lifecycles: expandBackupPolicyDataLakeStorageLifeCycle(item.Duration), + }) + } + + return results +} + +func expandBackupPolicyDataLakeStorageDefaultAzureRetentionRule(duration string) basebackuppolicyresources.BasePolicyRule { + return basebackuppolicyresources.AzureRetentionRule{ + Name: "Default", + IsDefault: pointer.To(true), + Lifecycles: expandBackupPolicyDataLakeStorageLifeCycle(duration), + } +} + +func expandBackupPolicyDataLakeStorageLifeCycle(duration string) []basebackuppolicyresources.SourceLifeCycle { + // NOTE: currently only `VaultStore` is supported by the service team. When `ArchiveStore` is supported + // in the future, export `data_store_type` as a schema field and use `VaultStore` as the default value. + return []basebackuppolicyresources.SourceLifeCycle{ + { + DeleteAfter: basebackuppolicyresources.AbsoluteDeleteOption{ + Duration: duration, + }, + SourceDataStore: basebackuppolicyresources.DataStoreInfoBase{ + DataStoreType: basebackuppolicyresources.DataStoreTypesVaultStore, + ObjectType: "DataStoreInfoBase", + }, + TargetDataStoreCopySettings: &[]basebackuppolicyresources.TargetCopySetting{}, + }, + } +} + +func expandBackupPolicyDataLakeStorageTaggingCriteria(input []BackupPolicyDataLakeStorageRetentionRule) []basebackuppolicyresources.TaggingCriteria { + results := []basebackuppolicyresources.TaggingCriteria{ + { + IsDefault: true, + TaggingPriority: 99, + TagInfo: basebackuppolicyresources.RetentionTag{ + Id: pointer.To("Default_"), + TagName: "Default", + }, + }, + } + + for i, item := range input { + result := basebackuppolicyresources.TaggingCriteria{ + IsDefault: false, + Criteria: expandBackupPolicyDataLakeStorageRetentionRuleCriteria(item), + TaggingPriority: int64(i + 1), + TagInfo: basebackuppolicyresources.RetentionTag{ + Id: pointer.To(item.Name + "_"), + TagName: item.Name, + }, + } + + results = append(results, result) + } + + return results +} + +func expandBackupPolicyDataLakeStorageRetentionRuleCriteria(input BackupPolicyDataLakeStorageRetentionRule) *[]basebackuppolicyresources.BackupCriteria { + var absoluteCriteria []basebackuppolicyresources.AbsoluteMarker + if len(input.AbsoluteCriteria) > 0 { + absoluteCriteria = []basebackuppolicyresources.AbsoluteMarker{basebackuppolicyresources.AbsoluteMarker(input.AbsoluteCriteria)} + } + + var daysOfWeek []basebackuppolicyresources.DayOfWeek + if len(input.DaysOfWeek) > 0 { + daysOfWeek = make([]basebackuppolicyresources.DayOfWeek, 0) + for _, value := range input.DaysOfWeek { + daysOfWeek = append(daysOfWeek, basebackuppolicyresources.DayOfWeek(value)) + } + } + + var monthsOfYear []basebackuppolicyresources.Month + if len(input.MonthsOfYear) > 0 { + monthsOfYear = make([]basebackuppolicyresources.Month, 0) + for _, value := range input.MonthsOfYear { + monthsOfYear = append(monthsOfYear, basebackuppolicyresources.Month(value)) + } + } + + var weeksOfMonth []basebackuppolicyresources.WeekNumber + if len(input.WeeksOfMonth) > 0 { + weeksOfMonth = make([]basebackuppolicyresources.WeekNumber, 0) + for _, value := range input.WeeksOfMonth { + weeksOfMonth = append(weeksOfMonth, basebackuppolicyresources.WeekNumber(value)) + } + } + + var scheduleTimes []string + if len(input.ScheduledBackupTimes) > 0 { + scheduleTimes = input.ScheduledBackupTimes + } + + if len(absoluteCriteria) == 0 && len(daysOfWeek) == 0 && len(monthsOfYear) == 0 && len(weeksOfMonth) == 0 && len(scheduleTimes) == 0 { + return nil + } + + return &[]basebackuppolicyresources.BackupCriteria{ + basebackuppolicyresources.ScheduleBasedBackupCriteria{ + AbsoluteCriteria: pointer.To(absoluteCriteria), + DaysOfTheWeek: pointer.To(daysOfWeek), + MonthsOfYear: pointer.To(monthsOfYear), + ScheduleTimes: pointer.To(scheduleTimes), + WeeksOfTheMonth: pointer.To(weeksOfMonth), + }, + } +} + +func flattenBackupPolicyDataLakeStoragePolicyRules(input []basebackuppolicyresources.BasePolicyRule) (string, []BackupPolicyDataLakeStorageRetentionRule, []string, string) { + var taggingCriteria []basebackuppolicyresources.TaggingCriteria + var nonDefaultRetentionRules []basebackuppolicyresources.AzureRetentionRule + var backupSchedule []string + var timeZone string + var defaultRetentionDuration string + retentionRules := make([]BackupPolicyDataLakeStorageRetentionRule, 0) + + for _, item := range input { + switch rule := item.(type) { + case basebackuppolicyresources.AzureBackupRule: + if trigger, ok := rule.Trigger.(basebackuppolicyresources.ScheduleBasedTriggerContext); ok { + backupSchedule = trigger.Schedule.RepeatingTimeIntervals + timeZone = pointer.From(trigger.Schedule.TimeZone) + taggingCriteria = trigger.TaggingCriteria + } + case basebackuppolicyresources.AzureRetentionRule: + if pointer.From(rule.IsDefault) { + if v := rule.Lifecycles; len(v) > 0 { + if deleteOption, ok := v[0].DeleteAfter.(basebackuppolicyresources.AbsoluteDeleteOption); ok { + defaultRetentionDuration = deleteOption.Duration + } + } + } else { + nonDefaultRetentionRules = append(nonDefaultRetentionRules, rule) + } + } + } + + for _, rule := range nonDefaultRetentionRules { + result := BackupPolicyDataLakeStorageRetentionRule{ + Name: rule.Name, + } + + for _, criteria := range taggingCriteria { + if strings.EqualFold(criteria.TagInfo.TagName, rule.Name) { + flattenBackupPolicyDataLakeStorageCriteriaIntoRule(criteria.Criteria, &result) + break + } + } + + if v := rule.Lifecycles; len(v) > 0 { + if deleteOption, ok := v[0].DeleteAfter.(basebackuppolicyresources.AbsoluteDeleteOption); ok { + result.Duration = deleteOption.Duration + } + } + + retentionRules = append(retentionRules, result) + } + + return defaultRetentionDuration, retentionRules, backupSchedule, timeZone +} + +func flattenBackupPolicyDataLakeStorageCriteriaIntoRule(input *[]basebackuppolicyresources.BackupCriteria, rule *BackupPolicyDataLakeStorageRetentionRule) { + if input == nil { + return + } + + for _, item := range pointer.From(input) { + if criteria, ok := item.(basebackuppolicyresources.ScheduleBasedBackupCriteria); ok { + if criteria.AbsoluteCriteria != nil && len(pointer.From(criteria.AbsoluteCriteria)) > 0 { + rule.AbsoluteCriteria = string((pointer.From(criteria.AbsoluteCriteria))[0]) + } + + if criteria.DaysOfTheWeek != nil { + daysOfWeek := make([]string, 0) + for _, item := range pointer.From(criteria.DaysOfTheWeek) { + daysOfWeek = append(daysOfWeek, (string)(item)) + } + rule.DaysOfWeek = daysOfWeek + } + + if criteria.MonthsOfYear != nil { + monthsOfYear := make([]string, 0) + for _, item := range pointer.From(criteria.MonthsOfYear) { + monthsOfYear = append(monthsOfYear, (string)(item)) + } + rule.MonthsOfYear = monthsOfYear + } + + if criteria.WeeksOfTheMonth != nil { + weeksOfMonth := make([]string, 0) + for _, item := range pointer.From(criteria.WeeksOfTheMonth) { + weeksOfMonth = append(weeksOfMonth, (string)(item)) + } + rule.WeeksOfMonth = weeksOfMonth + } + + if criteria.ScheduleTimes != nil { + scheduleTimes := make([]string, 0) + scheduleTimes = append(scheduleTimes, pointer.From(criteria.ScheduleTimes)...) + rule.ScheduledBackupTimes = scheduleTimes + } + } + } +} diff --git a/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource_identity_gen_test.go b/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource_identity_gen_test.go new file mode 100644 index 000000000000..a9900966fa12 --- /dev/null +++ b/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource_identity_gen_test.go @@ -0,0 +1,40 @@ +// Copyright IBM Corp. 2014, 2025 +// SPDX-License-Identifier: MPL-2.0 + +package dataprotection_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + customstatecheck "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/statecheck" +) + +func TestAccDataProtectionBackupPolicyDataLakeStorage_resourceIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_data_lake_storage", "test") + r := DataProtectionBackupPolicyDataLakeStorageResource{} + + checkedFields := map[string]struct{}{ + "name": {}, + "backup_vault_name": {}, + "resource_group_name": {}, + "subscription_id": {}, + } + + data.ResourceIdentityTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + ConfigStateChecks: []statecheck.StateCheck{ + customstatecheck.ExpectAllIdentityFieldsAreChecked("azurerm_data_protection_backup_policy_data_lake_storage.test", checkedFields), + statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_data_protection_backup_policy_data_lake_storage.test", tfjsonpath.New("name"), tfjsonpath.New("name")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_data_protection_backup_policy_data_lake_storage.test", tfjsonpath.New("backup_vault_name"), tfjsonpath.New("data_protection_backup_vault_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_data_protection_backup_policy_data_lake_storage.test", tfjsonpath.New("resource_group_name"), tfjsonpath.New("data_protection_backup_vault_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_data_protection_backup_policy_data_lake_storage.test", tfjsonpath.New("subscription_id"), tfjsonpath.New("data_protection_backup_vault_id")), + }, + }, + data.ImportBlockWithResourceIdentityStep(false), + data.ImportBlockWithIDStep(false), + }, false) +} diff --git a/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource_test.go b/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource_test.go new file mode 100644 index 000000000000..7bc4843dbf23 --- /dev/null +++ b/internal/services/dataprotection/data_protection_backup_policy_data_lake_storage_resource_test.go @@ -0,0 +1,165 @@ +// Copyright IBM Corp. 2014, 2025 +// SPDX-License-Identifier: MPL-2.0 + +package dataprotection_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2025-07-01/basebackuppolicyresources" + "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" +) + +type DataProtectionBackupPolicyDataLakeStorageResource struct{} + +func TestAccDataProtectionBackupPolicyDataLakeStorage_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_data_lake_storage", "test") + r := DataProtectionBackupPolicyDataLakeStorageResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDataProtectionBackupPolicyDataLakeStorage_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_data_lake_storage", "test") + r := DataProtectionBackupPolicyDataLakeStorageResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccDataProtectionBackupPolicyDataLakeStorage_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_policy_data_lake_storage", "test") + r := DataProtectionBackupPolicyDataLakeStorageResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := basebackuppolicyresources.ParseBackupPolicyID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.DataProtection.BackupPolicyClient.BackupPoliciesGet(ctx, *id) + if err != nil { + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) + } + return pointer.To(resp.Model != nil), nil +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +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 DataProtectionBackupPolicyDataLakeStorageResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_data_protection_backup_policy_data_lake_storage" "test" { + name = "acctest-dbp-%d" + data_protection_backup_vault_id = azurerm_data_protection_backup_vault.test.id + backup_schedule = ["R/2021-05-23T02:30:00+00:00/P1W"] + + default_retention_duration = "P4M" +} +`, r.template(data), data.RandomInteger) +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_data_protection_backup_policy_data_lake_storage" "import" { + name = azurerm_data_protection_backup_policy_data_lake_storage.test.name + data_protection_backup_vault_id = azurerm_data_protection_backup_policy_data_lake_storage.test.data_protection_backup_vault_id + backup_schedule = azurerm_data_protection_backup_policy_data_lake_storage.test.backup_schedule + default_retention_duration = azurerm_data_protection_backup_policy_data_lake_storage.test.default_retention_duration +} +`, r.basic(data)) +} + +func (r DataProtectionBackupPolicyDataLakeStorageResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_data_protection_backup_policy_data_lake_storage" "test" { + name = "acctest-dbp-%d" + data_protection_backup_vault_id = azurerm_data_protection_backup_vault.test.id + backup_schedule = ["R/2021-05-23T02:30:00+00:00/P1W", "R/2021-05-24T03:40:00+00:00/P1W"] + time_zone = "Coordinated Universal Time" + + default_retention_duration = "P4M" + + retention_rule { + name = "weekly" + duration = "P6M" + absolute_criteria = "FirstOfWeek" + } + + retention_rule { + name = "thursday" + duration = "P1W" + days_of_week = ["Thursday", "Friday"] + months_of_year = ["November", "December"] + scheduled_backup_times = ["2021-05-23T02:30:00Z"] + } + + retention_rule { + name = "monthly" + duration = "P1D" + 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/data_protection_backup_vault_resource.go b/internal/services/dataprotection/data_protection_backup_vault_resource.go index f870e04f608b..1b9469d3a1b8 100644 --- a/internal/services/dataprotection/data_protection_backup_vault_resource.go +++ b/internal/services/dataprotection/data_protection_backup_vault_resource.go @@ -16,9 +16,11 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2025-07-01/backupvaultresources" + "github.com/hashicorp/go-azure-sdk/sdk/client/pollers" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/custompollers" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -302,6 +304,14 @@ func resourceDataProtectionBackupVaultDelete(d *pluginsdk.ResourceData, meta int if err := client.BackupVaultsDeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting DataProtection BackupVault (%q): %+v", id, err) } + + // API has bug, which appears API returns before the resource is fully deleted. Tracked by this issue: https://github.com/Azure/azure-rest-api-specs/issues/38944 + pollerType := custompollers.NewDataProtectionBackupVaultPoller(client, *id) + poller := pollers.NewPoller(pollerType, 30*time.Second, pollers.DefaultNumberOfDroppedConnectionsToAllow) + if err := poller.PollUntilDone(ctx); err != nil { + return err + } + return nil } diff --git a/internal/services/dataprotection/registration.go b/internal/services/dataprotection/registration.go index 7811d8015ca0..817235d950ef 100644 --- a/internal/services/dataprotection/registration.go +++ b/internal/services/dataprotection/registration.go @@ -91,12 +91,13 @@ func (r Registration) DataSources() []sdk.DataSource { // Resources returns a list of Resources supported by this Service func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ - DataProtectionBackupPolicyKubernatesClusterResource{}, - DataProtectionBackupPolicyMySQLFlexibleServerResource{}, - DataProtectionBackupPolicyPostgreSQLFlexibleServerResource{}, DataProtectionBackupInstanceKubernatesClusterResource{}, DataProtectionBackupInstanceMySQLFlexibleServerResource{}, DataProtectionBackupInstancePostgreSQLFlexibleServerResource{}, + DataProtectionBackupPolicyDataLakeStorageResource{}, + DataProtectionBackupPolicyKubernatesClusterResource{}, + DataProtectionBackupPolicyMySQLFlexibleServerResource{}, + DataProtectionBackupPolicyPostgreSQLFlexibleServerResource{}, DataProtectionBackupVaultCustomerManagedKeyResource{}, } } diff --git a/internal/services/dataprotection/validate/backup_policy_data_lake_storage_time_zone.go b/internal/services/dataprotection/validate/backup_policy_data_lake_storage_time_zone.go new file mode 100644 index 000000000000..2bece57cd860 --- /dev/null +++ b/internal/services/dataprotection/validate/backup_policy_data_lake_storage_time_zone.go @@ -0,0 +1,158 @@ +// Copyright IBM Corp. 2014, 2025 +// 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 BackupPolicyDataLakeStorageTimeZone() 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", + "Coordinated Universal 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_data_lake_storage.html.markdown b/website/docs/r/data_protection_backup_policy_data_lake_storage.html.markdown new file mode 100644 index 000000000000..cd21d3f9a030 --- /dev/null +++ b/website/docs/r/data_protection_backup_policy_data_lake_storage.html.markdown @@ -0,0 +1,126 @@ +--- +subcategory: "DataProtection" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_data_protection_backup_policy_data_lake_storage" +description: |- + Manages a Backup Policy to Azure Data Lake Storage. +--- + +# azurerm_data_protection_backup_policy_data_lake_storage + +Manages a Backup Policy to Azure Data Lake Storage. + +## 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_data_lake_storage" "example" { + name = "example-backup-policy" + data_protection_backup_vault_id = azurerm_data_protection_backup_vault.example.id + backup_schedule = ["R/2021-05-23T02:30:00+00:00/P1W"] + time_zone = "India Standard Time" + + default_retention_duration = "P4M" + + retention_rule { + name = "weekly" + duration = "P6M" + absolute_criteria = "FirstOfWeek" + } + + retention_rule { + name = "thursday" + duration = "P1W" + days_of_week = ["Thursday"] + scheduled_backup_times = ["2021-05-23T02:30:00Z"] + } + + retention_rule { + name = "monthly" + duration = "P1D" + 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 Azure Backup Policy Data Lake Storage. Changing this forces a new resource to be created. + +* `data_protection_backup_vault_id` - (Required) The ID of the Backup Vault where the Azure Backup Policy Data Lake Storage should exist. Changing this forces a new resource to be created. + +* `backup_schedule` - (Required) Specifies a list of repeating time interval, also known as the backup schedule. It supports daily & weekly backup. It should follow [`ISO 8601` recurring time interval format](https://en.wikipedia.org/wiki/ISO_8601#Recurring_intervals), for example: `R/2021-05-23T02:30:00+00:00/P1W`. Changing this forces a new resource to be created. + +* `default_retention_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. + +* `retention_rule` - (Optional) One or more `retention_rule` blocks as defined below. The priority of each rule is determined by its order in the list, where the first rule has the highest priority. 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. Possible values are `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`,`Coordinated Universal 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` and `Yukon Standard Time`. + +--- + +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. + +* `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. + +* `absolute_criteria` - (Optional) Specifies the absolute criteria for the retention rule. Possible values include `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) Specifies a list of days of the week on which the retention rule applies. Possible values include `Monday`, `Tuesday`, `Wednesday`, `Thursday`, `Friday`, `Saturday`, and `Sunday`. Changing this forces a new resource to be created. + +* `weeks_of_month` - (Optional) Specifies a list of weeks of the month on which the retention rule applies. Possible values include `First`, `Second`, `Third`, `Fourth`, and `Last`. Changing this forces a new resource to be created. + +* `months_of_year` - (Optional) Specifies a list of months of the year on which the retention rule applies. Possible values include `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. + +~> **Note:** At least one of `absolute_criteria` or `days_of_week` must be specified. `weeks_of_month` and `months_of_year` are optional and can be supplied together. Multiple intervals may be set using multiple `retention_rule` blocks. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Azure Backup Policy Data Lake Storage. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://developer.hashicorp.com/terraform/language/resources/configure#define-operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Azure Backup Policy Data Lake Storage. +* `read` - (Defaults to 5 minutes) Used when retrieving the Azure Backup Policy Data Lake Storage. +* `delete` - (Defaults to 30 minutes) Used when deleting the Azure Backup Policy Data Lake Storage. + +## Import + +Azure Backup Policy Data Lake Storages can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_data_protection_backup_policy_data_lake_storage.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.DataProtection/backupVaults/vault1/backupPolicies/backupPolicy1 +``` + +## API Providers + +This resource uses the following Azure API Providers: + +* `Microsoft.DataProtection` - 2025-07-01