Skip to content

Commit

Permalink
Merge pull request #12501 from DrFaust92/r/backup_vault_notif
Browse files Browse the repository at this point in the history
r/backup_vault_notifications - Add new resource
  • Loading branch information
breathingdust authored Sep 29, 2020
2 parents 3a39299 + 68bc9aa commit fd436d6
Show file tree
Hide file tree
Showing 5 changed files with 403 additions and 0 deletions.
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ func Provider() *schema.Provider {
"aws_backup_plan": resourceAwsBackupPlan(),
"aws_backup_selection": resourceAwsBackupSelection(),
"aws_backup_vault": resourceAwsBackupVault(),
"aws_backup_vault_notifications": resourceAwsBackupVaultNotifications(),
"aws_budgets_budget": resourceAwsBudgetsBudget(),
"aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(),
"aws_cloudformation_stack": resourceAwsCloudFormationStack(),
Expand Down
115 changes: 115 additions & 0 deletions aws/resource_aws_backup_vault_notifications.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package aws

import (
"fmt"
"log"
"regexp"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/backup"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceAwsBackupVaultNotifications() *schema.Resource {
return &schema.Resource{
Create: resourceAwsBackupVaultNotificationsCreate,
Read: resourceAwsBackupVaultNotificationsRead,
Delete: resourceAwsBackupVaultNotificationsDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"backup_vault_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9\-\_\.]{1,50}$`), "must consist of lowercase letters, numbers, and hyphens."),
},
"sns_topic_arn": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateArn,
},
"backup_vault_events": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice(backup.VaultEvent_Values(), false),
},
},
"backup_vault_arn": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceAwsBackupVaultNotificationsCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).backupconn

input := &backup.PutBackupVaultNotificationsInput{
BackupVaultName: aws.String(d.Get("backup_vault_name").(string)),
SNSTopicArn: aws.String(d.Get("sns_topic_arn").(string)),
BackupVaultEvents: expandStringSet(d.Get("backup_vault_events").(*schema.Set)),
}

_, err := conn.PutBackupVaultNotifications(input)
if err != nil {
return fmt.Errorf("error creating Backup Vault Notifications (%s): %w", d.Id(), err)
}

d.SetId(d.Get("backup_vault_name").(string))

return resourceAwsBackupVaultNotificationsRead(d, meta)
}

func resourceAwsBackupVaultNotificationsRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).backupconn

input := &backup.GetBackupVaultNotificationsInput{
BackupVaultName: aws.String(d.Id()),
}

resp, err := conn.GetBackupVaultNotifications(input)
if isAWSErr(err, backup.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] Backup Vault Notifcations %s not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading Backup Vault Notifications (%s): %w", d.Id(), err)
}
d.Set("backup_vault_name", resp.BackupVaultName)
d.Set("sns_topic_arn", resp.SNSTopicArn)
d.Set("backup_vault_arn", resp.BackupVaultArn)
if err := d.Set("backup_vault_events", flattenStringSet(resp.BackupVaultEvents)); err != nil {
return fmt.Errorf("error setting backup_vault_events: %w", err)
}

return nil
}

func resourceAwsBackupVaultNotificationsDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).backupconn

input := &backup.DeleteBackupVaultNotificationsInput{
BackupVaultName: aws.String(d.Id()),
}

_, err := conn.DeleteBackupVaultNotifications(input)
if err != nil {
if isAWSErr(err, backup.ErrCodeResourceNotFoundException, "") {
return nil
}
return fmt.Errorf("error deleting Backup Vault Notifications (%s): %w", d.Id(), err)
}

return nil
}
210 changes: 210 additions & 0 deletions aws/resource_aws_backup_vault_notifications_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package aws

import (
"fmt"
"log"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/backup"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func init() {
resource.AddTestSweepers("aws_backup_vault_notifications", &resource.Sweeper{
Name: "aws_backup_vault_notifications",
F: testSweepBackupVaultNotifications,
})
}

