Skip to main content

Quick Reference: How SNS Works with Lambda for Slack Notifications

🎯 The Simple Version

What Happens When an Alarm Triggers?

1. RDS CPU hits 85% for 15 minutes
   ↓
2. CloudWatch Alarm: "CW-RDS-AppName-prod-rds-db-CPUUtilization" β†’ ALARM state
   ↓
3. Alarm has this config: alarm_actions = ["arn:aws:sns:...:PROD_Default_CloudWatch_Alarms_Topic"]
   ↓
4. SNS Topic receives the alarm notification
   ↓
5. SNS sees: "I have a Lambda subscriber for this topic!"
   ↓
6. SNS invokes Lambda: CloudWatch-Alarms-To-Slack
   ↓
7. Lambda receives SNS event, parses the alarm data
   ↓
8. Lambda formats a pretty message with colors & emojis
   ↓
9. Lambda sends HTTP POST to Slack webhook
   ↓
10. πŸ’¬ Message appears in your Slack channel!

πŸ”‘ Key Concepts

SNS (Simple Notification Service)

  • Think of it as: A message router/broadcaster
  • What it does: When it receives a message, it forwards it to all subscribers
  • Subscribers can be: Lambda, Email, SMS, HTTP endpoints, etc.
  • In our case: CloudWatch β†’ SNS β†’ Lambda

Why use SNS instead of CloudWatch β†’ Lambda directly?

βœ… Flexibility: You can add multiple subscribers (email, Lambda, etc.) βœ… Decoupling: CloudWatch doesn’t need to know about Lambda βœ… Fan-out: One alarm can notify multiple destinations βœ… Retry logic: SNS handles retries if Lambda fails

Lambda Subscription to SNS

resource "aws_sns_topic_subscription" "lambda_subscription" {
  topic_arn = "arn:aws:sns:ap-south-1:3AWS-Account-ID-NO5:PROD_Default_CloudWatch_Alarms_Topic"
  protocol  = "lambda"  # ← This tells SNS: "Call a Lambda function"
  endpoint  = aws_lambda_function.slack_notifier.arn  # ← This Lambda
}
This creates a subscription that says:
β€œHey SNS topic! Whenever you receive a message, please invoke this Lambda function”

Permission for SNS to Call Lambda

resource "aws_lambda_permission" "allow_sns" {
  statement_id  = "AllowExecutionFromSNS"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.slack_notifier.function_name
  principal     = "sns.amazonaws.com"  # ← SNS service
  source_arn    = var.sns_topic_arn    # ← Only this specific SNS topic
}
This permission says:
β€œLambda function, please allow SNS to invoke you”

πŸ“Š Data Flow Example

CloudWatch Alarm Data

{
  "AlarmName": "CW-RDS-AppName-prod-rds-db-CPUUtilization",
  "NewStateValue": "ALARM",
  "NewStateReason": "Threshold Crossed: 3 datapoints were greater than threshold",
  "Region": "ap-south-1",
  "Trigger": {
    "MetricName": "CPUUtilization",
    "Namespace": "AWS/RDS",
    "Threshold": 80.0
  }
}

SNS Wraps it

{
  "Records": [
    {
      "Sns": {
        "Type": "Notification",
        "TopicArn": "arn:aws:sns:ap-south-1:3AWS-Account-ID-NO5:PROD_Default_CloudWatch_Alarms_Topic",
        "Message": "{...CloudWatch alarm JSON above...}",
        "Timestamp": "2026-01-12T10:30:00.000Z"
      }
    }
  ]
}

Lambda Receives & Processes

def lambda_handler(event, context):
    # Extract the CloudWatch alarm from SNS wrapper
    sns_record = event['Records'][0]['Sns']
    alarm_data = json.loads(sns_record['Message'])
    
    # alarm_data now has: AlarmName, NewStateValue, etc.
    
    # Format for Slack
    slack_message = {
        "text": f"🚨 {alarm_data['AlarmName']} is in {alarm_data['NewStateValue']} state"
    }
    
    # Send to Slack
    requests.post(SLACK_WEBHOOK_URL, json=slack_message)

Slack Receives

