From 859d751a335587973b15864576067f95397b07b9 Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Tue, 29 Jan 2019 22:57:40 -0600 Subject: [PATCH 1/9] r/aws_backup_selection: Adding resource to manage selections for AWS Backup plans --- aws/provider.go | 1 + aws/resource_aws_backup_selection.go | 186 ++++++++++++++++++ aws/resource_aws_backup_selection_test.go | 100 ++++++++++ website/aws.erb | 3 + website/docs/r/backup_selection.html.markdown | 52 +++++ 5 files changed, 342 insertions(+) create mode 100644 aws/resource_aws_backup_selection.go create mode 100644 aws/resource_aws_backup_selection_test.go create mode 100644 website/docs/r/backup_selection.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 4dd6e810176..f47dc5b9e8b 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -338,6 +338,7 @@ func Provider() terraform.ResourceProvider { "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), "aws_autoscaling_schedule": resourceAwsAutoscalingSchedule(), "aws_backup_plan": resourceAwsBackupPlan(), + "aws_backup_selection": resourceAwsBackupSelection(), "aws_backup_vault": resourceAwsBackupVault(), "aws_budgets_budget": resourceAwsBudgetsBudget(), "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), diff --git a/aws/resource_aws_backup_selection.go b/aws/resource_aws_backup_selection.go new file mode 100644 index 00000000000..56a338fedac --- /dev/null +++ b/aws/resource_aws_backup_selection.go @@ -0,0 +1,186 @@ +package aws + +import ( + "bytes" + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/backup" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsBackupSelection() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsBackupSelectionCreate, + Read: resourceAwsBackupSelectionRead, + Delete: resourceAwsBackupSelectionDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "plan_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "iam_role": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "tag": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + }, + "key": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + Set: resourceAwsConditionTagHash, + }, + "resources": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func resourceAwsBackupSelectionCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).backupconn + + selection := &backup.Selection{} + + selection.SelectionName = aws.String(d.Get("name").(string)) + selection.IamRoleArn = aws.String(d.Get("iam_role").(string)) + selection.ListOfTags = gatherConditionTags(d) + selection.Resources = expandStringList(d.Get("resources").([]interface{})) + + input := &backup.CreateBackupSelectionInput{ + BackupPlanId: aws.String(d.Get("plan_id").(string)), + BackupSelection: selection, + } + + resp, err := conn.CreateBackupSelection(input) + if err != nil { + return err + } + + d.SetId(*resp.SelectionId) + + return resourceAwsBackupSelectionRead(d, meta) +} + +func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).backupconn + + input := &backup.GetBackupSelectionInput{ + BackupPlanId: aws.String(d.Get("plan_id").(string)), + SelectionId: aws.String(d.Id()), + } + + resp, err := conn.GetBackupSelection(input) + if err != nil { + return err + } + + d.Set("plan_id", resp.BackupPlanId) + + s := make(map[string]interface{}) + + s["name"] = *resp.BackupSelection.SelectionName + s["iam_role"] = *resp.BackupSelection.IamRoleArn + if resp.BackupSelection.ListOfTags != nil { + tag := &schema.Set{F: resourceAwsConditionTagHash} + + for _, r := range resp.BackupSelection.ListOfTags { + m := make(map[string]interface{}) + + m["type"] = *r.ConditionType + m["key"] = *r.ConditionKey + m["value"] = *r.ConditionValue + + tag.Add(m) + } + + s["tag"] = tag + } + if resp.BackupSelection.Resources != nil { + s["resources"] = resp.BackupSelection.Resources + } + + d.Set("selection", s) + + return nil +} + +func resourceAwsBackupSelectionDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).backupconn + + input := &backup.DeleteBackupSelectionInput{ + BackupPlanId: aws.String(d.Get("plan_id").(string)), + SelectionId: aws.String(d.Id()), + } + + _, err := conn.DeleteBackupSelection(input) + if err != nil { + return err + } + + return nil +} + +func gatherConditionTags(d *schema.ResourceData) []*backup.Condition { + conditions := []*backup.Condition{} + selectionTags := d.Get("tag").(*schema.Set).List() + + for _, i := range selectionTags { + item := i.(map[string]interface{}) + tag := &backup.Condition{} + + tag.ConditionType = aws.String(item["type"].(string)) + tag.ConditionKey = aws.String(item["key"].(string)) + tag.ConditionValue = aws.String(item["value"].(string)) + + conditions = append(conditions, tag) + } + + return conditions +} + +func resourceAwsConditionTagHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + if v, ok := m["type"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + if v, ok := m["key"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + if v, ok := m["value"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + return hashcode.String(buf.String()) +} diff --git a/aws/resource_aws_backup_selection_test.go b/aws/resource_aws_backup_selection_test.go new file mode 100644 index 00000000000..fe4f0cf8615 --- /dev/null +++ b/aws/resource_aws_backup_selection_test.go @@ -0,0 +1,100 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/backup" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsBackupSelection_basic(t *testing.T) { + rInt := acctest.RandInt() + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsBackupSelectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBackupSelectionConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsBackupSelectionExists("aws_backup_selection.test"), + ), + }, + }, + }) +} + +func testAccCheckAwsBackupSelectionDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).backupconn + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_backup_selection" { + continue + } + + input := &backup.GetBackupSelectionInput{ + BackupPlanId: aws.String(rs.Primary.Attributes["plan_id"]), + SelectionId: aws.String(rs.Primary.ID), + } + + resp, err := conn.GetBackupSelection(input) + + if err == nil { + if *resp.SelectionId == rs.Primary.ID { + return fmt.Errorf("Selection '%s' was not deleted properly", rs.Primary.ID) + } + } + } + + return nil +} + +func testAccCheckAwsBackupSelectionExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("not found: %s, %v", name, s.RootModule().Resources) + } + return nil + } +} + +func testAccBackupSelectionConfig(randInt int) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_backup_vault" "test" { + name = "tf_acc_test_backup_vault_%d" +} + +resource "aws_backup_plan" "test" { + name = "tf_acc_test_backup_plan_%d" + + rule { + rule_name = "tf_acc_test_backup_rule_%d" + target_vault_name = "${aws_backup_vault.test.name}" + schedule = "cron(0 12 * * ? *)" + } +} + +resource "aws_backup_selection" "test" { + plan_id = "${aws_backup_plan.test.id}" + + name = "tf_acc_test_backup_selection_%d" + iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + + tag { + type = "STRINGEQUALS" + key = "foo" + value = "bar" + } + + resources = [ + "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" + ] +} +`, randInt, randInt, randInt, randInt) +} diff --git a/website/aws.erb b/website/aws.erb index c05bda373d4..8f9a7af7f48 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -567,6 +567,9 @@ > aws_backup_plan + > + aws_backup_selection + > aws_backup_vault diff --git a/website/docs/r/backup_selection.html.markdown b/website/docs/r/backup_selection.html.markdown new file mode 100644 index 00000000000..48a5c6e1134 --- /dev/null +++ b/website/docs/r/backup_selection.html.markdown @@ -0,0 +1,52 @@ +--- +layout: "aws" +page_title: "AWS: aws_backup_selection" +sidebar_current: "docs-aws-resource-backup-selection" +description: |- + Manages selection conditions for AWS Backup plan resources. +--- + +# aws_backup_selection + +Manages selection conditions for AWS Backup plan resources. + +## Example Usage + +```hcl +resource "aws_backup_selection" "example" { + plan_id = "${aws_backup_plan.example.id}" + + name = "tf_example_backup_selection_%d" + iam_role = "arn:aws:iam::123456789012:role/service-role/AWSBackupDefaultServiceRole" + + tag { + type = "STRINGEQUALS" + key = "foo" + value = "bar" + } + + resources = [ + "arn:aws:ec2:us-east-1:123456789012:volume/" + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The display name of a resource selection document. +* `plan_id` - (Required) The backup plan ID to be associated with the selection of resources. +* `iam_role` - (Required) The ARN of the IAM role that AWS Backup uses to authenticate when restoring the target resource. +* `tag` - (Optional) Tag-based conditions used to specify a set of resources to assign to a backup plan. +* `resources` - (Optional) An array of strings that either contain Amazon Resource Names (ARNs) or match patterns of resources to assign to a backup plan.. + +Tag conditions (`tag`) support the following: + +* `type` - (Required) An operation, such as `StringEquals`, that is applied to a key-value pair used to filter resources in a selection. +* `key` - (Required) The key in a key-value pair. +* `value` - (Required) The value in a key-value pair. + +## Attributes Reference + +All of the above arguments are exported as attributes. From efb62f34f2140f6bc193fd45148f978b3a09694b Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Sat, 2 Feb 2019 22:45:36 -0600 Subject: [PATCH 2/9] adding tests --- aws/resource_aws_backup_selection.go | 12 +-- aws/resource_aws_backup_selection_test.go | 95 ++++++++++++++++++++++- 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/aws/resource_aws_backup_selection.go b/aws/resource_aws_backup_selection.go index 56a338fedac..43ea8ddf212 100644 --- a/aws/resource_aws_backup_selection.go +++ b/aws/resource_aws_backup_selection.go @@ -103,11 +103,9 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er } d.Set("plan_id", resp.BackupPlanId) + d.Set("name", resp.BackupSelection.SelectionName) + d.Set("iam_role", resp.BackupSelection.IamRoleArn) - s := make(map[string]interface{}) - - s["name"] = *resp.BackupSelection.SelectionName - s["iam_role"] = *resp.BackupSelection.IamRoleArn if resp.BackupSelection.ListOfTags != nil { tag := &schema.Set{F: resourceAwsConditionTagHash} @@ -121,14 +119,12 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er tag.Add(m) } - s["tag"] = tag + d.Set("tag", tag) } if resp.BackupSelection.Resources != nil { - s["resources"] = resp.BackupSelection.Resources + d.Set("resources", resp.BackupSelection.Resources) } - d.Set("selection", s) - return nil } diff --git a/aws/resource_aws_backup_selection_test.go b/aws/resource_aws_backup_selection_test.go index fe4f0cf8615..0fe8fdbc106 100644 --- a/aws/resource_aws_backup_selection_test.go +++ b/aws/resource_aws_backup_selection_test.go @@ -19,7 +19,7 @@ func TestAccAwsBackupSelection_basic(t *testing.T) { CheckDestroy: testAccCheckAwsBackupSelectionDestroy, Steps: []resource.TestStep{ { - Config: testAccBackupSelectionConfig(rInt), + Config: testAccBackupSelectionConfigBasic(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckAwsBackupSelectionExists("aws_backup_selection.test"), ), @@ -28,6 +28,42 @@ func TestAccAwsBackupSelection_basic(t *testing.T) { }) } +func TestAccAwsBackupSelection_withTags(t *testing.T) { + rInt := acctest.RandInt() + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsBackupSelectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBackupSelectionConfigWithTags(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsBackupSelectionExists("aws_backup_selection.test"), + resource.TestCheckResourceAttr("aws_backup_selection.test", "tag.#", "2"), + ), + }, + }, + }) +} + +func TestAccAwsBackupSelection_withResources(t *testing.T) { + rInt := acctest.RandInt() + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsBackupSelectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBackupSelectionConfigWithResources(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsBackupSelectionExists("aws_backup_selection.test"), + resource.TestCheckResourceAttr("aws_backup_selection.test", "resources.#", "2"), + ), + }, + }, + }) +} + func testAccCheckAwsBackupSelectionDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).backupconn for _, rs := range s.RootModule().Resources { @@ -62,7 +98,7 @@ func testAccCheckAwsBackupSelectionExists(name string) resource.TestCheckFunc { } } -func testAccBackupSelectionConfig(randInt int) string { +func testAccBackupSelectionConfigBase(rInt int) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -79,7 +115,32 @@ resource "aws_backup_plan" "test" { schedule = "cron(0 12 * * ? *)" } } +`, rInt, rInt, rInt) +} + +func testAccBackupSelectionConfigBasic(rInt int) string { + return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` +resource "aws_backup_selection" "test" { + plan_id = "${aws_backup_plan.test.id}" + + name = "tf_acc_test_backup_selection_%d" + iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + + tag { + type = "STRINGEQUALS" + key = "foo" + value = "bar" + } + + resources = [ + "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" + ] +} +`, rInt) +} +func testAccBackupSelectionConfigWithTags(rInt int) string { + return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` resource "aws_backup_selection" "test" { plan_id = "${aws_backup_plan.test.id}" @@ -92,9 +153,37 @@ resource "aws_backup_selection" "test" { value = "bar" } + tag { + type = "STRINGEQUALS" + key = "boo" + value = "far" + } + resources = [ "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" ] } -`, randInt, randInt, randInt, randInt) +`, rInt) +} + +func testAccBackupSelectionConfigWithResources(rInt int) string { + return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` +resource "aws_backup_selection" "test" { + plan_id = "${aws_backup_plan.test.id}" + + name = "tf_acc_test_backup_selection_%d" + iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + + tag { + type = "STRINGEQUALS" + key = "foo" + value = "bar" + } + + resources = [ + "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/", + "arn:aws:elasticfilesystem:us-east-1:${data.aws_caller_identity.current.account_id}:file-system/" + ] +} +`, rInt) } From c33b851f22b42babb1e30b440c20a01fb844f821 Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Mon, 18 Mar 2019 16:19:41 -0500 Subject: [PATCH 3/9] cleaning up tests and error handling --- aws/resource_aws_backup_selection.go | 6 +- aws/resource_aws_backup_selection_test.go | 100 +++++++++--------- website/docs/r/backup_selection.html.markdown | 22 ++-- 3 files changed, 64 insertions(+), 64 deletions(-) diff --git a/aws/resource_aws_backup_selection.go b/aws/resource_aws_backup_selection.go index 43ea8ddf212..36bb847ab00 100644 --- a/aws/resource_aws_backup_selection.go +++ b/aws/resource_aws_backup_selection.go @@ -81,7 +81,7 @@ func resourceAwsBackupSelectionCreate(d *schema.ResourceData, meta interface{}) resp, err := conn.CreateBackupSelection(input) if err != nil { - return err + return fmt.Errorf("error creating Backup Selection: %s", err) } d.SetId(*resp.SelectionId) @@ -99,7 +99,7 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er resp, err := conn.GetBackupSelection(input) if err != nil { - return err + return fmt.Errorf("error reading Backup Selection: %s", err) } d.Set("plan_id", resp.BackupPlanId) @@ -138,7 +138,7 @@ func resourceAwsBackupSelectionDelete(d *schema.ResourceData, meta interface{}) _, err := conn.DeleteBackupSelection(input) if err != nil { - return err + return fmt.Errorf("error deleting Backup Selection: %s", err) } return nil diff --git a/aws/resource_aws_backup_selection_test.go b/aws/resource_aws_backup_selection_test.go index 0fe8fdbc106..e4b881bff1e 100644 --- a/aws/resource_aws_backup_selection_test.go +++ b/aws/resource_aws_backup_selection_test.go @@ -103,17 +103,17 @@ func testAccBackupSelectionConfigBase(rInt int) string { data "aws_caller_identity" "current" {} resource "aws_backup_vault" "test" { - name = "tf_acc_test_backup_vault_%d" + name = "tf_acc_test_backup_vault_%d" } resource "aws_backup_plan" "test" { - name = "tf_acc_test_backup_plan_%d" + name = "tf_acc_test_backup_plan_%d" - rule { - rule_name = "tf_acc_test_backup_rule_%d" - target_vault_name = "${aws_backup_vault.test.name}" - schedule = "cron(0 12 * * ? *)" - } + rule { + rule_name = "tf_acc_test_backup_rule_%d" + target_vault_name = "${aws_backup_vault.test.name}" + schedule = "cron(0 12 * * ? *)" + } } `, rInt, rInt, rInt) } @@ -121,20 +121,20 @@ resource "aws_backup_plan" "test" { func testAccBackupSelectionConfigBasic(rInt int) string { return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` resource "aws_backup_selection" "test" { - plan_id = "${aws_backup_plan.test.id}" + plan_id = "${aws_backup_plan.test.id}" - name = "tf_acc_test_backup_selection_%d" - iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + name = "tf_acc_test_backup_selection_%d" + iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" - tag { - type = "STRINGEQUALS" - key = "foo" - value = "bar" - } + tag { + type = "STRINGEQUALS" + key = "foo" + value = "bar" + } - resources = [ - "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" - ] + resources = [ + "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" + ] } `, rInt) } @@ -142,26 +142,26 @@ resource "aws_backup_selection" "test" { func testAccBackupSelectionConfigWithTags(rInt int) string { return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` resource "aws_backup_selection" "test" { - plan_id = "${aws_backup_plan.test.id}" - - name = "tf_acc_test_backup_selection_%d" - iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" - - tag { - type = "STRINGEQUALS" - key = "foo" - value = "bar" - } - - tag { - type = "STRINGEQUALS" - key = "boo" - value = "far" - } - - resources = [ - "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" - ] + plan_id = "${aws_backup_plan.test.id}" + + name = "tf_acc_test_backup_selection_%d" + iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + + tag { + type = "STRINGEQUALS" + key = "foo" + value = "bar" + } + + tag { + type = "STRINGEQUALS" + key = "boo" + value = "far" + } + + resources = [ + "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" + ] } `, rInt) } @@ -169,21 +169,21 @@ resource "aws_backup_selection" "test" { func testAccBackupSelectionConfigWithResources(rInt int) string { return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` resource "aws_backup_selection" "test" { - plan_id = "${aws_backup_plan.test.id}" + plan_id = "${aws_backup_plan.test.id}" - name = "tf_acc_test_backup_selection_%d" - iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + name = "tf_acc_test_backup_selection_%d" + iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" - tag { - type = "STRINGEQUALS" - key = "foo" - value = "bar" - } + tag { + type = "STRINGEQUALS" + key = "foo" + value = "bar" + } - resources = [ - "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/", - "arn:aws:elasticfilesystem:us-east-1:${data.aws_caller_identity.current.account_id}:file-system/" - ] + resources = [ + "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/", + "arn:aws:elasticfilesystem:us-east-1:${data.aws_caller_identity.current.account_id}:file-system/" + ] } `, rInt) } diff --git a/website/docs/r/backup_selection.html.markdown b/website/docs/r/backup_selection.html.markdown index 48a5c6e1134..b68945a4c02 100644 --- a/website/docs/r/backup_selection.html.markdown +++ b/website/docs/r/backup_selection.html.markdown @@ -14,20 +14,20 @@ Manages selection conditions for AWS Backup plan resources. ```hcl resource "aws_backup_selection" "example" { - plan_id = "${aws_backup_plan.example.id}" + plan_id = "${aws_backup_plan.example.id}" - name = "tf_example_backup_selection_%d" - iam_role = "arn:aws:iam::123456789012:role/service-role/AWSBackupDefaultServiceRole" + name = "tf_example_backup_selection_%d" + iam_role = "arn:aws:iam::123456789012:role/service-role/AWSBackupDefaultServiceRole" - tag { - type = "STRINGEQUALS" - key = "foo" - value = "bar" - } + tag { + type = "STRINGEQUALS" + key = "foo" + value = "bar" + } - resources = [ - "arn:aws:ec2:us-east-1:123456789012:volume/" - ] + resources = [ + "arn:aws:ec2:us-east-1:123456789012:volume/" + ] } ``` From 33efb6923770272876ea390185d8a75ecf54d5dc Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Sat, 30 Mar 2019 15:51:57 -0500 Subject: [PATCH 4/9] changes based on review feedback --- aws/resource_aws_backup_selection.go | 52 ++++++++---- aws/resource_aws_backup_selection_test.go | 82 +++++++++++++++---- website/docs/r/backup_selection.html.markdown | 2 +- 3 files changed, 104 insertions(+), 32 deletions(-) diff --git a/aws/resource_aws_backup_selection.go b/aws/resource_aws_backup_selection.go index 36bb847ab00..fbd28e2dc8a 100644 --- a/aws/resource_aws_backup_selection.go +++ b/aws/resource_aws_backup_selection.go @@ -3,11 +3,14 @@ package aws import ( "bytes" "fmt" + "log" + "regexp" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/backup" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func resourceAwsBackupSelection() *schema.Resource { @@ -21,16 +24,21 @@ func resourceAwsBackupSelection() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 50), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9\-\_\.]+$`), "must contain only alphanumeric, hyphen, underscore, and period characters"), + ), }, "plan_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "iam_role": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + "iam_role_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, }, "tag": { Type: schema.TypeSet, @@ -41,6 +49,9 @@ func resourceAwsBackupSelection() *schema.Resource { "type": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.StringInSlice([]string{ + backup.ConditionTypeStringequals, + }, false), }, "key": { Type: schema.TypeString, @@ -67,12 +78,12 @@ func resourceAwsBackupSelection() *schema.Resource { func resourceAwsBackupSelectionCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).backupconn - selection := &backup.Selection{} - - selection.SelectionName = aws.String(d.Get("name").(string)) - selection.IamRoleArn = aws.String(d.Get("iam_role").(string)) - selection.ListOfTags = gatherConditionTags(d) - selection.Resources = expandStringList(d.Get("resources").([]interface{})) + selection := &backup.Selection{ + IamRoleArn: aws.String(d.Get("iam_role_arn").(string)), + ListOfTags: expandBackupConditionTags(d.Get("tag").(*schema.Set).List()), + Resources: expandStringList(d.Get("resources").([]interface{})), + SelectionName: aws.String(d.Get("name").(string)), + } input := &backup.CreateBackupSelectionInput{ BackupPlanId: aws.String(d.Get("plan_id").(string)), @@ -98,6 +109,12 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er } resp, err := conn.GetBackupSelection(input) + if isAWSErr(err, backup.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Backup Selection (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { return fmt.Errorf("error reading Backup Selection: %s", err) } @@ -112,14 +129,16 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er for _, r := range resp.BackupSelection.ListOfTags { m := make(map[string]interface{}) - m["type"] = *r.ConditionType - m["key"] = *r.ConditionKey - m["value"] = *r.ConditionValue + m["type"] = aws.StringValue(r.ConditionType) + m["key"] = aws.StringValue(r.ConditionKey) + m["value"] = aws.StringValue(r.ConditionValue) tag.Add(m) } - d.Set("tag", tag) + if err := d.Set("tag", tag); err != nil { + return fmt.Errorf("error setting tag: %s", err) + } } if resp.BackupSelection.Resources != nil { d.Set("resources", resp.BackupSelection.Resources) @@ -144,11 +163,10 @@ func resourceAwsBackupSelectionDelete(d *schema.ResourceData, meta interface{}) return nil } -func gatherConditionTags(d *schema.ResourceData) []*backup.Condition { +func expandBackupConditionTags(tagList []interface{}) []*backup.Condition { conditions := []*backup.Condition{} - selectionTags := d.Get("tag").(*schema.Set).List() - for _, i := range selectionTags { + for _, i := range tagList { item := i.(map[string]interface{}) tag := &backup.Condition{} diff --git a/aws/resource_aws_backup_selection_test.go b/aws/resource_aws_backup_selection_test.go index e4b881bff1e..dfddcd196ed 100644 --- a/aws/resource_aws_backup_selection_test.go +++ b/aws/resource_aws_backup_selection_test.go @@ -12,6 +12,7 @@ import ( ) func TestAccAwsBackupSelection_basic(t *testing.T) { + var selection1 backup.GetBackupSelectionOutput rInt := acctest.RandInt() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -21,14 +22,35 @@ func TestAccAwsBackupSelection_basic(t *testing.T) { { Config: testAccBackupSelectionConfigBasic(rInt), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsBackupSelectionExists("aws_backup_selection.test"), + testAccCheckAwsBackupSelectionExists("aws_backup_selection.test", &selection1), ), }, }, }) } +func TestAccAwsBackupSelection_disappears(t *testing.T) { + var selection1 backup.GetBackupSelectionOutput + rInt := acctest.RandInt() + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsBackupSelectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBackupSelectionConfigBasic(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsBackupSelectionExists("aws_backup_selection.test", &selection1), + testAccCheckAwsBackupSelectionDisappears(&selection1), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccAwsBackupSelection_withTags(t *testing.T) { + var selection1 backup.GetBackupSelectionOutput rInt := acctest.RandInt() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -38,7 +60,7 @@ func TestAccAwsBackupSelection_withTags(t *testing.T) { { Config: testAccBackupSelectionConfigWithTags(rInt), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsBackupSelectionExists("aws_backup_selection.test"), + testAccCheckAwsBackupSelectionExists("aws_backup_selection.test", &selection1), resource.TestCheckResourceAttr("aws_backup_selection.test", "tag.#", "2"), ), }, @@ -47,6 +69,7 @@ func TestAccAwsBackupSelection_withTags(t *testing.T) { } func TestAccAwsBackupSelection_withResources(t *testing.T) { + var selection1 backup.GetBackupSelectionOutput rInt := acctest.RandInt() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -56,7 +79,7 @@ func TestAccAwsBackupSelection_withResources(t *testing.T) { { Config: testAccBackupSelectionConfigWithResources(rInt), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsBackupSelectionExists("aws_backup_selection.test"), + testAccCheckAwsBackupSelectionExists("aws_backup_selection.test", &selection1), resource.TestCheckResourceAttr("aws_backup_selection.test", "resources.#", "2"), ), }, @@ -88,16 +111,47 @@ func testAccCheckAwsBackupSelectionDestroy(s *terraform.State) error { return nil } -func testAccCheckAwsBackupSelectionExists(name string) resource.TestCheckFunc { +func testAccCheckAwsBackupSelectionExists(name string, selection *backup.GetBackupSelectionOutput) resource.TestCheckFunc { return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("not found: %s, %v", name, s.RootModule().Resources) } + + conn := testAccProvider.Meta().(*AWSClient).backupconn + + input := &backup.GetBackupSelectionInput{ + BackupPlanId: aws.String(rs.Primary.Attributes["plan_id"]), + SelectionId: aws.String(rs.Primary.ID), + } + + output, err := conn.GetBackupSelection(input) + + if err != nil { + return err + } + + *selection = *output + return nil } } +func testAccCheckAwsBackupSelectionDisappears(selection *backup.GetBackupSelectionOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).backupconn + + input := &backup.DeleteBackupSelectionInput{ + BackupPlanId: selection.BackupPlanId, + SelectionId: selection.SelectionId, + } + + _, err := conn.DeleteBackupSelection(input) + + return err + } +} + func testAccBackupSelectionConfigBase(rInt int) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -121,10 +175,10 @@ resource "aws_backup_plan" "test" { func testAccBackupSelectionConfigBasic(rInt int) string { return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` resource "aws_backup_selection" "test" { - plan_id = "${aws_backup_plan.test.id}" + plan_id = "${aws_backup_plan.test.id}" - name = "tf_acc_test_backup_selection_%d" - iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + name = "tf_acc_test_backup_selection_%d" + iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" tag { type = "STRINGEQUALS" @@ -142,10 +196,10 @@ resource "aws_backup_selection" "test" { func testAccBackupSelectionConfigWithTags(rInt int) string { return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` resource "aws_backup_selection" "test" { - plan_id = "${aws_backup_plan.test.id}" + plan_id = "${aws_backup_plan.test.id}" - name = "tf_acc_test_backup_selection_%d" - iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + name = "tf_acc_test_backup_selection_%d" + iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" tag { type = "STRINGEQUALS" @@ -169,10 +223,10 @@ resource "aws_backup_selection" "test" { func testAccBackupSelectionConfigWithResources(rInt int) string { return testAccBackupSelectionConfigBase(rInt) + fmt.Sprintf(` resource "aws_backup_selection" "test" { - plan_id = "${aws_backup_plan.test.id}" + plan_id = "${aws_backup_plan.test.id}" - name = "tf_acc_test_backup_selection_%d" - iam_role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + name = "tf_acc_test_backup_selection_%d" + iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" tag { type = "STRINGEQUALS" diff --git a/website/docs/r/backup_selection.html.markdown b/website/docs/r/backup_selection.html.markdown index b68945a4c02..e17aa4ece6b 100644 --- a/website/docs/r/backup_selection.html.markdown +++ b/website/docs/r/backup_selection.html.markdown @@ -16,7 +16,7 @@ Manages selection conditions for AWS Backup plan resources. resource "aws_backup_selection" "example" { plan_id = "${aws_backup_plan.example.id}" - name = "tf_example_backup_selection_%d" + name = "tf_example_backup_selection" iam_role = "arn:aws:iam::123456789012:role/service-role/AWSBackupDefaultServiceRole" tag { From 4d2e478a182937d1378a67ee5d9f0030ee4fba87 Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Sat, 30 Mar 2019 15:54:23 -0500 Subject: [PATCH 5/9] forgot to document the id output value --- website/docs/r/backup_selection.html.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/r/backup_selection.html.markdown b/website/docs/r/backup_selection.html.markdown index e17aa4ece6b..42f8887c3bd 100644 --- a/website/docs/r/backup_selection.html.markdown +++ b/website/docs/r/backup_selection.html.markdown @@ -49,4 +49,6 @@ Tag conditions (`tag`) support the following: ## Attributes Reference -All of the above arguments are exported as attributes. +In addition to all arguments above, the following attributes are exported: + +* `id` - Backup Selection identifier From 8d4ee5237be1b64c700d65992ddad70046bffcf0 Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Sat, 30 Mar 2019 15:57:36 -0500 Subject: [PATCH 6/9] forgot to update all iam_role_arn references --- aws/resource_aws_backup_selection.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_backup_selection.go b/aws/resource_aws_backup_selection.go index fbd28e2dc8a..756ca908646 100644 --- a/aws/resource_aws_backup_selection.go +++ b/aws/resource_aws_backup_selection.go @@ -121,7 +121,7 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er d.Set("plan_id", resp.BackupPlanId) d.Set("name", resp.BackupSelection.SelectionName) - d.Set("iam_role", resp.BackupSelection.IamRoleArn) + d.Set("iam_role_arn", resp.BackupSelection.IamRoleArn) if resp.BackupSelection.ListOfTags != nil { tag := &schema.Set{F: resourceAwsConditionTagHash} From 5132ea53d6c2495fcb44ddccb2e227a4c8930bcd Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Sat, 30 Mar 2019 16:46:41 -0500 Subject: [PATCH 7/9] fixing resource tfstate issue --- aws/resource_aws_backup_selection.go | 4 +++- aws/resource_aws_backup_selection_test.go | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_backup_selection.go b/aws/resource_aws_backup_selection.go index 756ca908646..bb1bbe71c69 100644 --- a/aws/resource_aws_backup_selection.go +++ b/aws/resource_aws_backup_selection.go @@ -141,7 +141,9 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er } } if resp.BackupSelection.Resources != nil { - d.Set("resources", resp.BackupSelection.Resources) + if err := d.Set("resources", aws.StringValueSlice(resp.BackupSelection.Resources)); err != nil { + return fmt.Errorf("error setting resources: %s", err) + } } return nil diff --git a/aws/resource_aws_backup_selection_test.go b/aws/resource_aws_backup_selection_test.go index dfddcd196ed..27d30f61eef 100644 --- a/aws/resource_aws_backup_selection_test.go +++ b/aws/resource_aws_backup_selection_test.go @@ -235,8 +235,8 @@ resource "aws_backup_selection" "test" { } resources = [ - "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/", - "arn:aws:elasticfilesystem:us-east-1:${data.aws_caller_identity.current.account_id}:file-system/" + "arn:aws:elasticfilesystem:us-east-1:${data.aws_caller_identity.current.account_id}:file-system/", + "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" ] } `, rInt) From c80dd6f0e94317ad8a42f131632b7157bf459f78 Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Wed, 3 Apr 2019 21:02:51 -0500 Subject: [PATCH 8/9] more changes based on feedback --- aws/resource_aws_backup_selection.go | 34 ++++--------------- aws/resource_aws_backup_selection_test.go | 10 +++--- website/docs/r/backup_selection.html.markdown | 4 +-- 3 files changed, 13 insertions(+), 35 deletions(-) diff --git a/aws/resource_aws_backup_selection.go b/aws/resource_aws_backup_selection.go index bb1bbe71c69..2b55b30a95d 100644 --- a/aws/resource_aws_backup_selection.go +++ b/aws/resource_aws_backup_selection.go @@ -1,14 +1,12 @@ package aws import ( - "bytes" "fmt" "log" "regexp" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/backup" - "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" ) @@ -40,7 +38,7 @@ func resourceAwsBackupSelection() *schema.Resource { ForceNew: true, ValidateFunc: validateArn, }, - "tag": { + "selection_tag": { Type: schema.TypeSet, Optional: true, ForceNew: true, @@ -63,7 +61,6 @@ func resourceAwsBackupSelection() *schema.Resource { }, }, }, - Set: resourceAwsConditionTagHash, }, "resources": { Type: schema.TypeList, @@ -80,7 +77,7 @@ func resourceAwsBackupSelectionCreate(d *schema.ResourceData, meta interface{}) selection := &backup.Selection{ IamRoleArn: aws.String(d.Get("iam_role_arn").(string)), - ListOfTags: expandBackupConditionTags(d.Get("tag").(*schema.Set).List()), + ListOfTags: expandBackupConditionTags(d.Get("selection_tag").(*schema.Set).List()), Resources: expandStringList(d.Get("resources").([]interface{})), SelectionName: aws.String(d.Get("name").(string)), } @@ -124,7 +121,7 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er d.Set("iam_role_arn", resp.BackupSelection.IamRoleArn) if resp.BackupSelection.ListOfTags != nil { - tag := &schema.Set{F: resourceAwsConditionTagHash} + tags := make([]map[string]interface{}, 0) for _, r := range resp.BackupSelection.ListOfTags { m := make(map[string]interface{}) @@ -133,11 +130,11 @@ func resourceAwsBackupSelectionRead(d *schema.ResourceData, meta interface{}) er m["key"] = aws.StringValue(r.ConditionKey) m["value"] = aws.StringValue(r.ConditionValue) - tag.Add(m) + tags = append(tags, m) } - if err := d.Set("tag", tag); err != nil { - return fmt.Errorf("error setting tag: %s", err) + if err := d.Set("selection_tag", tags); err != nil { + return fmt.Errorf("error setting selection tag: %s", err) } } if resp.BackupSelection.Resources != nil { @@ -181,22 +178,3 @@ func expandBackupConditionTags(tagList []interface{}) []*backup.Condition { return conditions } - -func resourceAwsConditionTagHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - - if v, ok := m["type"]; ok { - buf.WriteString(fmt.Sprintf("%s-", v.(string))) - } - - if v, ok := m["key"]; ok { - buf.WriteString(fmt.Sprintf("%s-", v.(string))) - } - - if v, ok := m["value"]; ok { - buf.WriteString(fmt.Sprintf("%s-", v.(string))) - } - - return hashcode.String(buf.String()) -} diff --git a/aws/resource_aws_backup_selection_test.go b/aws/resource_aws_backup_selection_test.go index 27d30f61eef..700e0482c28 100644 --- a/aws/resource_aws_backup_selection_test.go +++ b/aws/resource_aws_backup_selection_test.go @@ -61,7 +61,7 @@ func TestAccAwsBackupSelection_withTags(t *testing.T) { Config: testAccBackupSelectionConfigWithTags(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckAwsBackupSelectionExists("aws_backup_selection.test", &selection1), - resource.TestCheckResourceAttr("aws_backup_selection.test", "tag.#", "2"), + resource.TestCheckResourceAttr("aws_backup_selection.test", "selection_tag.#", "2"), ), }, }, @@ -180,7 +180,7 @@ resource "aws_backup_selection" "test" { name = "tf_acc_test_backup_selection_%d" iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" - tag { + selection_tag { type = "STRINGEQUALS" key = "foo" value = "bar" @@ -201,13 +201,13 @@ resource "aws_backup_selection" "test" { name = "tf_acc_test_backup_selection_%d" iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" - tag { + selection_tag { type = "STRINGEQUALS" key = "foo" value = "bar" } - tag { + selection_tag { type = "STRINGEQUALS" key = "boo" value = "far" @@ -228,7 +228,7 @@ resource "aws_backup_selection" "test" { name = "tf_acc_test_backup_selection_%d" iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" - tag { + selection_tag { type = "STRINGEQUALS" key = "foo" value = "bar" diff --git a/website/docs/r/backup_selection.html.markdown b/website/docs/r/backup_selection.html.markdown index 42f8887c3bd..fbc07c0920b 100644 --- a/website/docs/r/backup_selection.html.markdown +++ b/website/docs/r/backup_selection.html.markdown @@ -38,10 +38,10 @@ The following arguments are supported: * `name` - (Required) The display name of a resource selection document. * `plan_id` - (Required) The backup plan ID to be associated with the selection of resources. * `iam_role` - (Required) The ARN of the IAM role that AWS Backup uses to authenticate when restoring the target resource. -* `tag` - (Optional) Tag-based conditions used to specify a set of resources to assign to a backup plan. +* `selection_tag` - (Optional) Tag-based conditions used to specify a set of resources to assign to a backup plan. * `resources` - (Optional) An array of strings that either contain Amazon Resource Names (ARNs) or match patterns of resources to assign to a backup plan.. -Tag conditions (`tag`) support the following: +Tag conditions (`selection_tag`) support the following: * `type` - (Required) An operation, such as `StringEquals`, that is applied to a key-value pair used to filter resources in a selection. * `key` - (Required) The key in a key-value pair. From 7524304e7de61220afffb4b0418c855e0b50ac42 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 4 Apr 2019 07:55:36 -0400 Subject: [PATCH 9/9] resource/aws_backup_selection: Use TypeSet for resources attribute to prevent ordering issues and aws_partition/aws_region data sources in testing to prevent invalid ARN errors Previous output from acceptance testing: ``` --- FAIL: TestAccAwsBackupSelection_withResources (13.48s) testing.go:538: Step 0 error: Error applying: 1 error occurred: * aws_backup_selection.test: 1 error occurred: * aws_backup_selection.test: error creating Backup Selection: InvalidParameterValueException: Invalid ARN: arn:aws:elasticfilesystem:us-east-1:--OMITTED--:file-system/. Expected region is us-west-2 status code: 400, request id: 8c64a59c-7b8c-46de-8e7e-6fa1a7879b3a --- FAIL: TestAccAwsBackupSelection_disappears (13.53s) testing.go:538: Step 0 error: Error applying: 1 error occurred: * aws_backup_selection.test: 1 error occurred: * aws_backup_selection.test: error creating Backup Selection: InvalidParameterValueException: Invalid ARN: arn:aws:ec2:us-east-1:--OMITTED--:volume/. Expected region is us-west-2 status code: 400, request id: bb483272-0b15-4978-88ab-0a5cf2400302 --- FAIL: TestAccAwsBackupSelection_basic (13.60s) testing.go:538: Step 0 error: Error applying: 1 error occurred: * aws_backup_selection.test: 1 error occurred: * aws_backup_selection.test: error creating Backup Selection: InvalidParameterValueException: Invalid ARN: arn:aws:ec2:us-east-1:--OMITTED--:volume/. Expected region is us-west-2 status code: 400, request id: 6e4c8dda-6cc8-4d63-be62-b4b156e4c30b --- FAIL: TestAccAwsBackupSelection_withTags (13.66s) testing.go:538: Step 0 error: Error applying: 1 error occurred: * aws_backup_selection.test: 1 error occurred: * aws_backup_selection.test: error creating Backup Selection: InvalidParameterValueException: Invalid ARN: arn:aws:ec2:us-east-1:--OMITTED--:volume/. Expected region is us-west-2 status code: 400, request id: a1229860-b1f7-4462-9b22-68d46cf62e31 --- FAIL: TestAccAwsBackupSelection_withResources (14.15s) testing.go:538: Step 0 error: After applying this step, the plan was not empty: DIFF: DESTROY/CREATE: aws_backup_selection.test iam_role_arn: "arn:aws:iam::123456789012:role/service-role/AWSBackupDefaultServiceRole" => "arn:aws:iam::187416307283:role/service-role/AWSBackupDefaultServiceRole" name: "tf_acc_test_backup_selection_5994421555613158377" => "tf_acc_test_backup_selection_5994421555613158377" plan_id: "7e69f127-aab4-4217-90cc-437ec8d9e19a" => "7e69f127-aab4-4217-90cc-437ec8d9e19a" resources.#: "2" => "2" resources.0: "arn:aws:ec2:us-west-2:123456789012:volume/" => "arn:aws:elasticfilesystem:us-west-2:123456789012:file-system/" (forces new resource) resources.1: "arn:aws:elasticfilesystem:us-west-2:123456789012:file-system/" => "arn:aws:ec2:us-west-2:123456789012:volume/" (forces new resource) selection_tag.#: "1" => "1" selection_tag.3487478888.key: "foo" => "foo" selection_tag.3487478888.type: "STRINGEQUALS" => "STRINGEQUALS" selection_tag.3487478888.value: "bar" => "bar" ``` Output from acceptance testing: ``` --- PASS: TestAccAwsBackupSelection_withResources (16.55s) --- PASS: TestAccAwsBackupSelection_basic (17.21s) --- PASS: TestAccAwsBackupSelection_disappears (17.70s) --- PASS: TestAccAwsBackupSelection_withTags (17.79s) ``` --- aws/resource_aws_backup_selection.go | 4 ++-- aws/resource_aws_backup_selection_test.go | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_backup_selection.go b/aws/resource_aws_backup_selection.go index 2b55b30a95d..6db13f2e524 100644 --- a/aws/resource_aws_backup_selection.go +++ b/aws/resource_aws_backup_selection.go @@ -63,7 +63,7 @@ func resourceAwsBackupSelection() *schema.Resource { }, }, "resources": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, @@ -78,7 +78,7 @@ func resourceAwsBackupSelectionCreate(d *schema.ResourceData, meta interface{}) selection := &backup.Selection{ IamRoleArn: aws.String(d.Get("iam_role_arn").(string)), ListOfTags: expandBackupConditionTags(d.Get("selection_tag").(*schema.Set).List()), - Resources: expandStringList(d.Get("resources").([]interface{})), + Resources: expandStringSet(d.Get("resources").(*schema.Set)), SelectionName: aws.String(d.Get("name").(string)), } diff --git a/aws/resource_aws_backup_selection_test.go b/aws/resource_aws_backup_selection_test.go index 700e0482c28..655209b3b7d 100644 --- a/aws/resource_aws_backup_selection_test.go +++ b/aws/resource_aws_backup_selection_test.go @@ -156,6 +156,10 @@ func testAccBackupSelectionConfigBase(rInt int) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} +data "aws_partition" "current" {} + +data "aws_region" "current" {} + resource "aws_backup_vault" "test" { name = "tf_acc_test_backup_vault_%d" } @@ -178,7 +182,7 @@ resource "aws_backup_selection" "test" { plan_id = "${aws_backup_plan.test.id}" name = "tf_acc_test_backup_selection_%d" - iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + iam_role_arn = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" selection_tag { type = "STRINGEQUALS" @@ -187,7 +191,7 @@ resource "aws_backup_selection" "test" { } resources = [ - "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" + "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:volume/" ] } `, rInt) @@ -199,7 +203,7 @@ resource "aws_backup_selection" "test" { plan_id = "${aws_backup_plan.test.id}" name = "tf_acc_test_backup_selection_%d" - iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + iam_role_arn = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" selection_tag { type = "STRINGEQUALS" @@ -214,7 +218,7 @@ resource "aws_backup_selection" "test" { } resources = [ - "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" + "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:volume/" ] } `, rInt) @@ -226,7 +230,7 @@ resource "aws_backup_selection" "test" { plan_id = "${aws_backup_plan.test.id}" name = "tf_acc_test_backup_selection_%d" - iam_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" + iam_role_arn = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role/service-role/AWSBackupDefaultServiceRole" selection_tag { type = "STRINGEQUALS" @@ -235,8 +239,8 @@ resource "aws_backup_selection" "test" { } resources = [ - "arn:aws:elasticfilesystem:us-east-1:${data.aws_caller_identity.current.account_id}:file-system/", - "arn:aws:ec2:us-east-1:${data.aws_caller_identity.current.account_id}:volume/" + "arn:${data.aws_partition.current.partition}:elasticfilesystem:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:file-system/", + "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:volume/" ] } `, rInt)