← Back to list

Publishing SNS notifications to a Firehose Delivery Stream

Discover how to utilize Amazon SNS and Amazon Data Firehose to route notifications or messages to storage endpoints for archival…

Vesna Milovanovic in Towards AWS · 2024-04-10 05:59 · 22 claps · 7.0 min read
#aws #amazon-kinesis-firehose #amazon-sns #devops #aws-tutorial
Open on Medium ↗
Wiki topics: RAG · RAG & Retrieval FT · Fine-tuning & Adaptation ☁️ · DevOps & Cloud

TUTORIAL

Publishing SNS notifications to a Firehose Delivery Stream

Discover how to utilize Amazon SNS and Amazon Data Firehose to route notifications or messages to storage endpoints for archival, compliance or other purposes. In this guide, Terraform will be used to provision and deploy resources on AWS.

Table of Contents

· Prerequisites · Introduction · A2A Messaging · Terraform Configuration · AWS Provider Configuration · Storage Endpoint · IAM Permissions · Firehose Delivery Stream · SNS SubscriptionVariablesOutputs · Deployment · Testing · Cleanup · Learn more

Prerequisites

Introduction

Before diving into the fun part, let’s break down the AWS services we’ll use in this guide.

  • Amazon Simple Storage Service (S3) is an object storage service for storing and retrieving any amount of data from anywhere.
  • Amazon Simple Notification Service (SNS) is a managed Pub/Sub AWS service that provides message delivery from publishers to subscribers. Publishers (producers) send messages to a topic and subscribers (consumers, clients) subscribe to the topic and receive messages using an endpoint type such as Firehose. SNS sends notifications two ways, A2A and A2P.
  • Amazon Data Firehose is a fully managed AWS service for delivering real-time streaming data to destinations such as Amazon S3. You configure the publishers to send messages to Firehose and it will automatically deliver the messages to the destination you specified.
  • Amazon CloudWatch monitors your AWS resources and the applications you run on AWS in real time.

Note: Amazon Data Firehose was previously known as Amazon Kinesis Data Firehose.

A2A Messaging

One of the primary uses of Amazon SNS is application-to-application (A2A) messaging with subscribers. You can distribute messages to Firehose delivery streams, Lambda functions, SQS queues, HTTP(S) endpoints, etc.

To forward notifications to storage endpoints, you can subscribe Amazon Data Firehose delivery streams to an Amazon SNS topic. When you publish messages to an Amazon SNS topic, they are sent to the Firehose delivery stream and then delivered to the destination you configured in Firehose.

SNS — Firehose — S3

SNS — Firehose — S3

Through these streams, SNS notifications can be routed to Amazon S3, Amazon Redshift, Amazon OpenSearch Service or third-party providers (Datadog, MongoDB,…). In this example, the destination will be an S3 bucket.

Note: We’re using various AWS services and there are associated costs beyond the Free Tier usage.

Terraform Configuration

The file structure will be as follows:

  • providers.tf — AWS provider configuration
  • main.tf — the main set of configuration (S3 bucket, Firehose delivery stream, SNS topic, etc.)
  • iam.tf — IAM roles and policies
  • variables.tf — input variable definitions
  • output.tf — output definitions used for testing
  • terraform.tfvars — sensitive variable values, if version control systems like Git are being used, this file should be ignored.

AWS Provider Configuration

Let’s begin by setting up the AWS provider. Create aprovider.tf file. The terraform block specifies the required provider and its version. The provider block initializes the AWS provider with the specified region and access keys belonging to your IAM user. These values are provided through variables, allowing for flexibility and security in the configuration.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.44.0"
    }
  }
}

provider "aws" {
  region     = var.region
  access_key = var.access_key
  secret_key = var.secret_key
}

Now we’ve established the necessary connection to the AWS environment for further infrastructure provisioning using Terraform.

Storage Endpoint

Create main.tf. The first resource to be created is an Amazon S3 bucket with minimal configuration. Since bucket names must be globally unique, we will append a random ID to the provided bucket name.

resource "random_id" "s3_bucket_id" {
  byte_length = 8
}

