Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/aws_sns_topic_subscription: add firehose protocol support #17307

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/17307.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-notes:enhancement
resource/sns_topic_subscription: Add `firehose` protocol option with `subscription_role_arn` attribute
```
39 changes: 39 additions & 0 deletions aws/resource_aws_sns_topic_subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func resourceAwsSnsTopicSubscription() *schema.Resource {
"lambda",
"sms",
"sqs",
"firehose",
}, true),
},
"endpoint": {
Expand Down Expand Up @@ -98,6 +99,10 @@ func resourceAwsSnsTopicSubscription() *schema.Resource {
return json
},
},
"subscription_role_arn": {
Type: schema.TypeString,
Optional: true,
},
},
}
}
Expand Down Expand Up @@ -153,6 +158,21 @@ func resourceAwsSnsTopicSubscriptionUpdate(d *schema.ResourceData, meta interfac
}
}

if d.HasChange("subscription_role_arn") {
protocol := d.Get("protocol").(string)
subscription_role_arn := d.Get("subscription_role_arn").(string)
if strings.Contains(protocol, "firehose") && subscription_role_arn == "" {
return fmt.Errorf("Protocol firehose must contain subscription_role_arn!")
}
if !strings.Contains(protocol, "firehose") && subscription_role_arn != "" {
return fmt.Errorf("Only protocol firehose supports subscription_role_arn!")
}

if err := snsSubscriptionAttributeUpdate(snsconn, d.Id(), "SubscriptionRoleArn", subscription_role_arn); err != nil {
return err
}
}

if d.HasChange("redrive_policy") {
if err := snsSubscriptionAttributeUpdate(snsconn, d.Id(), "RedrivePolicy", d.Get("redrive_policy").(string)); err != nil {
return err
Expand Down Expand Up @@ -197,6 +217,11 @@ func resourceAwsSnsTopicSubscriptionRead(d *schema.ResourceData, meta interface{
}

d.Set("redrive_policy", attributeOutput.Attributes["RedrivePolicy"])
d.Set("subscription_role_arn", "")
if v, ok := attributeOutput.Attributes["SubscriptionRoleArn"]; ok {
d.Set("subscription_role_arn", aws.StringValue(v))
}

d.Set("topic_arn", attributeOutput.Attributes["TopicArn"])

return nil
Expand All @@ -219,6 +244,7 @@ func getResourceAttributes(d *schema.ResourceData) (output map[string]*string) {
filter_policy := d.Get("filter_policy").(string)
raw_message_delivery := d.Get("raw_message_delivery").(bool)
redrive_policy := d.Get("redrive_policy").(string)
subscription_role_arn := d.Get("subscription_role_arn").(string)

// Collect attributes if available
attributes := map[string]*string{}
Expand All @@ -235,6 +261,10 @@ func getResourceAttributes(d *schema.ResourceData) (output map[string]*string) {
attributes["RawMessageDelivery"] = aws.String(fmt.Sprintf("%t", raw_message_delivery))
}

if subscription_role_arn != "" {
attributes["SubscriptionRoleArn"] = aws.String(subscription_role_arn)
}

if redrive_policy != "" {
attributes["RedrivePolicy"] = aws.String(redrive_policy)
}
Expand All @@ -248,12 +278,21 @@ func subscribeToSNSTopic(d *schema.ResourceData, snsconn *sns.SNS) (output *sns.
topic_arn := d.Get("topic_arn").(string)
endpoint_auto_confirms := d.Get("endpoint_auto_confirms").(bool)
confirmation_timeout_in_minutes := d.Get("confirmation_timeout_in_minutes").(int)
subscription_role_arn := d.Get("subscription_role_arn").(string)
attributes := getResourceAttributes(d)

if strings.Contains(protocol, "http") && !endpoint_auto_confirms {
return nil, fmt.Errorf("Protocol http/https is only supported for endpoints which auto confirms!")
}

if strings.Contains(protocol, "firehose") && subscription_role_arn == "" {
return nil, fmt.Errorf("Protocol firehose must contain subscription_role_arn!")
}

if !strings.Contains(protocol, "firehose") && subscription_role_arn != "" {
return nil, fmt.Errorf("Only protocol firehose supports subscription_role_arn!")
}

log.Printf("[DEBUG] SNS create topic subscription: %s (%s) @ '%s'", endpoint, protocol, topic_arn)

req := &sns.SubscribeInput{
Expand Down
94 changes: 94 additions & 0 deletions aws/resource_aws_sns_topic_subscription_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,51 @@ func TestAccAWSSNSTopicSubscription_autoConfirmingSecuredEndpoint(t *testing.T)
})
}

func TestAccAWSSNSTopicSubscription_firehose(t *testing.T) {
attributes := make(map[string]string)
resourceName := "aws_sns_topic_subscription.test_subscription"
ri := acctest.RandInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSNSTopicSubscriptionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSSNSTopicSubscriptionConfig_firehose(ri, 1),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSNSTopicSubscriptionExists(resourceName, attributes),
testAccMatchResourceAttrRegionalARN(resourceName, "arn", "sns", regexp.MustCompile(fmt.Sprintf("terraform-test-topic-%d:.+", ri))),
resource.TestCheckResourceAttr(resourceName, "delivery_policy", ""),
resource.TestCheckResourceAttrPair(resourceName, "endpoint", "aws_kinesis_firehose_delivery_stream.test_stream", "arn"),
resource.TestCheckResourceAttr(resourceName, "filter_policy", ""),
resource.TestCheckResourceAttr(resourceName, "protocol", "firehose"),
resource.TestCheckResourceAttr(resourceName, "raw_message_delivery", "false"),
resource.TestCheckResourceAttrPair(resourceName, "topic_arn", "aws_sns_topic.test_topic", "arn"),
resource.TestCheckResourceAttrPair(resourceName, "subscription_role_arn", "aws_iam_role.firehose_role", "arn"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"confirmation_timeout_in_minutes",
"endpoint_auto_confirms",
},
},
// Test attribute update
{
Config: testAccAWSSNSTopicSubscriptionConfig_firehose(ri, 2),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSNSTopicSubscriptionExists(resourceName, attributes),
resource.TestCheckResourceAttrPair(resourceName, "subscription_role_arn", "aws_iam_role.firehose_role", "arn"),
),
},
},
})
}

func testAccCheckAWSSNSTopicSubscriptionDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).snsconn

Expand Down Expand Up @@ -903,3 +948,52 @@ resource "aws_sns_topic_subscription" "test_subscription" {
}
`, i, i, i, i, i, i, i, i, i, username, password, username, password)
}

