From b50aec8a9dfde79124b320bbfc8f37d9f88f81ea Mon Sep 17 00:00:00 2001 From: Laurent Brucher Date: Wed, 21 Mar 2018 10:19:15 +0100 Subject: [PATCH 1/3] Add a resource to allow setting SNS SMS preferences --- aws/provider.go | 1 + aws/resource_aws_sns_sms_preferences.go | 159 +++++++++++++++++ aws/resource_aws_sns_sms_preferences_test.go | 160 ++++++++++++++++++ .../docs/r/sns_sms_peferences.html.markdown | 30 ++++ 4 files changed, 350 insertions(+) create mode 100644 aws/resource_aws_sns_sms_preferences.go create mode 100644 aws/resource_aws_sns_sms_preferences_test.go create mode 100644 website/docs/r/sns_sms_peferences.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 964c86e3bbcb..6cb6a386767c 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -519,6 +519,7 @@ func Provider() terraform.ResourceProvider { "aws_sqs_queue_policy": resourceAwsSqsQueuePolicy(), "aws_snapshot_create_volume_permission": resourceAwsSnapshotCreateVolumePermission(), "aws_sns_platform_application": resourceAwsSnsPlatformApplication(), + "aws_sns_sms_preferences": resourceAwsSnsSmsPreferences(), "aws_sns_topic": resourceAwsSnsTopic(), "aws_sns_topic_policy": resourceAwsSnsTopicPolicy(), "aws_sns_topic_subscription": resourceAwsSnsTopicSubscription(), diff --git a/aws/resource_aws_sns_sms_preferences.go b/aws/resource_aws_sns_sms_preferences.go new file mode 100644 index 000000000000..6863196767eb --- /dev/null +++ b/aws/resource_aws_sns_sms_preferences.go @@ -0,0 +1,159 @@ +package aws + +import ( + "fmt" + "log" + "strconv" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/sns" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsSnsSmsPreferences() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSnsSmsPreferencesSet, + Read: resourceAwsSnsSmsPreferencesGet, + Update: resourceAwsSnsSmsPreferencesSet, + Delete: resourceAwsSnsSmsPreferencesDelete, + + Schema: map[string]*schema.Schema{ + "monthly_spend_limit": { + Type: schema.TypeString, + Optional: true, + }, + + "delivery_status_iam_role_arn": { + Type: schema.TypeString, + Optional: true, + }, + + "delivery_status_success_sampling_rate": { + Type: schema.TypeString, + Optional: true, + }, + + "default_sender_id": { + Type: schema.TypeString, + Optional: true, + }, + + "default_sms_type": { + Type: schema.TypeString, + Optional: true, + }, + + "usage_report_s3_bucket": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +const resourceId = "aws_sns_sms_id" + +var SMSAttributeMap = map[string]string{ + "monthly_spend_limit": "MonthlySpendLimit", + "delivery_status_iam_role_arn": "DeliveryStatusIAMRole", + "delivery_status_success_sampling_rate": "DeliveryStatusSuccessSamplingRate", + "default_sender_id": "DefaultSenderID", + "default_sms_type": "DefaultSMSType", + "usage_report_s3_bucket": "UsageReportS3Bucket", +} + +var SMSAttributeDefaultValues = map[string]string{ + "monthly_spend_limit": "", + "delivery_status_iam_role_arn": "", + "delivery_status_success_sampling_rate": "", + "default_sender_id": "", + "default_sms_type": "", + "usage_report_s3_bucket": "", +} + +func resourceAwsSnsSmsPreferencesSet(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + log.Printf("[DEBUG] SNS Set SMS preferences") + + monthlySpendLimit := d.Get("monthly_spend_limit").(string) + monthlySpendLimitInt, _ := strconv.Atoi(monthlySpendLimit) + deliveryStatusIamRoleArn := d.Get("delivery_status_iam_role_arn").(string) + deliveryStatusSuccessSamplingRate := d.Get("delivery_status_success_sampling_rate").(string) + deliveryStatusSuccessSamplingRateInt, _ := strconv.Atoi(deliveryStatusSuccessSamplingRate) + defaultSenderId := d.Get("default_sender_id").(string) + defaultSmsType := d.Get("default_sms_type").(string) + usageReportS3Bucket := d.Get("usage_report_s3_bucket").(string) + + // Validation + if monthlySpendLimitInt < 0 { + return fmt.Errorf("Error setting SMS preferences: monthly spend limit value [%d] must be >= 0!", monthlySpendLimitInt) + } + if deliveryStatusSuccessSamplingRateInt < 0 || deliveryStatusSuccessSamplingRateInt > 100 { + return fmt.Errorf("Error setting SMS preferences: default percentage of success to sample value [%d] must be between 0 and 100!", deliveryStatusSuccessSamplingRateInt) + } + if defaultSmsType != "" && defaultSmsType != "Promotional" && defaultSmsType != "Transactional" { + return fmt.Errorf("Error setting SMS preferences: default SMS type value [%s] is invalid!", defaultSmsType) + } + + // Set preferences + params := &sns.SetSMSAttributesInput{ + Attributes: map[string]*string{ + "MonthlySpendLimit": aws.String(monthlySpendLimit), + "DeliveryStatusIAMRole": aws.String(deliveryStatusIamRoleArn), + "DeliveryStatusSuccessSamplingRate": aws.String(deliveryStatusSuccessSamplingRate), + "DefaultSenderID": aws.String(defaultSenderId), + "DefaultSMSType": aws.String(defaultSmsType), + "UsageReportS3Bucket": aws.String(usageReportS3Bucket), + }, + } + + if _, err := snsconn.SetSMSAttributes(params); err != nil { + return fmt.Errorf("Error setting SMS preferences: %s", err) + } + + d.SetId(resourceId) + return nil +} + +func resourceAwsSnsSmsPreferencesGet(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + // Fetch ALL attributes + attrs, err := snsconn.GetSMSAttributes(&sns.GetSMSAttributesInput{}) + if err != nil { + return err + } + + // Reset with default values first + for tfAttrName, defValue := range SMSAttributeDefaultValues { + d.Set(tfAttrName, defValue) + } + + // Apply existing settings + if attrs.Attributes != nil && len(attrs.Attributes) > 0 { + attrmap := attrs.Attributes + for tfAttrName, snsAttrName := range SMSAttributeMap { + d.Set(tfAttrName, attrmap[snsAttrName]) + } + } + + return nil +} + +func resourceAwsSnsSmsPreferencesDelete(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + // Reset the attributes to their default value + attrs := map[string]*string{} + for tfAttrName, defValue := range SMSAttributeDefaultValues { + attrs[SMSAttributeMap[tfAttrName]] = &defValue + } + + params := &sns.SetSMSAttributesInput{Attributes: attrs} + if _, err := snsconn.SetSMSAttributes(params); err != nil { + return fmt.Errorf("Error resetting SMS preferences: %s", err) + } + + return nil +} diff --git a/aws/resource_aws_sns_sms_preferences_test.go b/aws/resource_aws_sns_sms_preferences_test.go new file mode 100644 index 000000000000..10a749eb9eaf --- /dev/null +++ b/aws/resource_aws_sns_sms_preferences_test.go @@ -0,0 +1,160 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSSNSSMSPreferences_empty(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSNSSMSPrefsDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSNSSMSPreferences_empty, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "monthly_spend_limit"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "delivery_status_iam_role_arn"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "delivery_status_success_sampling_rate"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "default_sender_id"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "default_sms_type"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "usage_report_s3_bucket"), + ), + }, + }, + }) +} + +func TestAccAWSSNSSMSPreferences_defaultSMSType(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSNSSMSPrefsDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSNSSMSPreferences_defSMSType, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "monthly_spend_limit"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "delivery_status_iam_role_arn"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "delivery_status_success_sampling_rate"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "default_sender_id"), + resource.TestCheckResourceAttr("aws_sns_sms_preferences.test_pref", "default_sms_type", "Transactional"), + resource.TestCheckNoResourceAttr("aws_sns_sms_preferences.test_pref", "usage_report_s3_bucket"), + ), + }, + }, + }) +} + +func TestAccAWSSNSSMSPreferences_almostAll(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSNSSMSPrefsDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSNSSMSPreferences_almostAll, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aws_sns_sms_preferences.test_pref", "monthly_spend_limit", "1"), + resource.TestCheckResourceAttr("aws_sns_sms_preferences.test_pref", "default_sms_type", "Transactional"), + resource.TestCheckResourceAttr("aws_sns_sms_preferences.test_pref", "usage_report_s3_bucket", "some-bucket"), + ), + }, + }, + }) +} + +func TestAccAWSSNSSMSPreferences_deliveryRole(t *testing.T) { + arnRole := regexp.MustCompile(`^arn:aws:iam::\d+:role/test_smsdelivery_role$`) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSNSSMSPrefsDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSNSSMSPreferences_deliveryRole, + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr("aws_sns_sms_preferences.test_pref", "delivery_status_iam_role_arn", arnRole), + resource.TestCheckResourceAttr("aws_sns_sms_preferences.test_pref", "delivery_status_success_sampling_rate", "75"), + ), + }, + }, + }) +} + +func testAccCheckAWSSNSSMSPrefsDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_sns_sms_preferences" { + continue + } + + return fmt.Errorf("SNS SMS Preferences resource exists when it should be destroyed!") + } + + return nil +} + +const testAccAWSSNSSMSPreferences_empty = ` +resource "aws_sns_sms_preferences" "test_pref" {} +` +const testAccAWSSNSSMSPreferences_defSMSType = ` +resource "aws_sns_sms_preferences" "test_pref" { + default_sms_type = "Transactional" +} +` +const testAccAWSSNSSMSPreferences_almostAll = ` +resource "aws_sns_sms_preferences" "test_pref" { + monthly_spend_limit = "1", + default_sms_type = "Transactional", + usage_report_s3_bucket = "some-bucket", +} +` +const testAccAWSSNSSMSPreferences_deliveryRole = ` +resource "aws_iam_role" "test_smsdelivery_role" { + name = "test_smsdelivery_role" + path = "/" + assume_role_policy = < Date: Wed, 25 Apr 2018 11:54:22 +0200 Subject: [PATCH 2/3] Validation performed using validation functions --- aws/resource_aws_sns_sms_preferences.go | 45 ++++++++++++++----------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/aws/resource_aws_sns_sms_preferences.go b/aws/resource_aws_sns_sms_preferences.go index 6863196767eb..21a1fa9fba12 100644 --- a/aws/resource_aws_sns_sms_preferences.go +++ b/aws/resource_aws_sns_sms_preferences.go @@ -8,8 +8,25 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sns" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) +func validateMonthlySpend(v interface{}, k string) (ws []string, errors []error) { + vInt, _ := strconv.Atoi(v.(string)) + if vInt < 0 { + errors = append(errors, fmt.Errorf("Error setting SMS preferences: monthly spend limit value [%d] must be >= 0!", vInt)) + } + return +} + +func validateDeliverySamplingRate(v interface{}, k string) (ws []string, errors []error) { + vInt, _ := strconv.Atoi(v.(string)) + if vInt < 0 || vInt > 100 { + errors = append(errors, fmt.Errorf("Error setting SMS preferences: default percentage of success to sample value [%d] must be between 0 and 100!", vInt)) + } + return +} + func resourceAwsSnsSmsPreferences() *schema.Resource { return &schema.Resource{ Create: resourceAwsSnsSmsPreferencesSet, @@ -19,8 +36,9 @@ func resourceAwsSnsSmsPreferences() *schema.Resource { Schema: map[string]*schema.Schema{ "monthly_spend_limit": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateMonthlySpend, }, "delivery_status_iam_role_arn": { @@ -29,8 +47,9 @@ func resourceAwsSnsSmsPreferences() *schema.Resource { }, "delivery_status_success_sampling_rate": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateDeliverySamplingRate, }, "default_sender_id": { @@ -39,8 +58,9 @@ func resourceAwsSnsSmsPreferences() *schema.Resource { }, "default_sms_type": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"Promotional", "Transactional"}, false), }, "usage_report_s3_bucket": { @@ -77,25 +97,12 @@ func resourceAwsSnsSmsPreferencesSet(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] SNS Set SMS preferences") monthlySpendLimit := d.Get("monthly_spend_limit").(string) - monthlySpendLimitInt, _ := strconv.Atoi(monthlySpendLimit) deliveryStatusIamRoleArn := d.Get("delivery_status_iam_role_arn").(string) deliveryStatusSuccessSamplingRate := d.Get("delivery_status_success_sampling_rate").(string) - deliveryStatusSuccessSamplingRateInt, _ := strconv.Atoi(deliveryStatusSuccessSamplingRate) defaultSenderId := d.Get("default_sender_id").(string) defaultSmsType := d.Get("default_sms_type").(string) usageReportS3Bucket := d.Get("usage_report_s3_bucket").(string) - // Validation - if monthlySpendLimitInt < 0 { - return fmt.Errorf("Error setting SMS preferences: monthly spend limit value [%d] must be >= 0!", monthlySpendLimitInt) - } - if deliveryStatusSuccessSamplingRateInt < 0 || deliveryStatusSuccessSamplingRateInt > 100 { - return fmt.Errorf("Error setting SMS preferences: default percentage of success to sample value [%d] must be between 0 and 100!", deliveryStatusSuccessSamplingRateInt) - } - if defaultSmsType != "" && defaultSmsType != "Promotional" && defaultSmsType != "Transactional" { - return fmt.Errorf("Error setting SMS preferences: default SMS type value [%s] is invalid!", defaultSmsType) - } - // Set preferences params := &sns.SetSMSAttributesInput{ Attributes: map[string]*string{ From c0eab44d192753c10140d22e9663de468a994fd8 Mon Sep 17 00:00:00 2001 From: Laurent Brucher Date: Wed, 25 Apr 2018 11:54:53 +0200 Subject: [PATCH 3/3] Not exporting smsAttriuteMap nor smsAttributeDefaultValues --- aws/resource_aws_sns_sms_preferences.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_sns_sms_preferences.go b/aws/resource_aws_sns_sms_preferences.go index 21a1fa9fba12..6892ec250bea 100644 --- a/aws/resource_aws_sns_sms_preferences.go +++ b/aws/resource_aws_sns_sms_preferences.go @@ -73,7 +73,7 @@ func resourceAwsSnsSmsPreferences() *schema.Resource { const resourceId = "aws_sns_sms_id" -var SMSAttributeMap = map[string]string{ +var smsAttributeMap = map[string]string{ "monthly_spend_limit": "MonthlySpendLimit", "delivery_status_iam_role_arn": "DeliveryStatusIAMRole", "delivery_status_success_sampling_rate": "DeliveryStatusSuccessSamplingRate", @@ -82,7 +82,7 @@ var SMSAttributeMap = map[string]string{ "usage_report_s3_bucket": "UsageReportS3Bucket", } -var SMSAttributeDefaultValues = map[string]string{ +var smsAttributeDefaultValues = map[string]string{ "monthly_spend_limit": "", "delivery_status_iam_role_arn": "", "delivery_status_success_sampling_rate": "", @@ -133,14 +133,14 @@ func resourceAwsSnsSmsPreferencesGet(d *schema.ResourceData, meta interface{}) e } // Reset with default values first - for tfAttrName, defValue := range SMSAttributeDefaultValues { + for tfAttrName, defValue := range smsAttributeDefaultValues { d.Set(tfAttrName, defValue) } // Apply existing settings if attrs.Attributes != nil && len(attrs.Attributes) > 0 { attrmap := attrs.Attributes - for tfAttrName, snsAttrName := range SMSAttributeMap { + for tfAttrName, snsAttrName := range smsAttributeMap { d.Set(tfAttrName, attrmap[snsAttrName]) } } @@ -153,8 +153,8 @@ func resourceAwsSnsSmsPreferencesDelete(d *schema.ResourceData, meta interface{} // Reset the attributes to their default value attrs := map[string]*string{} - for tfAttrName, defValue := range SMSAttributeDefaultValues { - attrs[SMSAttributeMap[tfAttrName]] = &defValue + for tfAttrName, defValue := range smsAttributeDefaultValues { + attrs[smsAttributeMap[tfAttrName]] = &defValue } params := &sns.SetSMSAttributesInput{Attributes: attrs}