🚨 CloudWatch Alarm: ALARM

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

πŸ“Œ Alarm Name: CW-RDS-AppName-prod-rds-db-CPUUtilization
πŸ”„ State Change: OK β†’ ALARM
πŸ“ Reason: Threshold Crossed: 3 datapoints...

πŸŽͺ All Components Working Together

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     YOUR AWS ACCOUNT                         β”‚
β”‚                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                        β”‚
β”‚  β”‚ RDS Instance    β”‚  CPU at 85%                            β”‚
β”‚  β”‚ AppName-prod-rds β”‚                                        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                        β”‚
β”‚           β”‚                                                  β”‚
β”‚           β”‚ CloudWatch monitors metrics                     β”‚
β”‚           β–Ό                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                        β”‚
β”‚  β”‚ CloudWatch      β”‚  Threshold breached!                   β”‚
β”‚  β”‚ Alarm           β”‚                                        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                        β”‚
β”‚           β”‚                                                  β”‚
β”‚           β”‚ alarm_actions = [SNS Topic ARN]                 β”‚
β”‚           β–Ό                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                        β”‚
β”‚  β”‚ SNS Topic       β”‚  Receives alarm notification           β”‚
β”‚  β”‚ PROD_Default_   β”‚                                        β”‚
β”‚  β”‚ CloudWatch_     β”‚  Has subscribers:                      β”‚
β”‚  β”‚ Alarms_Topic    β”‚  - Lambda function                     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                        β”‚
β”‚           β”‚                                                  β”‚
β”‚           β”‚ Invokes all subscribers                         β”‚
β”‚           β–Ό                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                        β”‚
β”‚  β”‚ Lambda Function β”‚  1. Receives SNS event                 β”‚
β”‚  β”‚ CloudWatch-     β”‚  2. Parses alarm data                  β”‚
β”‚  β”‚ Alarms-To-Slack β”‚  3. Formats message                    β”‚
β”‚  β”‚                 β”‚  4. HTTP POST to Slack                 β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                        β”‚
β”‚           β”‚                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
            β”‚ HTTPS POST request
            β”‚ with formatted JSON
            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        SLACK                                 β”‚
β”‚                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                        β”‚
β”‚  β”‚ Your Channel    β”‚  πŸ’¬ Message appears!                   β”‚
β”‚  β”‚ #alerts or      β”‚                                        β”‚
β”‚  β”‚ #cloudwatch     β”‚  🚨 CW-RDS-AppName-prod-rds-db...      β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”„ Why This Architecture?

Alternative 1: CloudWatch β†’ Lambda directly

❌ Not supported by AWS (CloudWatch can’t directly invoke Lambda for alarms) ❌ Would need custom EventBridge rules (more complex)

Alternative 2: CloudWatch β†’ Email

❌ Not pretty, just plain text ❌ No formatting or colors ❌ Hard to filter/organize

Our Solution: CloudWatch β†’ SNS β†’ Lambda β†’ Slack

βœ… Standard AWS pattern (SNS is designed for this) βœ… Flexible (can add email, SMS, etc. to SNS later) βœ… Pretty Slack messages with formatting βœ… Easy to customize Lambda code βœ… Reliable (SNS handles retries)

πŸ“ Summary

SNS is the glue that connects CloudWatch alarms to Lambda functions. Think of SNS as a notification hub:
  • CloudWatch says: β€œHey SNS, I have an alarm!”
  • SNS says: β€œThanks! Let me notify all my subscribers”
  • Lambda (as subscriber) says: β€œGot it! Sending to Slack…”
Without SNS, you’d need complex EventBridge rules and more configuration. With SNS, it’s a simple, standard AWS pattern that works reliably.

πŸš€ Ready to Deploy?

Run these commands:
# 1. Validate
terraform validate

# 2. Preview what will be created
terraform plan

# 3. Deploy
terraform apply

# 4. Test
aws lambda invoke \
  --function-name CloudWatch-Alarms-To-Slack \
  --payload file://test-event.json \
  output.json
Your alarms will automatically flow through this pipeline: CloudWatch β†’ SNS β†’ Lambda β†’ Slack πŸŽ‰