Skip to content

Commit 99e9212

Browse files
fix: Handles update of mongodbatlas_backup_compliance_policy as a create operation (#2480)
* handle update as a create * add test to make sure no plan changes appear when reapplying config with non default values * add changelog * fix projectId * fix name of resource in test * Update .changelog/2480.txt Co-authored-by: kyuan-mongodb <[email protected]> --------- Co-authored-by: kyuan-mongodb <[email protected]>
1 parent 94371a9 commit 99e9212

File tree

3 files changed

+169
-168
lines changed

3 files changed

+169
-168
lines changed

.changelog/2480.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
resource/mongodbatlas_backup_compliance_policy: Fixes an issue where the update operation modified attributes that were not supposed to be modified"
3+
```

internal/service/backupcompliancepolicy/resource_backup_compliance_policy.go

Lines changed: 85 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -263,85 +263,8 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.
263263
connV2 := meta.(*config.MongoDBClient).AtlasV2
264264
projectID := d.Get("project_id").(string)
265265

266-
dataProtectionSettings := &admin.DataProtectionSettings20231001{
267-
ProjectId: conversion.StringPtr(projectID),
268-
AuthorizedEmail: d.Get("authorized_email").(string),
269-
AuthorizedUserFirstName: d.Get("authorized_user_first_name").(string),
270-
AuthorizedUserLastName: d.Get("authorized_user_last_name").(string),
271-
CopyProtectionEnabled: conversion.Pointer(d.Get("copy_protection_enabled").(bool)),
272-
EncryptionAtRestEnabled: conversion.Pointer(d.Get("encryption_at_rest_enabled").(bool)),
273-
PitEnabled: conversion.Pointer(d.Get("pit_enabled").(bool)),
274-
RestoreWindowDays: conversion.Pointer(cast.ToInt(d.Get("restore_window_days"))),
275-
OnDemandPolicyItem: expandDemandBackupPolicyItem(d),
276-
}
266+
err := updateOrCreateDataProtectionSetting(ctx, d, connV2, projectID)
277267

278-
var backupPoliciesItem []admin.BackupComplianceScheduledPolicyItem
279-
if v, ok := d.GetOk("policy_item_hourly"); ok {
280-
item := v.([]any)
281-
itemObj := item[0].(map[string]any)
282-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
283-
FrequencyType: cloudbackupschedule.Hourly,
284-
RetentionUnit: itemObj["retention_unit"].(string),
285-
FrequencyInterval: itemObj["frequency_interval"].(int),
286-
RetentionValue: itemObj["retention_value"].(int),
287-
})
288-
}
289-
if v, ok := d.GetOk("policy_item_daily"); ok {
290-
item := v.([]any)
291-
itemObj := item[0].(map[string]any)
292-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
293-
FrequencyType: cloudbackupschedule.Daily,
294-
RetentionUnit: itemObj["retention_unit"].(string),
295-
FrequencyInterval: itemObj["frequency_interval"].(int),
296-
RetentionValue: itemObj["retention_value"].(int),
297-
})
298-
}
299-
if v, ok := d.GetOk("policy_item_weekly"); ok {
300-
items := v.([]any)
301-
for _, s := range items {
302-
itemObj := s.(map[string]any)
303-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
304-
FrequencyType: cloudbackupschedule.Weekly,
305-
RetentionUnit: itemObj["retention_unit"].(string),
306-
FrequencyInterval: itemObj["frequency_interval"].(int),
307-
RetentionValue: itemObj["retention_value"].(int),
308-
})
309-
}
310-
}
311-
if v, ok := d.GetOk("policy_item_monthly"); ok {
312-
items := v.([]any)
313-
for _, s := range items {
314-
itemObj := s.(map[string]any)
315-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
316-
FrequencyType: cloudbackupschedule.Monthly,
317-
RetentionUnit: itemObj["retention_unit"].(string),
318-
FrequencyInterval: itemObj["frequency_interval"].(int),
319-
RetentionValue: itemObj["retention_value"].(int),
320-
})
321-
}
322-
}
323-
if v, ok := d.GetOk("policy_item_yearly"); ok {
324-
items := v.([]any)
325-
for _, s := range items {
326-
itemObj := s.(map[string]any)
327-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
328-
FrequencyType: cloudbackupschedule.Yearly,
329-
RetentionUnit: itemObj["retention_unit"].(string),
330-
FrequencyInterval: itemObj["frequency_interval"].(int),
331-
RetentionValue: itemObj["retention_value"].(int),
332-
})
333-
}
334-
}
335-
if len(backupPoliciesItem) > 0 {
336-
dataProtectionSettings.ScheduledPolicyItems = &backupPoliciesItem
337-
}
338-
339-
params := admin.UpdateDataProtectionSettingsApiParams{
340-
GroupId: projectID,
341-
DataProtectionSettings20231001: dataProtectionSettings,
342-
OverwriteBackupPolicies: conversion.Pointer(false),
343-
}
344-
_, _, err := connV2.CloudBackupsApi.UpdateDataProtectionSettingsWithParams(ctx, &params).Execute()
345268
if err != nil {
346269
return diag.FromErr(fmt.Errorf(errorBackupPolicyUpdate, projectID, err))
347270
}
@@ -444,97 +367,8 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.
444367
ids := conversion.DecodeStateID(d.Id())
445368
projectID := ids["project_id"]
446369

447-
dataProtectionSettings := &admin.DataProtectionSettings20231001{
448-
ProjectId: conversion.StringPtr(projectID),
449-
AuthorizedEmail: d.Get("authorized_email").(string),
450-
AuthorizedUserFirstName: d.Get("authorized_user_first_name").(string),
451-
AuthorizedUserLastName: d.Get("authorized_user_last_name").(string),
452-
OnDemandPolicyItem: expandDemandBackupPolicyItem(d),
453-
}
454-
455-
if d.HasChange("copy_protection_enabled") {
456-
dataProtectionSettings.CopyProtectionEnabled = conversion.Pointer(d.Get("copy_protection_enabled").(bool))
457-
}
458-
459-
if d.HasChange("encryption_at_rest_enabled") {
460-
dataProtectionSettings.EncryptionAtRestEnabled = conversion.Pointer(d.Get("encryption_at_rest_enabled").(bool))
461-
}
462-
463-
if d.HasChange("pit_enabled") {
464-
dataProtectionSettings.PitEnabled = conversion.Pointer(d.Get("pit_enabled").(bool))
465-
}
466-
467-
if d.HasChange("restore_window_days") {
468-
dataProtectionSettings.RestoreWindowDays = conversion.Pointer(cast.ToInt(d.Get("restore_window_days")))
469-
}
370+
err := updateOrCreateDataProtectionSetting(ctx, d, connV2, projectID)
470371

471-
var backupPoliciesItem []admin.BackupComplianceScheduledPolicyItem
472-
if v, ok := d.GetOk("policy_item_hourly"); ok {
473-
item := v.([]any)
474-
itemObj := item[0].(map[string]any)
475-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
476-
FrequencyType: cloudbackupschedule.Hourly,
477-
RetentionUnit: itemObj["retention_unit"].(string),
478-
FrequencyInterval: itemObj["frequency_interval"].(int),
479-
RetentionValue: itemObj["retention_value"].(int),
480-
})
481-
}
482-
if v, ok := d.GetOk("policy_item_daily"); ok {
483-
item := v.([]any)
484-
itemObj := item[0].(map[string]any)
485-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
486-
FrequencyType: cloudbackupschedule.Daily,
487-
RetentionUnit: itemObj["retention_unit"].(string),
488-
FrequencyInterval: itemObj["frequency_interval"].(int),
489-
RetentionValue: itemObj["retention_value"].(int),
490-
})
491-
}
492-
if v, ok := d.GetOk("policy_item_weekly"); ok {
493-
items := v.([]any)
494-
for _, s := range items {
495-
itemObj := s.(map[string]any)
496-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
497-
FrequencyType: cloudbackupschedule.Weekly,
498-
RetentionUnit: itemObj["retention_unit"].(string),
499-
FrequencyInterval: itemObj["frequency_interval"].(int),
500-
RetentionValue: itemObj["retention_value"].(int),
501-
})
502-
}
503-
}
504-
if v, ok := d.GetOk("policy_item_monthly"); ok {
505-
items := v.([]any)
506-
for _, s := range items {
507-
itemObj := s.(map[string]any)
508-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
509-
FrequencyType: cloudbackupschedule.Monthly,
510-
RetentionUnit: itemObj["retention_unit"].(string),
511-
FrequencyInterval: itemObj["frequency_interval"].(int),
512-
RetentionValue: itemObj["retention_value"].(int),
513-
})
514-
}
515-
}
516-
if v, ok := d.GetOk("policy_item_yearly"); ok {
517-
items := v.([]any)
518-
for _, s := range items {
519-
itemObj := s.(map[string]any)
520-
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
521-
FrequencyType: cloudbackupschedule.Yearly,
522-
RetentionUnit: itemObj["retention_unit"].(string),
523-
FrequencyInterval: itemObj["frequency_interval"].(int),
524-
RetentionValue: itemObj["retention_value"].(int),
525-
})
526-
}
527-
}
528-
if len(backupPoliciesItem) > 0 {
529-
dataProtectionSettings.ScheduledPolicyItems = &backupPoliciesItem
530-
}
531-
532-
params := admin.UpdateDataProtectionSettingsApiParams{
533-
GroupId: projectID,
534-
DataProtectionSettings20231001: dataProtectionSettings,
535-
OverwriteBackupPolicies: conversion.Pointer(false),
536-
}
537-
_, _, err := connV2.CloudBackupsApi.UpdateDataProtectionSettingsWithParams(ctx, &params).Execute()
538372
if err != nil {
539373
return diag.FromErr(fmt.Errorf(errorBackupPolicyUpdate, projectID, err))
540374
}
@@ -622,3 +456,86 @@ func flattenBackupPolicyItems(items []admin.BackupComplianceScheduledPolicyItem,
622456
}
623457
return policyItems
624458
}
459+
460+
func updateOrCreateDataProtectionSetting(ctx context.Context, d *schema.ResourceData, connV2 *admin.APIClient, projectID string) error {
461+
dataProtectionSettings := &admin.DataProtectionSettings20231001{
462+
ProjectId: conversion.StringPtr(projectID),
463+
AuthorizedEmail: d.Get("authorized_email").(string),
464+
AuthorizedUserFirstName: d.Get("authorized_user_first_name").(string),
465+
AuthorizedUserLastName: d.Get("authorized_user_last_name").(string),
466+
CopyProtectionEnabled: conversion.Pointer(d.Get("copy_protection_enabled").(bool)),
467+
EncryptionAtRestEnabled: conversion.Pointer(d.Get("encryption_at_rest_enabled").(bool)),
468+
PitEnabled: conversion.Pointer(d.Get("pit_enabled").(bool)),
469+
RestoreWindowDays: conversion.Pointer(cast.ToInt(d.Get("restore_window_days"))),
470+
OnDemandPolicyItem: expandDemandBackupPolicyItem(d),
471+
}
472+
473+
var backupPoliciesItem []admin.BackupComplianceScheduledPolicyItem
474+
if v, ok := d.GetOk("policy_item_hourly"); ok {
475+
item := v.([]any)
476+
itemObj := item[0].(map[string]any)
477+
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
478+
FrequencyType: cloudbackupschedule.Hourly,
479+
RetentionUnit: itemObj["retention_unit"].(string),
480+
FrequencyInterval: itemObj["frequency_interval"].(int),
481+
RetentionValue: itemObj["retention_value"].(int),
482+
})
483+
}
484+
if v, ok := d.GetOk("policy_item_daily"); ok {
485+
item := v.([]any)
486+
itemObj := item[0].(map[string]any)
487+
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
488+
FrequencyType: cloudbackupschedule.Daily,
489+
RetentionUnit: itemObj["retention_unit"].(string),
490+
FrequencyInterval: itemObj["frequency_interval"].(int),
491+
RetentionValue: itemObj["retention_value"].(int),
492+
})
493+
}
494+
if v, ok := d.GetOk("policy_item_weekly"); ok {
495+
items := v.([]any)
496+
for _, s := range items {
497+
itemObj := s.(map[string]any)
498+
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
499+
FrequencyType: cloudbackupschedule.Weekly,
500+
RetentionUnit: itemObj["retention_unit"].(string),
501+
FrequencyInterval: itemObj["frequency_interval"].(int),
502+
RetentionValue: itemObj["retention_value"].(int),
503+
})
504+
}
505+
}
506+
if v, ok := d.GetOk("policy_item_monthly"); ok {
507+
items := v.([]any)
508+
for _, s := range items {
509+
itemObj := s.(map[string]any)
510+
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
511+
FrequencyType: cloudbackupschedule.Monthly,
512+
RetentionUnit: itemObj["retention_unit"].(string),
513+
FrequencyInterval: itemObj["frequency_interval"].(int),
514+
RetentionValue: itemObj["retention_value"].(int),
515+
})
516+
}
517+
}
518+
if v, ok := d.GetOk("policy_item_yearly"); ok {
519+
items := v.([]any)
520+
for _, s := range items {
521+
itemObj := s.(map[string]any)
522+
backupPoliciesItem = append(backupPoliciesItem, admin.BackupComplianceScheduledPolicyItem{
523+
FrequencyType: cloudbackupschedule.Yearly,
524+
RetentionUnit: itemObj["retention_unit"].(string),
525+
FrequencyInterval: itemObj["frequency_interval"].(int),
526+
RetentionValue: itemObj["retention_value"].(int),
527+
})
528+
}
529+
}
530+
if len(backupPoliciesItem) > 0 {
531+
dataProtectionSettings.ScheduledPolicyItems = &backupPoliciesItem
532+
}
533+
534+
params := admin.UpdateDataProtectionSettingsApiParams{
535+
GroupId: projectID,
536+
DataProtectionSettings20231001: dataProtectionSettings,
537+
OverwriteBackupPolicies: conversion.Pointer(false),
538+
}
539+
_, _, err := connV2.CloudBackupsApi.UpdateDataProtectionSettingsWithParams(ctx, &params).Execute()
540+
return err
541+
}

internal/service/backupcompliancepolicy/resource_backup_compliance_policy_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
11+
"github.com/hashicorp/terraform-plugin-testing/plancheck"
1112
"github.com/hashicorp/terraform-plugin-testing/terraform"
1213
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion"
1314
"github.com/mongodb/terraform-provider-mongodbatlas/internal/testutil/acc"
@@ -116,6 +117,41 @@ func TestAccBackupCompliancePolicy_withoutRestoreWindowDays(t *testing.T) {
116117
})
117118
}
118119

120+
func TestAccBackupCompliancePolicy_UpdateSetsAllAttributes(t *testing.T) {
121+
var (
122+
orgID = os.Getenv("MONGODB_ATLAS_ORG_ID")
123+
projectName = acc.RandomProjectName() // No ProjectIDExecution to avoid conflicts with backup compliance policy
124+
projectOwnerID = os.Getenv("MONGODB_ATLAS_PROJECT_OWNER_ID")
125+
)
126+
127+
resource.ParallelTest(t, resource.TestCase{
128+
PreCheck: func() { acc.PreCheckBasic(t) },
129+
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
130+
Steps: []resource.TestStep{
131+
{
132+
Config: configBasicWithOptionalAttributesWithNonDefaultValues(projectName, orgID, projectOwnerID),
133+
Check: resource.ComposeAggregateTestCheckFunc(
134+
checkExists(resourceName),
135+
resource.TestCheckResourceAttr(resourceName, "authorized_user_first_name", "First"),
136+
resource.TestCheckResourceAttr(resourceName, "authorized_user_last_name", "Last"),
137+
resource.TestCheckResourceAttr(resourceName, "pit_enabled", "false"),
138+
resource.TestCheckResourceAttr(resourceName, "encryption_at_rest_enabled", "false"),
139+
resource.TestCheckResourceAttr(resourceName, "copy_protection_enabled", "true"),
140+
),
141+
},
142+
{
143+
Config: configBasicWithOptionalAttributesWithNonDefaultValues(projectName, orgID, projectOwnerID),
144+
ConfigPlanChecks: resource.ConfigPlanChecks{
145+
PreApply: []plancheck.PlanCheck{
146+
acc.DebugPlan(),
147+
plancheck.ExpectEmptyPlan(),
148+
},
149+
},
150+
},
151+
},
152+
})
153+
}
154+
119155
func basicTestCase(tb testing.TB, useYearly bool) *resource.TestCase {
120156
tb.Helper()
121157

@@ -419,3 +455,48 @@ func basicChecks() []resource.TestCheckFunc {
419455
checks = append(checks, checkExists(resourceName), checkExists(dataSourceName))
420456
return checks
421457
}
458+
459+
func configBasicWithOptionalAttributesWithNonDefaultValues(projectName, orgID, projectOwnerID string) string {
460+
return acc.ConfigProjectWithSettings(projectName, orgID, projectOwnerID, false) +
461+
`resource "mongodbatlas_backup_compliance_policy" "backup_policy_res" {
462+
project_id = mongodbatlas_project.test.id
463+
authorized_email = "[email protected]"
464+
authorized_user_first_name = "First"
465+
authorized_user_last_name = "Last"
466+
copy_protection_enabled = true
467+
pit_enabled = false
468+
encryption_at_rest_enabled = false
469+
470+
restore_window_days = 7
471+
472+
on_demand_policy_item {
473+
frequency_interval = 0
474+
retention_unit = "days"
475+
retention_value = 3
476+
}
477+
478+
policy_item_hourly {
479+
frequency_interval = 6
480+
retention_unit = "days"
481+
retention_value = 7
482+
}
483+
484+
policy_item_daily {
485+
frequency_interval = 0
486+
retention_unit = "days"
487+
retention_value = 7
488+
}
489+
490+
policy_item_weekly {
491+
frequency_interval = 0
492+
retention_unit = "weeks"
493+
retention_value = 4
494+
}
495+
496+
policy_item_monthly {
497+
frequency_interval = 0
498+
retention_unit = "months"
499+
retention_value = 12
500+
}
501+
}`
502+
}

0 commit comments

Comments
 (0)