# S3 Destination Bucket
resource "aws_s3_bucket" "s3_destination_bucket" {
  bucket = "${var.s3_destination_bucket_name}-${random_id.s3_bucket_id.hex}"
  force_destroy = true
  tags = {
    Environment = "dev"
    Role        = "analytics"
  }
}

resource "aws_s3_bucket_ownership_controls" "s3_destination_bucket_ownership" {
  bucket = aws_s3_bucket.s3_destination_bucket.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

resource "aws_s3_bucket_acl" "s3_destination_bucket_acl" {
  depends_on = [aws_s3_bucket_ownership_controls.s3_destination_bucket_ownership]
  bucket     = aws_s3_bucket.s3_destination_bucket.id
  acl        = "private"
}

resource "aws_s3_bucket_versioning" "s3_destination_bucket_versioning" {
  bucket = aws_s3_bucket.s3_destination_bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

In this example, ownership controls and access control list (ACL) for the bucket are also configured, ensuring that only authorized entities can access the bucket. We also enable versioning for the bucket.

Note: It’s a good practice to tag all (taggable) resources for better organization or cost-tracking purposes.

IAM Permissions

Create iam.tf file to store IAM roles, access policies and other IAM resources.

To subscribe a Firehose delivery stream to an SNS topic, we need an IAM role that trusts the Amazon SNS service principal and has permission to write to the delivery stream. An access policy defines a set of recommended permissions.

# SNS to Firehose
resource "aws_iam_policy" "sns_to_firehose_access_policy" {
  name        = "SNSToFirehoseAccessPolicy"
  description = "Allow SNS to put records in the Firehose Delivery Stream"
  policy      = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "firehose:DescribeDeliveryStream",
        "firehose:ListDeliveryStreams",
        "firehose:ListTagsForDeliveryStream",
        "firehose:PutRecord",
        "firehose:PutRecordBatch"
      ],
      "Resource": [
        "${aws_kinesis_firehose_delivery_stream.extended_s3_stream.arn}"
      ],
      "Effect": "Allow"
    }
  ]
}
EOF
}

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

resource "aws_iam_role_policy_attachment" "sns_subscription_role_policy_attachment" {
  role       = aws_iam_role.sns_subscription_role.name
  policy_arn = aws_iam_policy.sns_to_firehose_access_policy.arn
}

We also need to configure the IAM policy and role for granting permissions to Amazon Data Firehose to interact with an S3 bucket.

# Firehose to S3
resource "aws_iam_policy" "firehose_to_s3_policy" {
  name        = "FirehoseToS3Policy"
  description = "Allow Firehose to put records in the S3 bucket"
  policy      = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:GetBucketLocation",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads",
        "s3:PutObject"
      ],
      "Resource": [
        "${aws_s3_bucket.s3_destination_bucket.arn}",
        "${aws_s3_bucket.s3_destination_bucket.arn}/*"
      ]
    }
  ]
}
EOF
}

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

resource "aws_iam_role_policy_attachment" "delivery_stream_access_attachment" {
  policy_arn = aws_iam_policy.firehose_to_s3_policy.arn
  role       = aws_iam_role.firehose_role.name
}

Firehose Delivery Stream

Update main.tf to create a Firehose delivery stream for the message archiving, specifically targeting Amazon S3 as its destination. Enable Amazon CloudWatch error logging to ensure that you can access error details when failures occur.

# CloudWatch Log Group
resource "aws_cloudwatch_log_group" "firehose_s3_log_group" {
  name = "firehose/s3"
  retention_in_days = 5
  log_group_class = "STANDARD"
  tags = {
    Environment = "dev"
    Application = "analytics"
  }
}

# Firehose
resource "aws_kinesis_firehose_delivery_stream" "extended_s3_stream" {
  name        = "user-activity-stream"
  destination = "extended_s3"

  extended_s3_configuration {
    bucket_arn         = aws_s3_bucket.s3_destination_bucket.arn
    role_arn           = aws_iam_role.firehose_role.arn
    buffering_size     = 1
    buffering_interval = 60
    custom_time_zone   = "Europe/Belgrade"

    cloudwatch_logging_options {
      enabled = true
      log_group_name = aws_cloudwatch_log_group.firehose_s3_log_group.name
      log_stream_name = "S3Delivery"
    }
  }

 tags = {
    Environment = "dev"
    Role        = "analytics"
  }
}

