diff --git a/aws/provider.go b/aws/provider.go index 8c9121b48b6..150e51e7655 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -507,6 +507,7 @@ func Provider() terraform.ResourceProvider { "aws_neptune_cluster": resourceAwsNeptuneCluster(), "aws_neptune_cluster_instance": resourceAwsNeptuneClusterInstance(), "aws_neptune_cluster_parameter_group": resourceAwsNeptuneClusterParameterGroup(), + "aws_neptune_event_subscription": resourceAwsNeptuneEventSubscription(), "aws_neptune_parameter_group": resourceAwsNeptuneParameterGroup(), "aws_neptune_subnet_group": resourceAwsNeptuneSubnetGroup(), "aws_network_acl_rule": resourceAwsNetworkAclRule(), diff --git a/aws/resource_aws_neptune_event_subscription.go b/aws/resource_aws_neptune_event_subscription.go new file mode 100644 index 00000000000..e5fb2e0043b --- /dev/null +++ b/aws/resource_aws_neptune_event_subscription.go @@ -0,0 +1,388 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/neptune" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsNeptuneEventSubscription() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsNeptuneEventSubscriptionCreate, + Read: resourceAwsNeptuneEventSubscriptionRead, + Update: resourceAwsNeptuneEventSubscriptionUpdate, + Delete: resourceAwsNeptuneEventSubscriptionDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(40 * time.Minute), + Delete: schema.DefaultTimeout(40 * time.Minute), + Update: schema.DefaultTimeout(40 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: validateNeptuneEventSubscriptionName, + }, + "name_prefix": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name"}, + ValidateFunc: validateNeptuneEventSubscriptionNamePrefix, + }, + "sns_topic_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + "event_categories": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "source_ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "source_type": { + Type: schema.TypeString, + Optional: true, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "customer_aws_id": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchema(), + }, + } +} + +func resourceAwsNeptuneEventSubscriptionCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).neptuneconn + + if v, ok := d.GetOk("name"); ok { + d.Set("name", v.(string)) + } else if v, ok := d.GetOk("name_prefix"); ok { + d.Set("name", resource.PrefixedUniqueId(v.(string))) + } else { + d.Set("name", resource.PrefixedUniqueId("tf-")) + } + + tags := tagsFromMapNeptune(d.Get("tags").(map[string]interface{})) + + request := &neptune.CreateEventSubscriptionInput{ + SubscriptionName: aws.String(d.Get("name").(string)), + SnsTopicArn: aws.String(d.Get("sns_topic_arn").(string)), + Enabled: aws.Bool(d.Get("enabled").(bool)), + Tags: tags, + } + + if v, ok := d.GetOk("source_ids"); ok { + sourceIdsSet := v.(*schema.Set) + sourceIds := make([]*string, sourceIdsSet.Len()) + for i, sourceId := range sourceIdsSet.List() { + sourceIds[i] = aws.String(sourceId.(string)) + } + request.SourceIds = sourceIds + } + + if v, ok := d.GetOk("event_categories"); ok { + eventCategoriesSet := v.(*schema.Set) + eventCategories := make([]*string, eventCategoriesSet.Len()) + for i, eventCategory := range eventCategoriesSet.List() { + eventCategories[i] = aws.String(eventCategory.(string)) + } + request.EventCategories = eventCategories + } + + if v, ok := d.GetOk("source_type"); ok { + request.SourceType = aws.String(v.(string)) + } + + log.Println("[DEBUG] Create Neptune Event Subscription:", request) + + output, err := conn.CreateEventSubscription(request) + if err != nil || output.EventSubscription == nil { + return fmt.Errorf("Error creating Neptune Event Subscription %s: %s", d.Get("name").(string), err) + } + + d.SetId(aws.StringValue(output.EventSubscription.CustSubscriptionId)) + + log.Println("[INFO] Waiting for Neptune Event Subscription to be ready") + + stateConf := &resource.StateChangeConf{ + Pending: []string{"creating"}, + Target: []string{"active"}, + Refresh: resourceAwsNeptuneEventSubscriptionRefreshFunc(d.Id(), conn), + Timeout: d.Timeout(schema.TimeoutCreate), + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + // Wait, catching any errors + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for Neptune Event Subscription state to be \"active\": %s", err) + } + + return resourceAwsNeptuneEventSubscriptionRead(d, meta) +} + +func resourceAwsNeptuneEventSubscriptionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).neptuneconn + + sub, err := resourceAwsNeptuneEventSubscriptionRetrieve(d.Id(), conn) + if err != nil { + return fmt.Errorf("Error reading Neptune Event Subscription %s: %s", d.Id(), err) + } + + if sub == nil { + log.Printf("[DEBUG] Neptune Event Subscription (%s) not found - removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("arn", sub.EventSubscriptionArn) + d.Set("name", sub.CustSubscriptionId) + d.Set("sns_topic_arn", sub.SnsTopicArn) + d.Set("enabled", sub.Enabled) + d.Set("customer_aws_id", sub.CustomerAwsId) + + if sub.SourceType != nil { + d.Set("source_type", sub.SourceType) + } + + if sub.SourceIdsList != nil { + if err := d.Set("source_ids", flattenStringList(sub.SourceIdsList)); err != nil { + return fmt.Errorf("Error saving Source IDs to state for Neptune Event Subscription (%s): %s", d.Id(), err) + } + } + + if sub.EventCategoriesList != nil { + if err := d.Set("event_categories", flattenStringList(sub.EventCategoriesList)); err != nil { + return fmt.Errorf("Error saving Event Categories to state for Neptune Event Subscription (%s): %s", d.Id(), err) + } + } + + if err := saveTagsNeptune(conn, d, aws.StringValue(sub.EventSubscriptionArn)); err != nil { + return fmt.Errorf("Error saving tags for Neptune Event Subscription (%s): %s", d.Id(), err) + } + + return nil +} + +func resourceAwsNeptuneEventSubscriptionUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).neptuneconn + + d.Partial(true) + requestUpdate := false + + req := &neptune.ModifyEventSubscriptionInput{ + SubscriptionName: aws.String(d.Id()), + } + + if d.HasChange("event_categories") { + eventCategoriesSet := d.Get("event_categories").(*schema.Set) + req.EventCategories = make([]*string, eventCategoriesSet.Len()) + for i, eventCategory := range eventCategoriesSet.List() { + req.EventCategories[i] = aws.String(eventCategory.(string)) + } + req.SourceType = aws.String(d.Get("source_type").(string)) + requestUpdate = true + } + + if d.HasChange("enabled") { + req.Enabled = aws.Bool(d.Get("enabled").(bool)) + requestUpdate = true + } + + if d.HasChange("sns_topic_arn") { + req.SnsTopicArn = aws.String(d.Get("sns_topic_arn").(string)) + requestUpdate = true + } + + if d.HasChange("source_type") { + req.SourceType = aws.String(d.Get("source_type").(string)) + requestUpdate = true + } + + log.Printf("[DEBUG] Send Neptune Event Subscription modification request: %#v", requestUpdate) + if requestUpdate { + log.Printf("[DEBUG] Neptune Event Subscription modification request: %#v", req) + _, err := conn.ModifyEventSubscription(req) + if err != nil { + return fmt.Errorf("Modifying Neptune Event Subscription %s failed: %s", d.Id(), err) + } + + log.Println("[INFO] Waiting for Neptune Event Subscription modification to finish") + + stateConf := &resource.StateChangeConf{ + Pending: []string{"modifying"}, + Target: []string{"active"}, + Refresh: resourceAwsNeptuneEventSubscriptionRefreshFunc(d.Id(), conn), + Timeout: d.Timeout(schema.TimeoutUpdate), + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + // Wait, catching any errors + _, err = stateConf.WaitForState() + if err != nil { + return err + } + d.SetPartial("event_categories") + d.SetPartial("enabled") + d.SetPartial("sns_topic_arn") + d.SetPartial("source_type") + } + + if err := setTagsNeptune(conn, d, d.Get("arn").(string)); err != nil { + return err + } else { + d.SetPartial("tags") + } + + if d.HasChange("source_ids") { + o, n := d.GetChange("source_ids") + if o == nil { + o = new(schema.Set) + } + if n == nil { + n = new(schema.Set) + } + + os := o.(*schema.Set) + ns := n.(*schema.Set) + remove := expandStringList(os.Difference(ns).List()) + add := expandStringList(ns.Difference(os).List()) + + if len(remove) > 0 { + for _, removing := range remove { + log.Printf("[INFO] Removing %s as a Source Identifier from %q", *removing, d.Id()) + _, err := conn.RemoveSourceIdentifierFromSubscription(&neptune.RemoveSourceIdentifierFromSubscriptionInput{ + SourceIdentifier: removing, + SubscriptionName: aws.String(d.Id()), + }) + if err != nil { + return err + } + } + } + + if len(add) > 0 { + for _, adding := range add { + log.Printf("[INFO] Adding %s as a Source Identifier to %q", *adding, d.Id()) + _, err := conn.AddSourceIdentifierToSubscription(&neptune.AddSourceIdentifierToSubscriptionInput{ + SourceIdentifier: adding, + SubscriptionName: aws.String(d.Id()), + }) + if err != nil { + return err + } + } + } + d.SetPartial("source_ids") + } + + d.Partial(false) + + return resourceAwsNeptuneEventSubscriptionRead(d, meta) +} + +func resourceAwsNeptuneEventSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).neptuneconn + deleteOpts := neptune.DeleteEventSubscriptionInput{ + SubscriptionName: aws.String(d.Id()), + } + + if _, err := conn.DeleteEventSubscription(&deleteOpts); err != nil { + if isAWSErr(err, neptune.ErrCodeSubscriptionNotFoundFault, "") { + log.Printf("[WARN] Neptune Event Subscription %s not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("Error deleting Neptune Event Subscription %s: %s", d.Id(), err) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"deleting"}, + Target: []string{}, + Refresh: resourceAwsNeptuneEventSubscriptionRefreshFunc(d.Id(), conn), + Timeout: d.Timeout(schema.TimeoutDelete), + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error deleting Neptune Event Subscription %s: %s", d.Id(), err) + } + + return nil +} + +func resourceAwsNeptuneEventSubscriptionRefreshFunc(name string, conn *neptune.Neptune) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + sub, err := resourceAwsNeptuneEventSubscriptionRetrieve(name, conn) + + if err != nil { + log.Printf("Error on retrieving Neptune Event Subscription when waiting: %s", err) + return nil, "", err + } + + if sub == nil { + return nil, "", nil + } + + if sub.Status != nil { + log.Printf("[DEBUG] Neptune Event Subscription status for %s: %s", name, aws.StringValue(sub.Status)) + } + + return sub, aws.StringValue(sub.Status), nil + } +} + +func resourceAwsNeptuneEventSubscriptionRetrieve(name string, conn *neptune.Neptune) (*neptune.EventSubscription, error) { + + request := &neptune.DescribeEventSubscriptionsInput{ + SubscriptionName: aws.String(name), + } + + describeResp, err := conn.DescribeEventSubscriptions(request) + if err != nil { + if isAWSErr(err, neptune.ErrCodeSubscriptionNotFoundFault, "") { + log.Printf("[DEBUG] Neptune Event Subscription (%s) not found", name) + return nil, nil + } + return nil, err + } + + if len(describeResp.EventSubscriptionsList) != 1 || + aws.StringValue(describeResp.EventSubscriptionsList[0].CustSubscriptionId) != name { + return nil, nil + } + + return describeResp.EventSubscriptionsList[0], nil +} diff --git a/aws/resource_aws_neptune_event_subscription_test.go b/aws/resource_aws_neptune_event_subscription_test.go new file mode 100644 index 00000000000..45cb0d632d1 --- /dev/null +++ b/aws/resource_aws_neptune_event_subscription_test.go @@ -0,0 +1,351 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/neptune" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSNeptuneEventSubscription_basic(t *testing.T) { + var v neptune.EventSubscription + rInt := acctest.RandInt() + rName := fmt.Sprintf("tf-acc-test-neptune-event-subs-%d", rInt) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSNeptuneEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSNeptuneEventSubscriptionConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSNeptuneEventSubscriptionExists("aws_neptune_event_subscription.bar", &v), + resource.TestMatchResourceAttr("aws_neptune_event_subscription.bar", "arn", regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:rds:[^:]+:[^:]+:es:%s$", rName))), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "enabled", "true"), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "source_type", "db-instance"), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "name", rName), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "tags.%", "1"), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "tags.Name", "tf-acc-test"), + ), + }, + { + Config: testAccAWSNeptuneEventSubscriptionConfigUpdate(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSNeptuneEventSubscriptionExists("aws_neptune_event_subscription.bar", &v), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "enabled", "false"), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "source_type", "db-parameter-group"), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "tags.%", "1"), + resource.TestCheckResourceAttr("aws_neptune_event_subscription.bar", "tags.Name", "tf-acc-test1"), + ), + }, + }, + }) +} + +func TestAccAWSNeptuneEventSubscription_withPrefix(t *testing.T) { + var v neptune.EventSubscription + rInt := acctest.RandInt() + startsWithPrefix := regexp.MustCompile("^tf-acc-test-neptune-event-subs-") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSNeptuneEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSNeptuneEventSubscriptionConfigWithPrefix(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSNeptuneEventSubscriptionExists("aws_neptune_event_subscription.bar", &v), + resource.TestMatchResourceAttr( + "aws_neptune_event_subscription.bar", "name", startsWithPrefix), + ), + }, + }, + }) +} + +func TestAccAWSNeptuneEventSubscription_withSourceIds(t *testing.T) { + var v neptune.EventSubscription + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSNeptuneEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSNeptuneEventSubscriptionConfigWithSourceIds(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSNeptuneEventSubscriptionExists("aws_neptune_event_subscription.bar", &v), + resource.TestCheckResourceAttr( + "aws_neptune_event_subscription.bar", "source_type", "db-parameter-group"), + resource.TestCheckResourceAttr( + "aws_neptune_event_subscription.bar", "source_ids.#", "1"), + ), + }, + { + Config: testAccAWSNeptuneEventSubscriptionConfigUpdateSourceIds(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSNeptuneEventSubscriptionExists("aws_neptune_event_subscription.bar", &v), + resource.TestCheckResourceAttr( + "aws_neptune_event_subscription.bar", "source_type", "db-parameter-group"), + resource.TestCheckResourceAttr( + "aws_neptune_event_subscription.bar", "source_ids.#", "2"), + ), + }, + }, + }) +} + +func TestAccAWSNeptuneEventSubscription_withCategories(t *testing.T) { + var v neptune.EventSubscription + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSNeptuneEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSNeptuneEventSubscriptionConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSNeptuneEventSubscriptionExists("aws_neptune_event_subscription.bar", &v), + resource.TestCheckResourceAttr( + "aws_neptune_event_subscription.bar", "source_type", "db-instance"), + resource.TestCheckResourceAttr( + "aws_neptune_event_subscription.bar", "event_categories.#", "5"), + ), + }, + { + Config: testAccAWSNeptuneEventSubscriptionConfigUpdateCategories(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSNeptuneEventSubscriptionExists("aws_neptune_event_subscription.bar", &v), + resource.TestCheckResourceAttr( + "aws_neptune_event_subscription.bar", "source_type", "db-instance"), + resource.TestCheckResourceAttr( + "aws_neptune_event_subscription.bar", "event_categories.#", "1"), + ), + }, + }, + }) +} + +func testAccCheckAWSNeptuneEventSubscriptionExists(n string, v *neptune.EventSubscription) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Neptune Event Subscription is set") + } + + conn := testAccProvider.Meta().(*AWSClient).neptuneconn + + opts := neptune.DescribeEventSubscriptionsInput{ + SubscriptionName: aws.String(rs.Primary.ID), + } + + resp, err := conn.DescribeEventSubscriptions(&opts) + + if err != nil { + return err + } + + if len(resp.EventSubscriptionsList) != 1 || + aws.StringValue(resp.EventSubscriptionsList[0].CustSubscriptionId) != rs.Primary.ID { + return fmt.Errorf("Neptune Event Subscription not found") + } + + *v = *resp.EventSubscriptionsList[0] + return nil + } +} + +func testAccCheckAWSNeptuneEventSubscriptionDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).neptuneconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_neptune_event_subscription" { + continue + } + + var err error + resp, err := conn.DescribeEventSubscriptions( + &neptune.DescribeEventSubscriptionsInput{ + SubscriptionName: aws.String(rs.Primary.ID), + }) + + if ae, ok := err.(awserr.Error); ok && ae.Code() == "SubscriptionNotFound" { + continue + } + + if err == nil { + if len(resp.EventSubscriptionsList) != 0 && + aws.StringValue(resp.EventSubscriptionsList[0].CustSubscriptionId) == rs.Primary.ID { + return fmt.Errorf("Event Subscription still exists") + } + } + + newerr, ok := err.(awserr.Error) + if !ok { + return err + } + if newerr.Code() != "SubscriptionNotFound" { + return err + } + } + + return nil +} + +func testAccAWSNeptuneEventSubscriptionConfig(rInt int) string { + return fmt.Sprintf(` +resource "aws_sns_topic" "aws_sns_topic" { + name = "tf-acc-test-neptune-event-subs-sns-topic-%d" +} + +resource "aws_neptune_event_subscription" "bar" { + name = "tf-acc-test-neptune-event-subs-%d" + sns_topic_arn = "${aws_sns_topic.aws_sns_topic.arn}" + source_type = "db-instance" + event_categories = [ + "availability", + "backup", + "creation", + "deletion", + "maintenance" + ] + tags { + Name = "tf-acc-test" + } +}`, rInt, rInt) +} + +func testAccAWSNeptuneEventSubscriptionConfigUpdate(rInt int) string { + return fmt.Sprintf(` +resource "aws_sns_topic" "aws_sns_topic" { + name = "tf-acc-test-neptune-event-subs-sns-topic-%d" +} + +resource "aws_neptune_event_subscription" "bar" { + name = "tf-acc-test-neptune-event-subs-%d" + sns_topic_arn = "${aws_sns_topic.aws_sns_topic.arn}" + enabled = false + source_type = "db-parameter-group" + event_categories = [ + "configuration change" + ] + tags { + Name = "tf-acc-test1" + } +}`, rInt, rInt) +} + +func testAccAWSNeptuneEventSubscriptionConfigWithPrefix(rInt int) string { + return fmt.Sprintf(` +resource "aws_sns_topic" "aws_sns_topic" { + name = "tf-acc-test-neptune-event-subs-sns-topic-%d" +} + +resource "aws_neptune_event_subscription" "bar" { + name_prefix = "tf-acc-test-neptune-event-subs-" + sns_topic_arn = "${aws_sns_topic.aws_sns_topic.arn}" + source_type = "db-instance" + event_categories = [ + "availability", + "backup", + "creation", + "deletion", + "maintenance" + ] + tags { + Name = "tf-acc-test" + } +}`, rInt) +} + +func testAccAWSNeptuneEventSubscriptionConfigWithSourceIds(rInt int) string { + return fmt.Sprintf(` +resource "aws_sns_topic" "aws_sns_topic" { + name = "tf-acc-test-neptune-event-subs-sns-topic-%d" +} + +resource "aws_neptune_parameter_group" "bar" { + name = "neptune-parameter-group-event-%d" + family = "neptune1" + description = "Test parameter group for terraform" +} + +resource "aws_neptune_event_subscription" "bar" { + name = "tf-acc-test-neptune-event-subs-with-ids-%d" + sns_topic_arn = "${aws_sns_topic.aws_sns_topic.arn}" + source_type = "db-parameter-group" + source_ids = ["${aws_neptune_parameter_group.bar.id}"] + event_categories = [ + "configuration change" + ] + tags { + Name = "tf-acc-test" + } +}`, rInt, rInt, rInt) +} + +func testAccAWSNeptuneEventSubscriptionConfigUpdateSourceIds(rInt int) string { + return fmt.Sprintf(` + resource "aws_sns_topic" "aws_sns_topic" { + name = "tf-acc-test-neptune-event-subs-sns-topic-%d" + } + + resource "aws_neptune_parameter_group" "bar" { + name = "neptune-parameter-group-event-%d" + family = "neptune1" + description = "Test parameter group for terraform" + } + + resource "aws_neptune_parameter_group" "foo" { + name = "neptune-parameter-group-event-2-%d" + family = "neptune1" + description = "Test parameter group for terraform" + } + + resource "aws_neptune_event_subscription" "bar" { + name = "tf-acc-test-neptune-event-subs-with-ids-%d" + sns_topic_arn = "${aws_sns_topic.aws_sns_topic.arn}" + source_type = "db-parameter-group" + source_ids = ["${aws_neptune_parameter_group.bar.id}","${aws_neptune_parameter_group.foo.id}"] + event_categories = [ + "configuration change" + ] + tags { + Name = "tf-acc-test" + } + }`, rInt, rInt, rInt, rInt) +} + +func testAccAWSNeptuneEventSubscriptionConfigUpdateCategories(rInt int) string { + return fmt.Sprintf(` +resource "aws_sns_topic" "aws_sns_topic" { + name = "tf-acc-test-neptune-event-subs-sns-topic-%d" +} + +resource "aws_neptune_event_subscription" "bar" { + name = "tf-acc-test-neptune-event-subs-%d" + sns_topic_arn = "${aws_sns_topic.aws_sns_topic.arn}" + source_type = "db-instance" + event_categories = [ + "availability", + ] + tags { + Name = "tf-acc-test" + } +}`, rInt, rInt) +} diff --git a/aws/validators.go b/aws/validators.go index 3e2128391df..ba3d32d326f 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -1946,3 +1946,30 @@ func validateNeptuneParamGroupNamePrefix(v interface{}, k string) (ws []string, } return } + +func validateNeptuneEventSubscriptionName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only alphanumeric characters and hyphens allowed in %q", k)) + } + if len(value) > 255 { + errors = append(errors, fmt.Errorf( + "%q cannot be greater than 255 characters", k)) + } + return +} + +func validateNeptuneEventSubscriptionNamePrefix(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only alphanumeric characters and hyphens allowed in %q", k)) + } + prefixMaxLength := 255 - resource.UniqueIDSuffixLength + if len(value) > prefixMaxLength { + errors = append(errors, fmt.Errorf( + "%q cannot be greater than %d characters", k, prefixMaxLength)) + } + return +} diff --git a/website/aws.erb b/website/aws.erb index 029e1ace0eb..71337578a6a 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1725,6 +1725,10 @@ aws_neptune_cluster_instance + > + aws_neptune_event_subscription + + diff --git a/website/docs/r/neptune_event_subscription.html.markdown b/website/docs/r/neptune_event_subscription.html.markdown new file mode 100644 index 00000000000..d036ee528b7 --- /dev/null +++ b/website/docs/r/neptune_event_subscription.html.markdown @@ -0,0 +1,92 @@ +--- +layout: "aws" +page_title: "AWS: aws_neptune_event_subscription" +sidebar_current: "docs-aws-resource-neptune-event-subscription" +description: |- + Provides a Neptune event subscription resource. +--- + +# aws_neptune_event_subscription + +## Example Usage + +```hcl +resource "aws_neptune_cluster" "default" { + cluster_identifier = "neptune-cluster-demo" + engine = "neptune" + backup_retention_period = 5 + preferred_backup_window = "07:00-09:00" + skip_final_snapshot = true + iam_database_authentication_enabled = "true" + apply_immediately = "true" +} + +resource "aws_neptune_cluster_instance" "example" { + count = 1 + cluster_identifier = "${aws_neptune_cluster.default.id}" + engine = "neptune" + instance_class = "db.r4.large" + apply_immediately = "true" +} + +resource "aws_sns_topic" "default" { + name = "neptune-events" +} + +resource "aws_neptune_event_subscription" "default" { + name = "neptune-event-sub" + sns_topic_arn = "${aws_sns_topic.default.arn}" + + source_type = "db-instance" + source_ids = ["${aws_neptune_cluster_instance.example.id}"] + + event_categories = [ + "maintenance", + "availability", + "creation", + "backup", + "restoration", + "recovery", + "deletion", + "failover", + "failure", + "notification", + "configuration change", + "read replica", + ] + + tags { + "env" = "test" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `enabled` - (Optional) A boolean flag to enable/disable the subscription. Defaults to true. +* `event_categories` - (Optional) A list of event categories for a `source_type` that you want to subscribe to. Run `aws neptune describe-event-categories` to find all the event categories. +* `name` - (Optional) The name of the Neptune event subscription. By default generated by Terraform. +* `name_prefix` - (Optional) The name of the Neptune event subscription. Conflicts with `name`. +* `sns_topic_arn` - (Required) The ARN of the SNS topic to send events to. +* `source_ids` - (Optional) A list of identifiers of the event sources for which events will be returned. If not specified, then all sources are included in the response. If specified, a `source_type` must also be specified. +* `source_type` - (Optional) The type of source that will be generating the events. Valid options are `db-instance`, `db-security-group`, `db-parameter-group`, `db-snapshot`, `db-cluster` or `db-cluster-snapshot`. If not set, all sources will be subscribed to. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes + +The following additional atttributes are provided: + +* `id` - The name of the Neptune event notification subscription. +* `arn` - The Amazon Resource Name of the Neptune event notification subscription. +* `customer_aws_id` - The AWS customer account associated with the Neptune event notification subscription. + +## Timeouts + +`aws_neptune_event_subscription` provides the following [Timeouts](/docs/configuration/resources.html#timeouts) +configuration options: + +- `create` - (Default `40m`) How long to wait for creating event subscription to become available. +- `delete` - (Default `40m`) How long to wait for deleting event subscription to become fully deleted. +- `update` - (Default `40m`) How long to wait for updating event subscription to complete updates. \ No newline at end of file