Configuring AWS SNS with Terraform

A comprehensive guide to setting up Amazon Simple Notification Service (SNS) using Terraform Infrastructure as Code

Configuring AWS SNS with Terraform

Amazon Simple Notification Service (SNS) is a fully managed pub/sub messaging service. This guide shows how to set up SNS using Terraform.

Prerequisites

  • AWS CLI configured
  • Terraform installed
  • Basic understanding of pub/sub messaging
  • Subscriber endpoints ready (email, Lambda, etc.)

Project Structure

aws-sns-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

Basic SNS Configuration

# main.tf
provider "aws" {
  region = var.aws_region
}

# SNS Topic
resource "aws_sns_topic" "main" {
  name = "${var.project_name}-topic"
  
  tags = {
    Environment = var.environment
  }
}

# Topic Policy
resource "aws_sns_topic_policy" "default" {
  arn = aws_sns_topic.main.arn

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "Default SNS Policy"
        Effect = "Allow"
        Principal = {
          AWS = "*"
        }
        Action = [
          "SNS:GetTopicAttributes",
          "SNS:SetTopicAttributes",
          "SNS:AddPermission",
          "SNS:RemovePermission",
          "SNS:DeleteTopic",
          "SNS:Subscribe",
          "SNS:ListSubscriptionsByTopic",
          "SNS:Publish"
        ]
        Resource = aws_sns_topic.main.arn
        Condition = {
          StringEquals = {
            "AWS:SourceOwner": data.aws_caller_identity.current.account_id
          }
        }
      }
    ]
  })
}

Subscription Configuration

# Email Subscription
resource "aws_sns_topic_subscription" "email" {
  topic_arn = aws_sns_topic.main.arn
  protocol  = "email"
  endpoint  = var.email_endpoint
}

# Lambda Subscription
resource "aws_sns_topic_subscription" "lambda" {
  topic_arn = aws_sns_topic.main.arn
  protocol  = "lambda"
  endpoint  = var.lambda_function_arn
}

# SQS Subscription
resource "aws_sns_topic_subscription" "sqs" {
  topic_arn = aws_sns_topic.main.arn
  protocol  = "sqs"
  endpoint  = var.sqs_queue_arn
}

# HTTP/HTTPS Endpoint
resource "aws_sns_topic_subscription" "https" {
  topic_arn = aws_sns_topic.main.arn
  protocol  = "https"
  endpoint  = var.https_endpoint
}

FIFO Topic Configuration

# FIFO Topic
resource "aws_sns_topic" "fifo" {
  name                        = "${var.project_name}.fifo"
  fifo_topic                 = true
  content_based_deduplication = true

  tags = {
    Environment = var.environment
  }
}

# FIFO Topic Subscription (SQS FIFO Queue)
resource "aws_sns_topic_subscription" "fifo_sqs" {
  topic_arn = aws_sns_topic.fifo.arn
  protocol  = "sqs"
  endpoint  = var.fifo_queue_arn
}

Cross-Account Access

# Topic Policy for Cross-Account Access
resource "aws_sns_topic_policy" "cross_account" {
  arn = aws_sns_topic.main.arn

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "CrossAccountPublish"
        Effect = "Allow"
        Principal = {
          AWS = var.allowed_aws_account_ids
        }
        Action   = "SNS:Publish"
        Resource = aws_sns_topic.main.arn
      }
    ]
  })
}

Variables Configuration

# variables.tf
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

variable "project_name" {
  description = "Project name"
  type        = string
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "dev"
}

variable "email_endpoint" {
  description = "Email endpoint for subscription"
  type        = string
}

variable "lambda_function_arn" {
  description = "Lambda function ARN"
  type        = string
}

variable "allowed_aws_account_ids" {
  description = "List of AWS account IDs allowed to publish"
  type        = list(string)
  default     = []
}

Best Practices

  1. Topic Management

    • Use meaningful topic names
    • Implement proper access controls
    • Consider using FIFO topics for ordered delivery
    • Use topic tags for organization
  2. Security

    • Implement least privilege access
    • Use encryption for sensitive data
    • Regularly audit topic policies
    • Monitor failed deliveries
  3. Reliability

    • Implement proper error handling
    • Use DLQ for failed messages
    • Monitor delivery status
    • Implement retry policies
  4. Cost Optimization

    • Monitor message volume
    • Clean up unused subscriptions
    • Use appropriate message filtering
    • Consider message batching

Message Filtering

# Subscription with Filter Policy
resource "aws_sns_topic_subscription" "filtered" {
  topic_arn = aws_sns_topic.main.arn
  protocol  = "sqs"
  endpoint  = var.sqs_queue_arn

  filter_policy = jsonencode({
    environment = ["production"]
    severity    = ["high", "critical"]
  })
}

Dead Letter Queue Configuration

# DLQ for Failed Message Delivery
resource "aws_sqs_queue" "dlq" {
  name = "${var.project_name}-dlq"
}

resource "aws_sns_topic_subscription" "with_dlq" {
  topic_arn = aws_sns_topic.main.arn
  protocol  = "lambda"
  endpoint  = var.lambda_function_arn

  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.dlq.arn
  })
}

Monitoring Configuration

# CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "failed_deliveries" {
  alarm_name          = "${var.project_name}-failed-deliveries"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "1"
  metric_name         = "NumberOfNotificationsFailed"
  namespace           = "AWS/SNS"
  period             = "300"
  statistic          = "Sum"
  threshold          = "1"
  alarm_description  = "This metric monitors failed SNS deliveries"
  alarm_actions      = [var.alarm_topic_arn]

  dimensions = {
    TopicName = aws_sns_topic.main.name
  }
}

Deployment Steps

  1. Initialize Terraform:
terraform init
  1. Plan the deployment:
terraform plan
  1. Apply the configuration:
terraform apply

Clean Up

Remove all resources when done:

terraform destroy

Common Use Cases

  1. Event Notification System
resource "aws_sns_topic" "events" {
  name = "${var.project_name}-events"
}

resource "aws_sns_topic_subscription" "events_email" {
  count     = length(var.notification_emails)
  topic_arn = aws_sns_topic.events.arn
  protocol  = "email"
  endpoint  = var.notification_emails[count.index]
}
  1. Multi-Region Fanout
resource "aws_sns_topic" "primary" {
  name = "${var.project_name}-primary"
}

resource "aws_sns_topic" "secondary" {
  provider = aws.secondary_region
  name     = "${var.project_name}-secondary"
}

resource "aws_sns_topic_subscription" "region_fanout" {
  provider  = aws.secondary_region
  topic_arn = aws_sns_topic.primary.arn
  protocol  = "sns"
  endpoint  = aws_sns_topic.secondary.arn
}

Conclusion

This setup provides a comprehensive foundation for deploying SNS using Terraform. Remember to:

  • Plan your messaging architecture carefully
  • Implement proper security measures
  • Monitor delivery status and failures
  • Keep your configurations versioned
  • Test thoroughly before production deployment

The complete code can be customized based on your specific requirements and use cases.