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

Support SQS queue subscription to sns-standard-topic #11

Merged
merged 1 commit into from
Oct 4, 2023
Merged
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
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