func testAccAWSSNSTopicSubscriptionConfig_firehose(i, n int) string {
return fmt.Sprintf(`
resource "aws_sns_topic" "test_topic" {
name = "terraform-test-topic-%d"
}

resource "aws_sns_topic_subscription" "test_subscription" {
endpoint = aws_kinesis_firehose_delivery_stream.test_stream.arn
protocol = "firehose"
topic_arn = aws_sns_topic.test_topic.arn
subscription_role_arn = aws_iam_role.firehose_role.arn
}
resource "aws_s3_bucket" "bucket" {
bucket = "tf-test-bucket-%d"
acl = "private"
}

resource "aws_iam_role" "firehose_role" {
name = "tf-test-firehose-role-%d-%d"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": ["sns.amazonaws.com","firehose.amazonaws.com"]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_kinesis_firehose_delivery_stream" "test_stream" {
name = "tf-test-firehose-stream-%d"
destination = "s3"

s3_configuration {
role_arn = aws_iam_role.firehose_role.arn
bucket_arn = aws_s3_bucket.bucket.arn
}
}
`, i, i, n, i, i)
}
6 changes: 5 additions & 1 deletion website/docs/r/sns_topic_subscription.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,15 @@ resource "aws_sns_topic_subscription" "sns-topic" {
The following arguments are supported:

* `topic_arn` - (Required) The ARN of the SNS topic to subscribe to
* `protocol` - (Required) The protocol to use. The possible values for this are: `sqs`, `sms`, `lambda`, `application`. (`http` or `https` are partially supported, see below) (`email` is an option but is unsupported, see below).
* `protocol` - (Required) The protocol to use. The possible values for this are: `sqs`, `sms`, `lambda`, `application`, `firehose`. (`http` or `https` are partially supported, see below) (`email` is an option but is unsupported, see below).
* `endpoint` - (Required) The endpoint to send data to, the contents will vary with the protocol. (see below for more information)
* `endpoint_auto_confirms` - (Optional) Boolean indicating whether the end point is capable of [auto confirming subscription](http://docs.aws.amazon.com/sns/latest/dg/SendMessageToHttp.html#SendMessageToHttp.prepare) e.g., PagerDuty (default is false)
* `confirmation_timeout_in_minutes` - (Optional) Integer indicating number of minutes to wait in retying mode for fetching subscription arn before marking it as failure. Only applicable for http and https protocols (default is 1 minute).
* `raw_message_delivery` - (Optional) Boolean indicating whether or not to enable raw message delivery (the original message is directly passed, not wrapped in JSON with the original message in the message property) (default is false).
* `filter_policy` - (Optional) JSON String with the filter policy that will be used in the subscription to filter messages seen by the target resource. Refer to the [SNS docs](https://docs.aws.amazon.com/sns/latest/dg/message-filtering.html) for more details.
* `delivery_policy` - (Optional) JSON String with the delivery policy (retries, backoff, etc.) that will be used in the subscription - this only applies to HTTP/S subscriptions. Refer to the [SNS docs](https://docs.aws.amazon.com/sns/latest/dg/DeliveryPolicies.html) for more details.
* `redrive_policy` - (Optional) JSON String with the redrive policy that will be used in the subscription. Refer to the [SNS docs](https://docs.aws.amazon.com/sns/latest/dg/sns-dead-letter-queues.html#how-messages-moved-into-dead-letter-queue) for more details.
* `subscription_role_arn` - (Optional) Required ARN of the IAM role to publish to Kinesis Data Firehose delivery stream. Require for `firehose` protocol. Refer to [SNS docs](https://docs.aws.amazon.com/sns/latest/dg/sns-firehose-as-subscriber.html).

### Protocols supported

Expand All @@ -248,6 +249,7 @@ Supported SNS protocols include:
* `sqs` -- delivery of JSON-encoded message to an Amazon SQS queue
* `application` -- delivery of JSON-encoded message to an EndpointArn for a mobile app and device
* `sms` -- delivery text message
* `firehose` -- delivery of JSON-encoded message to an Amazon Kinesis Data Firehose delivery stream

Partially supported SNS protocols include:

Expand All @@ -269,6 +271,8 @@ Endpoints have different format requirements according to the protocol that is c

* SQS endpoints come in the form of the SQS queue's ARN (not the URL of the queue) e.g: `arn:aws:sqs:us-west-2:432981146916:terraform-queue-too`
* Application endpoints are also the endpoint ARN for the mobile app and device.
* Firehose endpoints are in the form of Firehose ARN e.g:
`arn:aws:firehose:us-east-1:123456789012:deliverystream/ticketUploadStream`

## Attributes Reference

Expand Down