Skip to content

Commit

Permalink
Support SQS queue subscription to sns-standard-topic (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
posquit0 authored Oct 4, 2023
1 parent 15d70b8 commit 0a60585
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 3 deletions.
40 changes: 40 additions & 0 deletions examples/sns-standard-topic-sqs-subscription/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
provider "aws" {
region = "us-east-1"
}


###################################################
# SNS Topic
###################################################

module "topic" {
source = "../../modules/sns-standard-topic"
# source = "tedilabs/messaging/aws//modules/sns-standard-topic"
# version = "~> 0.2.0"

name = "standard-test-sqs"
display_name = "Standard Test SQS"

subscriptions_by_sqs = [
{
name = "example-sqs"
queue = aws_sqs_queue.example.arn

raw_message_delivery_enabled = true
redrive_policy = {
dead_letter_sqs_queue = aws_sqs_queue.dlq.arn
}
filter_policy = {
enabled = true
scope = "ATTRIBUTES"
policy = jsonencode({
"store" = ["example_corp"]
})
}
},
]

tags = {
"project" = "terraform-aws-messaging-examples"
}
}
12 changes: 12 additions & 0 deletions examples/sns-standard-topic-sqs-subscription/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
output "queues" {
description = "The SQS queue."
value = {
"example" = resource.aws_sqs_queue.example.arn
"dlq" = resource.aws_sqs_queue.dlq.arn
}
}

output "topic" {
description = "The SNS topic."
value = module.topic
}
73 changes: 73 additions & 0 deletions examples/sns-standard-topic-sqs-subscription/sqs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
resource "aws_sqs_queue" "example" {
name = "sqs-queue-for-sns-topic"
}

resource "aws_sqs_queue_policy" "example" {
queue_url = aws_sqs_queue.example.id
policy = data.aws_iam_policy_document.sqs_example.json
}

resource "aws_sqs_queue" "dlq" {
name = "sqs-dead-letter-queue-for-sns-topic"
}

resource "aws_sqs_queue_policy" "dlq" {
queue_url = aws_sqs_queue.dlq.id
policy = data.aws_iam_policy_document.sqs_dlq.json
}

data "aws_iam_policy_document" "sqs_example" {
statement {
effect = "Allow"

principals {
type = "AWS"
identifiers = ["*"]
}

actions = [
"SQS:SendMessage",
]

resources = [
aws_sqs_queue.example.arn,
]

condition {
test = "ArnEquals"
variable = "aws:SourceArn"

values = [
module.topic.arn,
]
}
}
}

data "aws_iam_policy_document" "sqs_dlq" {
statement {
effect = "Allow"

principals {
type = "AWS"
identifiers = ["*"]
}

actions = [
"SQS:SendMessage",
]

resources = [
aws_sqs_queue.dlq.arn,
]

condition {
test = "ArnEquals"
variable = "aws:SourceArn"

values = [
module.topic.arn,
]
}
}
}
10 changes: 10 additions & 0 deletions examples/sns-standard-topic-sqs-subscription/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = "~> 1.5"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
2 changes: 2 additions & 0 deletions modules/sns-standard-topic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This module creates following resources.
| [aws_sns_topic_subscription.email](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [aws_sns_topic_subscription.email_json](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [aws_sns_topic_subscription.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [aws_sns_topic_subscription.sqs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |

## Inputs
Expand All @@ -57,6 +58,7 @@ This module creates following resources.
| <a name="input_subscriptions_by_email"></a> [subscriptions\_by\_email](#input\_subscriptions\_by\_email) | (Optional) A configuration for email subscriptions to the SNS topic. Deliver messages to the subscriber via SMTP. Until the subscription is confirmed, AWS does not allow Terraform to delete / unsubscribe the subscription. If you destroy an unconfirmed subscription, Terraform will remove the subscription from its state but the subscription will still exist in AWS. Each block of `subscriptions_by_email` as defined below.<br> (Required) `email` - An email address that can receive notifications from the SNS topic.<br> (Optional) `filter_policy` - The configuration to filter the messages that a subscriber receives. Additions or changes to the filter policy require up to 15 minutes to fully take effect. `filter_policy` as defined below.<br> (Optional) `enabled` - Whether to enable the filter policy. Defaults to `false`.<br> (Optional) `scope` - Determine how the filter policy will be applied to the message.<br> Valid values are `ATTRIBUTES` and `BODY`. Defaults to `ATTRIBUTES`.<br> `ATTRIBUTES` - The filter policy will be applied to the message attributes.<br> `BODY` - The filter policy will be applied to the message body.<br> (Optional) `redrive_policy` - The configuration to send undeliverable messages to a dead-letter queue. `redrive_policy` as defined below.<br> (Optional) `dead_letter_sqs_queue` - The ARN of the SQS queue to which Amazon SNS can send undeliverable messages. | <pre>list(object({<br> email = string<br> filter_policy = optional(object({<br> enabled = optional(bool, false)<br> scope = optional(string, "ATTRIBUTES")<br> policy = optional(string)<br> }), {})<br> redrive_policy = optional(object({<br> dead_letter_sqs_queue = optional(string)<br> }), {})<br> }))</pre> | `[]` | no |
| <a name="input_subscriptions_by_email_json"></a> [subscriptions\_by\_email\_json](#input\_subscriptions\_by\_email\_json) | (Optional) A configuration for JSON-encoded email subscriptions to the SNS topic. Deliver JSON-encoded messages to the subscriber via SMTP. Until the subscription is confirmed, AWS does not allow Terraform to delete / unsubscribe the subscription. If you destroy an unconfirmed subscription, Terraform will remove the subscription from its state but the subscription will still exist in AWS. Each block of `subscriptions_by_email_json` as defined below.<br> (Required) `email` - An email address that can receive notifications from the SNS topic.<br> (Optional) `filter_policy` - The configuration to filter the messages that a subscriber receives. Additions or changes to the filter policy require up to 15 minutes to fully take effect. `filter_policy` as defined below.<br> (Optional) `enabled` - Whether to enable the filter policy. Defaults to `false`.<br> (Optional) `scope` - Determine how the filter policy will be applied to the message.<br> Valid values are `ATTRIBUTES` and `BODY`. Defaults to `ATTRIBUTES`.<br> `ATTRIBUTES` - The filter policy will be applied to the message attributes.<br> `BODY` - The filter policy will be applied to the message body.<br> (Optional) `redrive_policy` - The configuration to send undeliverable messages to a dead-letter queue. `redrive_policy` as defined below.<br> (Optional) `dead_letter_sqs_queue` - The ARN of the SQS queue to which Amazon SNS can send undeliverable messages. | <pre>list(object({<br> email = string<br> filter_policy = optional(object({<br> enabled = optional(bool, false)<br> scope = optional(string, "ATTRIBUTES")<br> policy = optional(string)<br> }), {})<br> redrive_policy = optional(object({<br> dead_letter_sqs_queue = optional(string)<br> }), {})<br> }))</pre> | `[]` | no |
| <a name="input_subscriptions_by_lambda"></a> [subscriptions\_by\_lambda](#input\_subscriptions\_by\_lambda) | (Optional) A configuration for Lambda Function subscriptions to the SNS topic. Deliver JSON-encoded messages to the Lambda function. Each block of `subscriptions_by_lambda` as defined below.<br> (Required) `name` - The name of the subscription to the SNS topic. This value is only used internally within Terraform code.<br> (Required) `function` - The ARN of the AWS Lambda function that can receive notifications from the SNS topic.<br> (Optional) `filter_policy` - The configuration to filter the messages that a subscriber receives. Additions or changes to the filter policy require up to 15 minutes to fully take effect. `filter_policy` as defined below.<br> (Optional) `enabled` - Whether to enable the filter policy. Defaults to `false`.<br> (Optional) `scope` - Determine how the filter policy will be applied to the message.<br> Valid values are `ATTRIBUTES` and `BODY`. Defaults to `ATTRIBUTES`.<br> `ATTRIBUTES` - The filter policy will be applied to the message attributes.<br> `BODY` - The filter policy will be applied to the message body.<br> (Optional) `redrive_policy` - The configuration to send undeliverable messages to a dead-letter queue. `redrive_policy` as defined below.<br> (Optional) `dead_letter_sqs_queue` - The ARN of the SQS queue to which Amazon SNS can send undeliverable messages. | <pre>list(object({<br> name = string<br> function = string<br> filter_policy = optional(object({<br> enabled = optional(bool, false)<br> scope = optional(string, "ATTRIBUTES")<br> policy = optional(string)<br> }), {})<br> redrive_policy = optional(object({<br> dead_letter_sqs_queue = optional(string)<br> }), {})<br> }))</pre> | `[]` | no |
| <a name="input_subscriptions_by_sqs"></a> [subscriptions\_by\_sqs](#input\_subscriptions\_by\_sqs) | (Optional) A configuration for SQS Queue subscriptions to the SNS topic. Deliver JSON-encoded messages to the SQS queue. Each block of `subscriptions_by_sqs` as defined below.<br> (Required) `name` - The name of the subscription to the SNS topic. This value is only used internally within Terraform code.<br> (Required) `queue` - The ARN of the AWS SQS queue that can receive notifications from the SNS topic.<br> (Optional) `raw_message_delivery_enabled` - Whether to enable raw message delivery. Raw messages are free of JSON formatting. Defaults to `false`.<br> (Optional) `filter_policy` - The configuration to filter the messages that a subscriber receives. Additions or changes to the filter policy require up to 15 minutes to fully take effect. `filter_policy` as defined below.<br> (Optional) `enabled` - Whether to enable the filter policy. Defaults to `false`.<br> (Optional) `scope` - Determine how the filter policy will be applied to the message.<br> Valid values are `ATTRIBUTES` and `BODY`. Defaults to `ATTRIBUTES`.<br> `ATTRIBUTES` - The filter policy will be applied to the message attributes.<br> `BODY` - The filter policy will be applied to the message body.<br> (Optional) `redrive_policy` - The configuration to send undeliverable messages to a dead-letter queue. `redrive_policy` as defined below.<br> (Optional) `dead_letter_sqs_queue` - The ARN of the SQS queue to which Amazon SNS can send undeliverable messages. | <pre>list(object({<br> name = string<br> queue = string<br> raw_message_delivery_enabled = optional(bool, false)<br> filter_policy = optional(object({<br> enabled = optional(bool, false)<br> scope = optional(string, "ATTRIBUTES")<br> policy = optional(string)<br> }), {})<br> redrive_policy = optional(object({<br> dead_letter_sqs_queue = optional(string)<br> }), {})<br> }))</pre> | `[]` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no |
| <a name="input_xray_tracing_enabled"></a> [xray\_tracing\_enabled](#input\_xray\_tracing\_enabled) | (Optional) Whether to activate AWS X-Ray Active Tracing mode for the SNS topic. If set to Active, Amazon SNS will vend X-Ray segment data to topic owner account if the sampled flag in the tracing header is true. Defaults to `false`, and the topic passes through the tracing header it receives from an Amazon SNS publisher to its subscriptions. | `bool` | `false` | no |

Expand Down
34 changes: 31 additions & 3 deletions modules/sns-standard-topic/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ output "subscriptions" {
email => {
arn = subscription.arn
owner = subscription.owner_id
name = subscription.endpoint
email = subscription.endpoint
is_active = !subscription.pending_confirmation

Expand All @@ -87,6 +88,7 @@ output "subscriptions" {
name => {
arn = subscription.arn
owner = subscription.owner_id
name = name
function = subscription.endpoint
is_active = !subscription.pending_confirmation

Expand All @@ -106,6 +108,32 @@ output "subscriptions" {
}, null)
}
}
"SQS" = {
for name, subscription in aws_sns_topic_subscription.sqs :
name => {
arn = subscription.arn
owner = subscription.owner_id
name = name
queue = subscription.endpoint
is_active = !subscription.pending_confirmation

raw_message_delivery_enabled = subscription.raw_message_delivery
filter_policy = try({
enabled = subscription.filter_policy != null && subscription.filter_policy != ""
scope = try(
{
for k, v in local.filter_policy_scopes :
v => k
}[subscription.filter_policy_scope],
null
)
policy = try(jsondecode(subscription.filter_policy), null)
}, null)
redrive_policy = try({
dead_letter_sqs_queue = jsondecode(subscription.redrive_policy)["deadLetterTargetArn"]
}, null)
}
}
}
}

Expand Down Expand Up @@ -135,14 +163,14 @@ output "encryption_at_rest" {
# if !contains(["id", "arn", "name", "name_prefix", "display_name", "owner", "tags", "tags_all", "signature_version", "kms_master_key_id", "tracing_config", "content_based_deduplication", "fifo_topic"], k)
# }
# }
#

# output "zz" {
# description = "The list of log streams for the log group."
# value = {
# policy = aws_sns_topic_policy.this
# data_policy = aws_sns_topic_data_protection_policy.this
# lambda = {
# for name, subscription in aws_sns_topic_subscription.lambda :
# queue = {
# for name, subscription in aws_sns_topic_subscription.sqs :
# name => {
# for k, v in subscription :
# k => v
Expand Down
43 changes: 43 additions & 0 deletions modules/sns-standard-topic/subscriptions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ locals {
# - `confirmation_timeout_in_minutes`
# - `delivery_policy`
# - `endpoint_auto_confirms`
# - `raw_message_delivery `
# - `subscription_role_arn`
resource "aws_sns_topic_subscription" "email" {
for_each = {
Expand Down Expand Up @@ -54,6 +55,7 @@ resource "aws_sns_topic_subscription" "email" {
# - `confirmation_timeout_in_minutes`
# - `delivery_policy`
# - `endpoint_auto_confirms`
# - `raw_message_delivery `
# - `subscription_role_arn`
resource "aws_sns_topic_subscription" "email_json" {
for_each = {
Expand Down Expand Up @@ -92,6 +94,7 @@ resource "aws_sns_topic_subscription" "email_json" {
# - `confirmation_timeout_in_minutes`
# - `delivery_policy`
# - `endpoint_auto_confirms`
# - `raw_message_delivery `
# - `subscription_role_arn`
resource "aws_sns_topic_subscription" "lambda" {
for_each = {
Expand Down Expand Up @@ -120,3 +123,43 @@ resource "aws_sns_topic_subscription" "lambda" {
: null
)
}


###################################################
# SQS Queue Subscriptions
###################################################

# INFO: Not supported attributes
# - `confirmation_timeout_in_minutes`
# - `delivery_policy`
# - `endpoint_auto_confirms`
# - `subscription_role_arn`
resource "aws_sns_topic_subscription" "sqs" {
for_each = {
for subscription in var.subscriptions_by_sqs :
subscription.name => subscription
}

topic_arn = aws_sns_topic.this.arn

protocol = "sqs"
endpoint = each.value.queue

raw_message_delivery = each.value.raw_message_delivery_enabled

filter_policy_scope = (each.value.filter_policy.enabled
? local.filter_policy_scopes[each.value.filter_policy.scope]
: null
)
filter_policy = (each.value.filter_policy.enabled
? each.value.filter_policy.policy
: null
)

redrive_policy = (each.value.redrive_policy.dead_letter_sqs_queue != null
? jsonencode({
"deadLetterTargetArn" = each.value.redrive_policy.dead_letter_sqs_queue
})
: null
)
}
51 changes: 51 additions & 0 deletions modules/sns-standard-topic/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,57 @@ variable "subscriptions_by_lambda" {
}
}

variable "subscriptions_by_sqs" {
description = <<EOF
(Optional) A configuration for SQS Queue subscriptions to the SNS topic. Deliver JSON-encoded messages to the SQS queue. Each block of `subscriptions_by_sqs` as defined below.
(Required) `name` - The name of the subscription to the SNS topic. This value is only used internally within Terraform code.
(Required) `queue` - The ARN of the AWS SQS queue that can receive notifications from the SNS topic.
(Optional) `raw_message_delivery_enabled` - Whether to enable raw message delivery. Raw messages are free of JSON formatting. Defaults to `false`.
(Optional) `filter_policy` - The configuration to filter the messages that a subscriber receives. Additions or changes to the filter policy require up to 15 minutes to fully take effect. `filter_policy` as defined below.
(Optional) `enabled` - Whether to enable the filter policy. Defaults to `false`.
(Optional) `scope` - Determine how the filter policy will be applied to the message.
Valid values are `ATTRIBUTES` and `BODY`. Defaults to `ATTRIBUTES`.
`ATTRIBUTES` - The filter policy will be applied to the message attributes.
`BODY` - The filter policy will be applied to the message body.
(Optional) `redrive_policy` - The configuration to send undeliverable messages to a dead-letter queue. `redrive_policy` as defined below.
(Optional) `dead_letter_sqs_queue` - The ARN of the SQS queue to which Amazon SNS can send undeliverable messages.
EOF
type = list(object({
name = string
queue = string
raw_message_delivery_enabled = optional(bool, false)
filter_policy = optional(object({
enabled = optional(bool, false)
scope = optional(string, "ATTRIBUTES")
policy = optional(string)
}), {})
redrive_policy = optional(object({
dead_letter_sqs_queue = optional(string)
}), {})
}))
default = []
nullable = false

validation {
condition = alltrue([
for subscription in var.subscriptions_by_sqs :
contains(["ATTRIBUTES", "BODY"], subscription.filter_policy.scope)
if subscription.filter_policy.enabled
])
error_message = "Valid values for `filter_policy.scope` are `ATTRIBUTES` and `BODY`."
}
validation {
condition = alltrue([
for subscription in var.subscriptions_by_sqs :
subscription.filter_policy.policy == null ||
can(jsondecode(subscription.filter_policy.policy))
if subscription.filter_policy.enabled
])
error_message = "`filter_policy.policy` must be JSON string."
}
}


variable "xray_tracing_enabled" {
description = "(Optional) Whether to activate AWS X-Ray Active Tracing mode for the SNS topic. If set to Active, Amazon SNS will vend X-Ray segment data to topic owner account if the sampled flag in the tracing header is true. Defaults to `false`, and the topic passes through the tracing header it receives from an Amazon SNS publisher to its subscriptions."
type = bool
Expand Down

0 comments on commit 0a60585

Please sign in to comment.