From 726b5500c390ce586f793f965f80f35143cf2738 Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Thu, 24 Sep 2020 23:18:13 +0100 Subject: [PATCH] resource/aws_backup_plan: Add support for AdvancedBackupSettings --- aws/resource_aws_backup_plan.go | 71 ++++++++++++++++++++++-- aws/resource_aws_backup_plan_test.go | 62 +++++++++++++++++++++ website/docs/r/backup_plan.html.markdown | 14 +++++ 3 files changed, 142 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_backup_plan.go b/aws/resource_aws_backup_plan.go index fa548c044c8..ddffa8d5f91 100644 --- a/aws/resource_aws_backup_plan.go +++ b/aws/resource_aws_backup_plan.go @@ -107,6 +107,23 @@ func resourceAwsBackupPlan() *schema.Resource { }, Set: backupBackupPlanHash, }, + "advanced_backup_setting": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "backup_options": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "resource_type": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, "arn": { Type: schema.TypeString, Computed: true, @@ -125,8 +142,9 @@ func resourceAwsBackupPlanCreate(d *schema.ResourceData, meta interface{}) error input := &backup.CreateBackupPlanInput{ BackupPlan: &backup.PlanInput{ - BackupPlanName: aws.String(d.Get("name").(string)), - Rules: expandBackupPlanRules(d.Get("rule").(*schema.Set)), + BackupPlanName: aws.String(d.Get("name").(string)), + Rules: expandBackupPlanRules(d.Get("rule").(*schema.Set)), + AdvancedBackupSettings: expandBackupPlanAdvancedBackupSettings(d.Get("advanced_backup_setting").(*schema.Set)), }, BackupPlanTags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().BackupTags(), } @@ -166,6 +184,12 @@ func resourceAwsBackupPlanRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("error setting rule: %s", err) } + // AdvancedBackupSettings being read direct from resp and not from under + // resp.BackupPlan is deliberate - the latter always contains null + if err := d.Set("advanced_backup_setting", flattenBackupPlanAdvancedBackupSettings(resp.AdvancedBackupSettings)); err != nil { + return fmt.Errorf("error setting advanced_backup_setting: %s", err) + } + tags, err := keyvaluetags.BackupListTags(conn, d.Get("arn").(string)) if err != nil { return fmt.Errorf("error listing tags for Backup Plan (%s): %s", d.Id(), err) @@ -180,12 +204,13 @@ func resourceAwsBackupPlanRead(d *schema.ResourceData, meta interface{}) error { func resourceAwsBackupPlanUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).backupconn - if d.HasChange("rule") { + if d.HasChanges("rule", "advanced_backup_setting") { input := &backup.UpdateBackupPlanInput{ BackupPlanId: aws.String(d.Id()), BackupPlan: &backup.PlanInput{ - BackupPlanName: aws.String(d.Get("name").(string)), - Rules: expandBackupPlanRules(d.Get("rule").(*schema.Set)), + BackupPlanName: aws.String(d.Get("name").(string)), + Rules: expandBackupPlanRules(d.Get("rule").(*schema.Set)), + AdvancedBackupSettings: expandBackupPlanAdvancedBackupSettings(d.Get("advanced_backup_setting").(*schema.Set)), }, } @@ -279,6 +304,27 @@ func expandBackupPlanRules(vRules *schema.Set) []*backup.RuleInput { return rules } +func expandBackupPlanAdvancedBackupSettings(vAdvancedBackupSettings *schema.Set) []*backup.AdvancedBackupSetting { + advancedBackupSettings := []*backup.AdvancedBackupSetting{} + + for _, vAdvancedBackupSetting := range vAdvancedBackupSettings.List() { + advancedBackupSetting := &backup.AdvancedBackupSetting{} + + mAdvancedBackupSetting := vAdvancedBackupSetting.(map[string]interface{}) + + if v, ok := mAdvancedBackupSetting["backup_options"].(map[string]interface{}); ok && v != nil { + advancedBackupSetting.BackupOptions = stringMapToPointers(v) + } + if v, ok := mAdvancedBackupSetting["resource_type"].(string); ok && v != "" { + advancedBackupSetting.ResourceType = aws.String(v) + } + + advancedBackupSettings = append(advancedBackupSettings, advancedBackupSetting) + } + + return advancedBackupSettings +} + func expandBackupPlanCopyActions(actionList []interface{}) []*backup.CopyAction { actions := []*backup.CopyAction{} @@ -341,6 +387,21 @@ func flattenBackupPlanRules(rules []*backup.Rule) *schema.Set { return schema.NewSet(backupBackupPlanHash, vRules) } +func flattenBackupPlanAdvancedBackupSettings(advancedBackupSettings []*backup.AdvancedBackupSetting) *schema.Set { + vAdvancedBackupSettings := []interface{}{} + + for _, advancedBackupSetting := range advancedBackupSettings { + mAdvancedBackupSetting := map[string]interface{}{ + "backup_options": aws.StringValueMap(advancedBackupSetting.BackupOptions), + "resource_type": aws.StringValue(advancedBackupSetting.ResourceType), + } + + vAdvancedBackupSettings = append(vAdvancedBackupSettings, mAdvancedBackupSetting) + } + + return schema.NewSet(backupBackupPlanHash, vAdvancedBackupSettings) +} + func flattenBackupPlanCopyActions(copyActions []*backup.CopyAction) []interface{} { if len(copyActions) == 0 { return nil diff --git a/aws/resource_aws_backup_plan_test.go b/aws/resource_aws_backup_plan_test.go index 7af5ff41dc3..d647508ec46 100644 --- a/aws/resource_aws_backup_plan_test.go +++ b/aws/resource_aws_backup_plan_test.go @@ -467,6 +467,38 @@ func TestAccAwsBackupPlan_Rule_CopyAction_CrossRegion(t *testing.T) { }) } +func TestAccAwsBackupPlan_AdvancedBackupSetting(t *testing.T) { + var plan backup.GetBackupPlanOutput + resourceName := "aws_backup_plan.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSBackup(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsBackupPlanDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsBackupPlanConfigAdvancedBackupSetting(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsBackupPlanExists(resourceName, &plan), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "advanced_backup_setting.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "advanced_backup_setting.*", map[string]string{ + "backup_options.%": "1", + "backup_options.WindowsVSS": "enabled", + "resource_type": "EC2", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAwsBackupPlan_disappears(t *testing.T) { var plan backup.GetBackupPlanOutput resourceName := "aws_backup_plan.test" @@ -890,3 +922,33 @@ resource "aws_backup_plan" "test" { } `, rName) } + +func testAccAwsBackupPlanConfigAdvancedBackupSetting(rName string) string { + return fmt.Sprintf(` +resource "aws_backup_vault" "test" { + name = "%[1]s-1" +} + +resource "aws_backup_plan" "test" { + name = %[1]q + + rule { + rule_name = %[1]q + target_vault_name = aws_backup_vault.test.name + schedule = "cron(0 12 * * ? *)" + + lifecycle { + cold_storage_after = 30 + delete_after = 180 + } + } + + advanced_backup_setting { + backup_options = { + WindowsVSS = "enabled" + } + resource_type = "EC2" + } +} +`, rName) +} diff --git a/website/docs/r/backup_plan.html.markdown b/website/docs/r/backup_plan.html.markdown index 420240f9a84..9958c80c4e5 100644 --- a/website/docs/r/backup_plan.html.markdown +++ b/website/docs/r/backup_plan.html.markdown @@ -21,6 +21,13 @@ resource "aws_backup_plan" "example" { target_vault_name = aws_backup_vault.test.name schedule = "cron(0 12 * * ? *)" } + + advanced_backup_setting { + backup_options = { + WindowsVSS = "enabled" + } + resource_type = "EC2" + } } ``` @@ -30,6 +37,7 @@ The following arguments are supported: * `name` - (Required) The display name of a backup plan. * `rule` - (Required) A rule object that specifies a scheduled task that is used to back up a selection of resources. +* `advanced_backup_setting` - (Optional) An object that specifies backup options for each resource type. * `tags` - (Optional) Metadata that you can assign to help organize the plans you create. ### Rule Arguments @@ -56,6 +64,12 @@ For **copy_action** the following attributes are supported: * `lifecycle` - (Optional) The lifecycle defines when a protected resource is copied over to a backup vault and when it expires. Fields documented above. * `destination_vault_arn` - (Required) An Amazon Resource Name (ARN) that uniquely identifies the destination backup vault for the copied backup. +### Advanced Backup Setting Arguments +For `advanced_backup_setting` the following attibutes are supported: + +* `backup_options` - (Optional) Specifies the backup option for a selected resource. This option is only available for Windows VSS backup jobs. Set to `{ WindowsVSS = "enabled" }` to enable Windows VSS backup option and create a VSS Windows backup. +* `resource_type` - (Optional) The type of AWS resource to be backed up. For VSS Windows backups, the only supported resource type is Amazon EC2. Valid values: `EC2`. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: