How to target subscribers in an SNS topic

Use a single list for multiple customers

6 mins
I have a lot of challenges when it comes to AWS, but I bet your pain points are entirely different than mine. I'd love to hear what keeps you up at night. It would be great to hear from you by filling out this form. Thanks in advance!

SNS notifications to users

Let’s say you have a monitoring app that pings customers’ pages and sends a notification when there is a problem. In AWS, the service for notifications is SNS, where you create a topic, customers subscribe to it, then you can push messages to the topic.

Single topicmonitoringSNS TopicCustomer 1Customer 2messagemessagemessage

But in this case, every customer gets every message, which is not desirable for services with many users.

Multiple topicsmonitoringSNS TopicSNS TopicCustomer 1Customer 2messagemessage

One solution is to create separate topics for each customer but that means you’ll have a ton of topics.

An alternative service to use is SES, which is the email-sending platform in AWS. It lets you send individual emails so that you can target the affected customer. But compared to SNS it is a lot more involved to set up. Not only you need to send a support request to be able to send emails but also you need to handle bounces, concurrency, and email errors. Compared to creating a topic and adding the subscribers, it is a lot of extra work.

Fortunately, there is a way to target a specific customer in an SNS topic. And it requires just a little bit of initial setup.

Filter policy

When you add a subscriber you can also specify a filter policy. This allows you to specify filtering criteria to control which messages the subscriber will get when publishing to the topic.

With Terraform, the policy is the filter_policy attribute, which expects a JSON map:

resource "aws_sns_topic_subscription" "subscription_1" {
	# topic_arn, protocol, endpoint

	filter_policy = jsonencode(map("target",list("customer_1", "all")))
}

monitoringSNS TopicCustomer 1Customer 2{target:"customer_1"}message

This policy makes sure that the subscriber only gets the message if the target attribute is either customer_1 or all. The latter is a best practice so that the whole topic can also be targeted.

When a second customer comes and you subscribe him to the topic, just give a different id:

resource "aws_sns_topic_subscription" "subscription_2" {
	# topic_arn, protocol, endpoint

	filter_policy = jsonencode(map("target",list("customer_2", "all")))
}

monitoringSNS TopicCustomer 1Customer 2{target:"customer_2"}message

If you add subscribers programmatically, use the Attributes property with the subscribe operation.

In the above scenario, sending a message with target: "customer_1" sends only to the first customer, target: "customer_2" to the second one, and target: "all" to both of them.

Customer id

What value to use in the filter policy? My first intuition was to use the subscription ARN so that every single subscription is targetable without any other stored value.

Customer 1monitoringSNS TopicOPS teamCTOSlack channelCustomer 2{target:"customer_1"}

But a better approach is to use a customer identifier that you already use throughout the application. In this case, you keep the door open for having a customer with multiple subscriptions to the same events. For example, an alarm sent to everyone in the ops team.

Adding a filter policy later

If you already have a list it’s not easy to move subscribers around as they need to reconfirm their subscription. But fortunately, the filter policy can be added for an existing subscription. This means if you want to migrate from a single-customer list to a multiplexed one, you can do so without affecting any third parties.

In NodeJS, use the AttributeValue property of the setTopicAttributes call. With the AWS CLI, the same would be the set-subscription-attributes command.

Message attributes

Filtering the messages is only one end of the solution, as you need a way to specify who should get the message during publishing a notification. This is done with the MessageAttributes property of publish:

// set the target to customer id or "all"
const target = "customer_1";

sns.publish({
	// TopicArn, Message
	MessageAttributes: {
		target: {
			DataType: "String",
			StringValue: target,
		}
	}
});

The key of the property (target) is the key in the filter policy, the data type is String, and the StringValue defines the actual parameter.

Conclusion

With the help of the filter policy, you can consolidate all notifications into a single topic and can still target subscribers. This makes it easy to integrate SNS notifications into your app and, for simple cases, makes it possible to avoid all the complexity a proper email solution would entail.

Download our ebook on AWS account security basics

Learn 5 simple steps to avoid the rookie mistakes.

  1. Why the root account is bad for security
  2. Use multiple users
  3. Secure accounts with multi-factor authentication
  4. Security logging with CloudTrail
  5. Billing alerts as an early warning system

Download the free guide here:

03 December 2019