SNS Subscription

Finally, update main.tf to create a topic for publishing notifications and configure a subscription to the SNS topic. Subscription specifies the protocol as firehose and the endpoint specifies the ARN of the Firehose delivery stream.

# SNS
resource "aws_sns_topic" "user_activity" {
  name = "user-activity-topic"
  tags = {
    Environment = "dev"
    Role        = "analytics"
  }
}

resource "aws_sns_topic_subscription" "user_activity_sns_subscription" {
  protocol              = "firehose"
  topic_arn             = aws_sns_topic.user_activity.arn
  endpoint              = aws_kinesis_firehose_delivery_stream.extended_s3_stream.arn
  subscription_role_arn = aws_iam_role.sns_subscription_role.arn
  depends_on = [
    aws_sns_topic.user_activity,
    aws_kinesis_firehose_delivery_stream.extended_s3_stream
  ]
}

We have now established a communication channel where notifications published to the SNS topic are automatically forwarded to the specified Firehose delivery stream for further processing or storage.

Variables

Input variable definitions are stored in variables.tf file.

variable "region" {
  type        = string
  description = "The region in which the resources will be created."
}

variable "access_key" {
  type        = string
  description = "The access key for the AWS account."
}

variable "secret_key" {
  type        = string
  description = "The secret access key for the AWS account."
}

variable "s3_destination_bucket_name" {
  type        = string
  description = "The name of the S3 bucket to which the messages will be directed."
}

All these variables are required. Store their values in terraform.tfvars.

region     = "<value>"
access_key = "<value>"
secret_key = "<value>"
s3_destination_bucket_name = "<value>"

Outputs

We’ll capture the outputs from the deployment process. For testing, we’ll need the resource names and/or ARNs.

  • SNS topic ARN
  • S3 bucket name
output "sns_topic_arn" {
  value = aws_sns_topic.user_activity.arn
}

output "s3_destination_bucket_name" {
  value = aws_s3_bucket.s3_destination_bucket.bucket
}

Deployment

From the command line, initialize Terraform to download and install the providers.

terraform init

Apply the configuration.

terraform apply -auto-approve

You can verify that resources have been creating via AWS CLI or in the AWS console.

An empty S3 bucket

An empty S3 bucket

SNS topic and subscription

SNS topic and subscription

Data Firehose delivery stream

Data Firehose delivery stream

New CloudWatch Log Group

New CloudWatch Log Group

Testing

Publish a message to the SNS topic via the AWS CLI command and check that the message was sent to the destination S3 bucket.

Replace <OUTPUT_SNS_TOPIC_ARN> and <OUTPUT_S3_BUCKET-NAME> with your output values.

aws sns publish --topic-arn <OUTPUT_SNS_TOPIC_ARN> --message "Publishing a first message to the SNS topic @cloudvesna"
aws s3 ls s3://<OUTPUT_S3_BUCKET-NAME> --recursive --human-readable --summarize

CLI

CLI

You should see a similar output in your terminal.

Message delivered to S3

Message delivered to S3

You can inspect Firehose Stream Metrics to gain more insight. For testing purposes, we only published one message so not much to see here (we sent one message with a size of 433 B).

Firehose Stream metrics

Firehose Stream metrics

Cleanup

Delete all resources by running terraform destroy command.

Learn more

Thank you for taking the time to read my article! If you found it valuable, I invite you to follow my Medium account for similar content in the future. I regularly share tutorials and tips on DevOps and Cloud, and I would be thrilled to connect with you.


메타데이터
post_id
a0afdd45d47c
slug
publishing-sns-notifications-to-a-firehose-delivery-stream-a0afdd45d47c
url
https://towardsaws.com/publishing-sns-notifications-to-a-firehose-delivery-stream-a0afdd45d47c
canonical_url
https://towardsaws.com/publishing-sns-notifications-to-a-firehose-delivery-stream-a0afdd45d47c
author_url
https://medium.com/@cloudvesna
status
ok
fetched_at
2026-08-07 08:47:49