TheArchitect

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.

Order Events S…Fulfillment Qu…Analytics QueueFulfillment Wo…Fulfillment Ro…Analytics Work…Analytics Role
SNS Fan-Out (SNS → SQS × 2 → Lambda) architecture diagram

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 IDResource type
OrderEventsSNSsnsAWS::SNS::Topic
FulfillmentQueuesqsAWS::SQS::Queue
AnalyticsQueuesqsAWS::SQS::Queue
FulfillmentWorkerlambdaAWS::Lambda::Function
FulfillmentRoleiamAWS::IAM::Role
AnalyticsWorkerlambdaAWS::Lambda::Function
AnalyticsRoleiamAWS::IAM::Role
OrderEventsSNSsnsFulfillmentQueuesqsQueuePolicyAWS::SQS::QueuePolicy
OrderEventsSNSsnsFulfillmentQueuesqsSubscriptionAWS::SNS::Subscription
OrderEventsSNSsnsAnalyticsQueuesqsQueuePolicyAWS::SQS::QueuePolicy
OrderEventsSNSsnsAnalyticsQueuesqsSubscriptionAWS::SNS::Subscription
FulfillmentWorkerlambdaFulfillmentQueuesqsEventSourceMappingAWS::Lambda::EventSourceMapping
AnalyticsWorkerlambdaAnalyticsQueuesqsEventSourceMappingAWS::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