{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "CFN template to create architecture represented at https://aws.amazon.com/blogs/compute/fanout-s3-event-notifications-to-multiple-endpoints/.", "Parameters": { "CodeBucket": { "Description": "S3 Bucket containing Lambda deployment packages and sub-stack templates", "Type": "String", "Default" : "awslambda-reference-architectures" }, "CodeKeyPrefix": { "Description": "The key prefix for all deployment packages and sub-stack templates within CodeBucket", "Type": "String", "Default" : "file-processing" } }, "Resources": { "InputBucket": { "Type": "AWS::S3::Bucket", "Properties": { "BucketName": {"Fn::Join" : ["-", [{"Ref" : "AWS::StackName"}, {"Ref" : "AWS::AccountId"}, "files"]]}, "NotificationConfiguration": { "TopicConfigurations": [ { "Event": "s3:ObjectCreated:*", "Topic": { "Ref" : "InputNotificationTopic" } } ] } }, "DependsOn": "NotificationPolicy" }, "OutputBucket": { "Type": "AWS::S3::Bucket", "Properties": { "BucketName": {"Fn::Join" : ["-", [{"Ref" : "InputBucket"}, "out"]]} } }, "InputNotificationTopic": { "Type": "AWS::SNS::Topic", "Properties": { "Subscription": [ { "Endpoint": { "Fn::GetAtt": [ "ProcessorFunctionOne", "Arn" ] }, "Protocol": "lambda" }, { "Endpoint": { "Fn::GetAtt": [ "ProcessorFunctionTwo", "Arn" ] }, "Protocol": "lambda" } ] } }, "NotificationPolicy": { "Type": "AWS::SNS::TopicPolicy", "Properties": { "PolicyDocument": { "Id": "PushBucketNotificationPolicy", "Version": "2012-10-17", "Statement": [ { "Sid": "AllowBucketToPushNotificationEffect", "Effect": "Allow", "Principal": { "Service": "s3.amazonaws.com" }, "Action": "sns:Publish", "Resource": { "Ref": "InputNotificationTopic" }, "Condition": { "ArnLike": { "aws:SourceArn": { "Fn::Join": [ "", [ "arn:aws:s3:*:*:", {"Fn::Join" : ["-", [{"Ref" : "AWS::StackName"}, {"Ref" : "AWS::AccountId"}, "files"]]} ] ] } } } } ] }, "Topics": [ { "Ref": "InputNotificationTopic" } ] } }, "ProcessorFunctionOne": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Ref": "CodeBucket" }, "S3Key": {"Fn::Join" : ["/", [{"Ref": "CodeKeyPrefix"}, "data-processor-1.zip"]]} }, "Description": "Data Processor One", "Handler": "data-processor-1.handler", "Role": { "Fn::GetAtt": [ "LambdaExecutionRole", "Arn" ] }, "Runtime": "nodejs10.x", "MemorySize": 128, "Timeout": 3 } }, "ProcessorFunctionTwo": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Ref": "CodeBucket" }, "S3Key": {"Fn::Join" : ["/", [{"Ref": "CodeKeyPrefix"}, "data-processor-2.zip"]]} }, "Description": "Data Processor Two", "Handler": "data-processor-2.handler", "Role": { "Fn::GetAtt": [ "LambdaExecutionRole", "Arn" ] }, "Runtime": "nodejs10.x", "MemorySize": 128, "Timeout": 3 } }, "LambdaExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/" } }, "RolePolicy": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyName": "root", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "InputBucket" }, "/*"]]} }, { "Effect": "Allow", "Action": [ "s3:PutObject" ], "Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "OutputBucket" }, "/*"]]} } ] }, "Roles": [ { "Ref": "LambdaExecutionRole" } ] } }, "LambdaInvokePermissionOne": { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName" : { "Fn::GetAtt" : ["ProcessorFunctionOne", "Arn"] }, "Action": "lambda:InvokeFunction", "Principal": "sns.amazonaws.com", "SourceArn" : { "Ref" : "InputNotificationTopic" } } }, "LambdaInvokePermissionTwo": { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName" : { "Fn::GetAtt" : ["ProcessorFunctionTwo", "Arn"] }, "Action": "lambda:InvokeFunction", "Principal": "sns.amazonaws.com", "SourceArn" : { "Ref" : "InputNotificationTopic" } } } }, "Outputs": { "Bucket": { "Description": "Storage location for data which is to be processed by Lambda functions", "Value": { "Ref": "InputBucket" } }, "BucketOut": { "Description": "Storage location for data which is to be processed by Lambda functions", "Value": { "Ref": "OutputBucket" } }, "Topic": { "Description": "SNS topic to fanout S3 Event notifications to Lambda functions", "Value": { "Ref": "InputNotificationTopic" } }, "ProcessorFxOne": { "Description": "Lambda function receiving SNS messages of S3 events", "Value": { "Ref": "ProcessorFunctionOne" } }, "ProcessorFxTwo": { "Description": "Lambda function receiving SNS messages of S3 events", "Value": { "Ref": "ProcessorFunctionTwo" } } } }