How to monitor activity in S3, using SNS and SQS Chao-Shih Chen Published: February 12, 2019 Amazon S3 is one of the most popular object storage services that apps use today. Monitoring S3 buckets for activity can be very beneficial, depending on the reason the bucket stores data.For example, the Kloudless File Picker provides an easy way for users to upload content to an app’s S3 bucket. Apps can monitor S3 for new files to process rather than write client-side logic to trigger data processing when a user completes an upload.AWS docs describe monitoring activity in S3 using SNS. In this article, we will describe a generalized version that enables events to be consumed by multiple receivers via SQS instead. Kloudless adopts this approach to scale to multiple environments and downstream processors rather than restrict the notifications to a single receiver. This is critical since S3 buckets do not support multiple notification configurations.There are three steps to receive notifications from buckets:Create an SNS topic to receive notifications sent from a bucket.Create an SQS queue for each receiver, and subscribe to the SNS topic.Configure S3 buckets to publish events to the SNS topic.We’ll use two AWS Account IDs represented by keywords to demonstrate the process:APP_ARN is the ID of the developer’s AWS accountOWNER_ARN is the ID of the bucket owner’s AWS accountThe developer’s app creates and configures the SNS topic and SQS queue in its own AWS account, and retrieves events from the queue. The app accesses buckets using the bucket owner’s account to configure the buckets to publish events to the SNS topic.Create an SNS topicThe app first creates an SNS topic in its own AWS account with the name s3-event-OWNER_ARN. This represents a topic that will receive notifications from the bucket owner’s buckets. The presence of the bucket owner’s ARN ensures uniqueness. Set a policy similar to the one below on the SNS topic: { "Version": "2008-10-17", "Id": "arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN/SNSDefaultPolicy", "Statement": [ { "Sid": "Publish S3 Event Notifications To SNS Topic", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "SNS:Publish" ], "Resource": "arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN", "Condition": { "ArnLike": { "aws:SourceArn": [ "arn:aws:s3:::bucket-1", "arn:aws:s3:::bucket-2", "arn:aws:s3:::bucket-3" ] } } } ] }1234567891011121314151617181920212223242526{ "Version": "2008-10-17", "Id": "arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN/SNSDefaultPolicy", "Statement": [ { "Sid": "Publish S3 Event Notifications To SNS Topic", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "SNS:Publish" ], "Resource": "arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN", "Condition": { "ArnLike": { "aws:SourceArn": [ "arn:aws:s3:::bucket-1", "arn:aws:s3:::bucket-2", "arn:aws:s3:::bucket-3" ] } } } ]}The Condition above specifies three S3 buckets that can publish to this SNS topic.Create an SQS queue and subscribe to the SNS topicContinuing to use the app’s own AWS account, create a queue with name s3-eventq-receiver-1-APP_ARN and set its policy similar to the one below: { "Version": "2012-10-17", "Id": "arn:aws:sqs:us-east-1:APP_ARN:s3-eventq-OWNER_ARN-APP_ARN/SQSDefaultPolicy", "Statement": [ { "Sid": "Send Message From SNS Topic To SQS Queue", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "SQS:SendMessage" ], "Resource": "arn:aws:sqs:us-east-1:APP_ARN:s3-eventq-receiver-1", "Condition": { "ArnEquals": { "aws:SourceArn": "arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN" } } } ] }12345678910111213141516171819202122{ "Version": "2012-10-17", "Id": "arn:aws:sqs:us-east-1:APP_ARN:s3-eventq-OWNER_ARN-APP_ARN/SQSDefaultPolicy", "Statement": [ { "Sid": "Send Message From SNS Topic To SQS Queue", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "SQS:SendMessage" ], "Resource": "arn:aws:sqs:us-east-1:APP_ARN:s3-eventq-receiver-1", "Condition": { "ArnEquals": { "aws:SourceArn": "arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN" } } } ]}Similar to the first policy, the Condition specifies an SNS topic that can send messages to the queue. The queue name includes receiver-1 to uniquely identify the recipient of messages in this queue. Create additional SQS queues for other recipients.Then, set the queue to subscribe to the SNS topic. Here’s an example of how to do this using the popular Python library, boto3: import boto3 queue_arn = 'arn:aws:sqs:us-east-1:APP_ARN:s3-eventq-receiver-1' topic_arn = 'arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN' sns_client = boto3.client( 'sns', aws_access_key_id='APP_ARN_AWS_KEY', aws_secret_access_key='APP_ARN_AWS_SECRET', region_name='us-east-1') sns_client.subscribe( TopicArn=topic_arn, Protocol='sqs', Endpoint=queue_arn)123456789101112import boto3queue_arn = 'arn:aws:sqs:us-east-1:APP_ARN:s3-eventq-receiver-1'topic_arn = 'arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN'sns_client = boto3.client( 'sns', aws_access_key_id='APP_ARN_AWS_KEY', aws_secret_access_key='APP_ARN_AWS_SECRET', region_name='us-east-1')sns_client.subscribe( TopicArn=topic_arn, Protocol='sqs', Endpoint=queue_arn)The developer app’s IAM account provides the key and secret for the API requests above.Update the S3 buckets’ notification configurationsUsing the bucket owner’s AWS IAM credentials, set the buckets to publish ObjectCreated and ObjectRemoved events to the SNS topic. Here is an example using boto3: bucket_configuration = { 'TopicConfigurations': [{ 'Id': 'S3-SQS-Notifications-Config', 'Events': [ 's3:ObjectCreated:*', 's3:ObjectRemoved:*' ], 'TopicArn': 'arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN' }] } s3_client = boto3.client( 's3', aws_access_key_id='OWNER_ARN_AWS_KEY', aws_secret_access_key='OWNER_ARN_AWS_SECRET', region_name='us-east-1') for bucket_name in ('bucket-1', 'bucket-2', 'bucket-3'): s3_client.put_bucket_notification_configuration( Bucket=bucket_name, NotificationConfiguration=bucket_configuration)12345678910111213141516171819bucket_configuration = { 'TopicConfigurations': [{ 'Id': 'S3-SQS-Notifications-Config', 'Events': [ 's3:ObjectCreated:*', 's3:ObjectRemoved:*' ], 'TopicArn': 'arn:aws:sns:us-east-1:APP_ARN:s3-event-OWNER_ARN' }]}s3_client = boto3.client( 's3', aws_access_key_id='OWNER_ARN_AWS_KEY', aws_secret_access_key='OWNER_ARN_AWS_SECRET', region_name='us-east-1')for bucket_name in ('bucket-1', 'bucket-2', 'bucket-3'): s3_client.put_bucket_notification_configuration( Bucket=bucket_name, NotificationConfiguration=bucket_configuration)The bucket owner’s key and secret are used in this case.The developer’s app can now poll the queue for new events! Here is a diagram of the event flow:The queue’s presence ensures that multiple apps can subscribe to bucket notifications. For example, the subscriber described above, receiver-1, could be the developer’s production application, but still leave room for a development and staging application to receive the same events. As mentioned above, buckets do not natively support more than a single notification configuration, even though the API endpoint accepts a list.Simplify S3 activity monitoring using KloudlessThe process described above is a simplified version. Real-world use involves complexities such as the ones below:Buckets in different regionsDetecting and monitoring newly added bucketsCleaning up SNS topics intended for deleted buckets and/or accountsConflicts with existing notification configurations on bucketsCheck out Kloudless for an easy-to-use API that enables your application to monitor S3 as well as several other cloud apps for activity with a single integration.