func testSweepBackupVaultNotifications(region string) error {
client, err := sharedClientForRegion(region)
if err != nil {
return fmt.Errorf("Error getting client: %w", err)
}
conn := client.(*AWSClient).backupconn
var sweeperErrs *multierror.Error

input := &backup.ListBackupVaultsInput{}

for {
output, err := conn.ListBackupVaults(input)
if err != nil {
if testSweepSkipSweepError(err) {
log.Printf("[WARN] Skipping Backup Vault Notifications sweep for %s: %s", region, err)
return nil
}
sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving Backup Vault Notifications: %w", err))
return sweeperErrs.ErrorOrNil()
}

if len(output.BackupVaultList) == 0 {
log.Print("[DEBUG] No Backup Vault Notifications to sweep")
return nil
}

for _, rule := range output.BackupVaultList {
name := aws.StringValue(rule.BackupVaultName)

log.Printf("[INFO] Deleting Backup Vault Notifications %s", name)
_, err := conn.DeleteBackupVaultNotifications(&backup.DeleteBackupVaultNotificationsInput{
BackupVaultName: aws.String(name),
})
if err != nil {
sweeperErr := fmt.Errorf("error deleting Backup Vault Notifications %s: %w", name, err)
log.Printf("[ERROR] %s", sweeperErr)
sweeperErrs = multierror.Append(sweeperErrs, sweeperErr)
continue
}
}

if output.NextToken == nil {
break
}
input.NextToken = output.NextToken
}

return sweeperErrs.ErrorOrNil()
}

func TestAccAwsBackupVaultNotification_basic(t *testing.T) {
var vault backup.GetBackupVaultNotificationsOutput

rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_backup_vault_notifications.test"
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSBackup(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsBackupVaultNotificationDestroy,
Steps: []resource.TestStep{
{
Config: testAccBackupVaultNotificationConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsBackupVaultNotificationExists(resourceName, &vault),
resource.TestCheckResourceAttr(resourceName, "backup_vault_events.#", "2"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAwsBackupVaultNotification_disappears(t *testing.T) {
var vault backup.GetBackupVaultNotificationsOutput

rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_backup_vault_notifications.test"
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSBackup(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsBackupVaultNotificationDestroy,
Steps: []resource.TestStep{
{
Config: testAccBackupVaultNotificationConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsBackupVaultNotificationExists(resourceName, &vault),
testAccCheckResourceDisappears(testAccProvider, resourceAwsBackupVaultNotifications(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccCheckAwsBackupVaultNotificationDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).backupconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_backup_vault_notifications" {
continue
}

input := &backup.GetBackupVaultNotificationsInput{
BackupVaultName: aws.String(rs.Primary.ID),
}

resp, err := conn.GetBackupVaultNotifications(input)

if err == nil {
if aws.StringValue(resp.BackupVaultName) == rs.Primary.ID {
return fmt.Errorf("Backup Plan notifications '%s' was not deleted properly", rs.Primary.ID)
}
}
}

return nil
}

func testAccCheckAwsBackupVaultNotificationExists(name string, vault *backup.GetBackupVaultNotificationsOutput) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}

conn := testAccProvider.Meta().(*AWSClient).backupconn
params := &backup.GetBackupVaultNotificationsInput{
BackupVaultName: aws.String(rs.Primary.ID),
}
resp, err := conn.GetBackupVaultNotifications(params)
if err != nil {
return err
}

*vault = *resp

return nil
}
}

func testAccBackupVaultNotificationConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_backup_vault" "test" {
name = %[1]q
}
resource "aws_sns_topic" "test" {
name = %[1]q
}
data "aws_iam_policy_document" "test" {
policy_id = "__default_policy_ID"
statement {
actions = [
"SNS:Publish",
]
effect = "Allow"
principals {
type = "Service"
identifiers = ["backup.amazonaws.com"]
}
resources = [
"${aws_sns_topic.test.arn}",
]
sid = "__default_statement_ID"
}
}
resource "aws_sns_topic_policy" "test" {
arn = aws_sns_topic.test.arn
policy = data.aws_iam_policy_document.test.json
}
resource "aws_backup_vault_notifications" "test" {
backup_vault_name = aws_backup_vault.test.name
sns_topic_arn = aws_sns_topic.test.arn
backup_vault_events = ["BACKUP_JOB_STARTED", "RESTORE_JOB_COMPLETED"]
}
`, rName)
}
1 change: 1 addition & 0 deletions aws/resource_aws_sns_topic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func init() {
F: testSweepSnsTopics,
Dependencies: []string{
"aws_autoscaling_group",
"aws_backup_vault_notifications",
"aws_budgets_budget",
"aws_config_delivery_channel",
"aws_dax_cluster",
Expand Down
Loading

0 comments on commit fd436d6

Please sign in to comment.