SNS Fan-Out (SNS → SQS × 2 → Lambda)
AWS architecture template · SAA-C03: SNS topic fans out to two SQS queues, each driving an independent Lambda worker — decoupled pub/sub pattern.
SNS Fan-Out (SNS → SQS × 2 → Lambda) is a free AWS architecture template built with IAM, Lambda, SQS and SNS. Open it in TheArchitect to edit the diagram, estimate monthly cost, and export the CloudFormation or Terraform that deploys it.
AWS services in this template
- IAM×2
- Lambda×2
- SQS×2
- SNS
Deployment notes
Deploy-ready
- SNS subscriptions and SQS event source mappings are pre-configured.
- Each Lambda worker has its own IAM execution role and SQS consume permissions.
Generated CloudFormation — 13 resources
| Logical ID | Resource type |
|---|---|
| OrderEventsSNSsns | AWS::SNS::Topic |
| FulfillmentQueuesqs | AWS::SQS::Queue |
| AnalyticsQueuesqs | AWS::SQS::Queue |
| FulfillmentWorkerlambda | AWS::Lambda::Function |
| FulfillmentRoleiam | AWS::IAM::Role |
| AnalyticsWorkerlambda | AWS::Lambda::Function |
| AnalyticsRoleiam | AWS::IAM::Role |
| OrderEventsSNSsnsFulfillmentQueuesqsQueuePolicy | AWS::SQS::QueuePolicy |
| OrderEventsSNSsnsFulfillmentQueuesqsSubscription | AWS::SNS::Subscription |
| OrderEventsSNSsnsAnalyticsQueuesqsQueuePolicy | AWS::SQS::QueuePolicy |
| OrderEventsSNSsnsAnalyticsQueuesqsSubscription | AWS::SNS::Subscription |
| FulfillmentWorkerlambdaFulfillmentQueuesqsEventSourceMapping | AWS::Lambda::EventSourceMapping |
| AnalyticsWorkerlambdaAnalyticsQueuesqsEventSourceMapping | AWS::Lambda::EventSourceMapping |
View the full CloudFormation template
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Generated by TheArchitect — https://www.thecloudarchitect.dev",
"Metadata": {
"TheArchitect": {
"Edges": [
{
"From": "OrderEventsSNSsns",
"To": "FulfillmentQueuesqs",
"Label": "subscribe"
},
{
"From": "OrderEventsSNSsns",
"To": "AnalyticsQueuesqs",
"Label": "subscribe"
},
{
"From": "FulfillmentQueuesqs",
"To": "FulfillmentWorkerlambda",
"Label": "trigger"
},
{
"From": "FulfillmentRoleiam",
"To": "FulfillmentWorkerlambda",
"Label": "execution role"
},
{
"From": "AnalyticsQueuesqs",
"To": "AnalyticsWorkerlambda",
"Label": "trigger"
},
{
"From": "AnalyticsRoleiam",
"To": "AnalyticsWorkerlambda",
"Label": "execution role"
}
]
}
},
"Resources": {
"OrderEventsSNSsns": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "saa-order-events",
"DisplayName": "Order Events Topic"
}
},
"FulfillmentQueuesqs": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": "saa-fulfillment-queue",
"VisibilityTimeout": 300,
"MessageRetentionPeriod": 345600,
"ReceiveMessageWaitTimeSeconds": 20
}
},
"AnalyticsQueuesqs": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": "saa-analytics-queue",
"VisibilityTimeout": 60,
"MessageRetentionPeriod": 86400,
"ReceiveMessageWaitTimeSeconds": 20
}
},
"FulfillmentWorkerlambda": {
"Type": "AWS::Lambda::Function",
"DependsOn": [
"FulfillmentRoleiam"
],
"Properties": {
"FunctionName": "saa-fulfillment-worker",
"Runtime": "nodejs20.x",
"Handler": "index.handler",
"MemorySize": 512,
"Architectures": [
"arm64"
],
"Timeout": 30,
"Description": "Processes order fulfillment events from SQS.",
"ReservedConcurrentExecutions": 10,
"Environment": {
"Variables": {
"APP_ENV": "demo",
"LOG_LEVEL": "info"
}
},
"TracingConfig": {
"Mode": "Active"
},
"Code": {
"ZipFile": "exports.handler = async (event) => ({ processed: Array.isArray(event.Records) ? event.Records.length : 1 });"
},
"Role": {
"Fn::GetAtt": [
"FulfillmentRoleiam",
"Arn"
]
}
}
},
"FulfillmentRoleiam": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": "saa-fulfillment-lambda-role",
"Description": "Execution role for fulfillment Lambda — SQS consume + CloudWatch Logs",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
"arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
]
}
},
"AnalyticsWorkerlambda": {
"Type": "AWS::Lambda::Function",
"DependsOn": [
"AnalyticsRoleiam"
],
"Properties": {
"FunctionName": "saa-analytics-worker",
"Runtime": "nodejs20.x",
"Handler": "index.handler",
"MemorySize": 512,
"Architectures": [
"arm64"
],
"Timeout": 30,
"Description": "Processes analytics events from SQS for downstream data pipelines.",
"Environment": {
"Variables": {
"APP_ENV": "demo",
"LOG_LEVEL": "info"
}
},
"TracingConfig": {
"Mode": "Active"
},
"Code": {
"ZipFile": "exports.handler = async (event) => ({ processed: Array.isArray(event.Records) ? event.Records.length : 1 });"
},
"Role": {
"Fn::GetAtt": [
"AnalyticsRoleiam",
"Arn"
]
}
}
},
"AnalyticsRoleiam": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": "saa-analytics-lambda-role",
"Description": "Execution role for analytics Lambda — SQS consume + CloudWatch Logs",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
"arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
]
}
},
"OrderEventsSNSsnsFulfillmentQueuesqsQueuePolicy": {
"Type": "AWS::SQS::QueuePolicy",
"DependsOn": [
"FulfillmentQueuesqs"
],
"Properties": {
"Queues": [
{
"Ref": "FulfillmentQueuesqs"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": {
"Fn::GetAtt": [
"FulfillmentQueuesqs",
"Arn"
]
},
"Condition": {
"ArnEquals": {
"aws:SourceArn": {
"Ref": "OrderEventsSNSsns"
}
}
}
}
]
}
}
},
"OrderEventsSNSsnsFulfillmentQueuesqsSubscription": {
"Type": "AWS::SNS::Subscription",
"DependsOn": [
"OrderEventsSNSsnsFulfillmentQueuesqsQueuePolicy"
],
"Properties": {
"TopicArn": {
"Ref": "OrderEventsSNSsns"
},
"Protocol": "sqs",
"Endpoint": {
"Fn::GetAtt": [
"FulfillmentQueuesqs",
"Arn"
]
}
}
},
"OrderEventsSNSsnsAnalyticsQueuesqsQueuePolicy": {
"Type": "AWS::SQS::QueuePolicy",
"DependsOn": [
"AnalyticsQueuesqs"
],
"Properties": {
"Queues": [
{
"Ref": "AnalyticsQueuesqs"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": {
"Fn::GetAtt": [
"AnalyticsQueuesqs",
"Arn"
]
},
"Condition": {
"ArnEquals": {
"aws:SourceArn": {
"Ref": "OrderEventsSNSsns"
}
}
}
}
]
}
}
},
"OrderEventsSNSsnsAnalyticsQueuesqsSubscription": {
"Type": "AWS::SNS::Subscription",
"DependsOn": [
"OrderEventsSNSsnsAnalyticsQueuesqsQueuePolicy"
],
"Properties": {
"TopicArn": {
"Ref": "OrderEventsSNSsns"
},
"Protocol": "sqs",
"Endpoint": {
"Fn::GetAtt": [
"AnalyticsQueuesqs",
"Arn"
]
}
}
},
"FulfillmentWorkerlambdaFulfillmentQueuesqsEventSourceMapping": {
"Type": "AWS::Lambda::EventSourceMapping",
"DependsOn": [
"FulfillmentWorkerlambda",
"FulfillmentQueuesqs"
],
"Properties": {
"EventSourceArn": {
"Fn::GetAtt": [
"FulfillmentQueuesqs",
"Arn"
]
},
"FunctionName": {
"Ref": "FulfillmentWorkerlambda"
},
"Enabled": true,
"BatchSize": 10
}
},
"AnalyticsWorkerlambdaAnalyticsQueuesqsEventSourceMapping": {
"Type": "AWS::Lambda::EventSourceMapping",
"DependsOn": [
"AnalyticsWorkerlambda",
"AnalyticsQueuesqs"
],
"Properties": {
"EventSourceArn": {
"Fn::GetAtt": [
"AnalyticsQueuesqs",
"Arn"
]
},
"FunctionName": {
"Ref": "AnalyticsWorkerlambda"
},
"Enabled": true,
"BatchSize": 10
}
}
}
}Generated by TheArchitect from the diagram above. Review values such as names, CIDR ranges and instance sizes before deploying to your account.
Related templates
- Twitter/X Scale TestCloudFront behavior split, API Gateway VPC Link, public/internal ALBs, private app/data subnets, queues, media, and data stores
- Serverless Event ProcessingSAA-C03: WAF + API Gateway → Lambda → SQS → Lambda Worker → DynamoDB + SNS
- Step Functions OrchestrationSAA-C03: API Gateway → Step Functions → Lambda tasks + SQS + DynamoDB + SNS