AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: > SAM template to deploy SNS dynamic subscribers # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: Function: Timeout: 10 Parameters: DynamicSubTopicName: Type: String Default: SNS-DynamicSubscription-Topic Resources: SNSDynamicSubsTopic: Type: AWS::SNS::Topic Properties: TopicName: !Ref DynamicSubTopicName SQSContainerMappingTable: Type: AWS::Serverless::SimpleTable Properties: TableName: SQSContainerMappping PrimaryKey: Name: id Type: String VPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.11.0.0/16 EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Join [":", [!Ref "AWS::StackName", "VPC"]] InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Join [":", [!Ref "AWS::StackName", "InternetGateway"]] VPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway RouteTable: DependsOn: VPCGatewayAttachment Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Join [":", [!Ref "AWS::StackName", "RouteTable"]] Route: Type: AWS::EC2::Route Properties: RouteTableId: !Ref RouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: 10.11.0.0/24 AvailabilityZone: !Select [0, !GetAZs ""] Tags: - Key: Name Value: !Join [":", [!Ref "AWS::StackName", "PublicSubnet1"]] PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: 10.11.1.0/24 AvailabilityZone: !Select [1, !GetAZs ""] Tags: - Key: Name Value: !Join [":", [!Ref "AWS::StackName", "PublicSubnet2"]] PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref RouteTable PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref RouteTable SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Limits security group ingress and egress traffic for the ECS tasks running in AWS Fargate VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 ECSTaskExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: [ecs-tasks.amazonaws.com] Action: ["sts:AssumeRole"] Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy ECSCluster: Type: AWS::ECS::Cluster Properties: ClusterName: sns-demo-cluster SNSDynamicSubscriberECSTaskDefinition: Type: AWS::ECS::TaskDefinition Properties: Family: sns-dynamic-subscriber-task Cpu: 512 Memory: 1024 NetworkMode: awsvpc RequiresCompatibilities: - FARGATE ExecutionRoleArn: !Ref ECSTaskExecutionRole TaskRoleArn: !Ref TaskRole ContainerDefinitions: - Name: sns-dynamic-subscriber-task Cpu: 512 Memory: 1024 Image: !Sub "681921237057.dkr.ecr.us-west-2.amazonaws.com/sns-demo/sns-dynamic-subscriber:latest" Environment: - Name: SERVICE_NAME Value: sns-dynamic-subscriber-service LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref SNSDemoCloudWatchLogsGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: sns-dynamic-subs-service SNSDynamicSubscriberECSService: Type: AWS::ECS::Service Properties: ServiceName: sns-dynamic-subscriber-service Cluster: !Ref ECSCluster LaunchType: FARGATE DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED # to be able to download images from ECR SecurityGroups: - !Ref SecurityGroup Subnets: - !Ref PublicSubnet1 - !Ref PublicSubnet2 TaskDefinition: !Ref SNSDynamicSubscriberECSTaskDefinition SNSDemoCloudWatchLogsGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: ecs/sns-demo/sns-dynamic-subscriber RetentionInDays: 30 TaskRole: Type: AWS::IAM::Role Properties: # RoleName: !Join ['', [!Ref ServiceName, TaskRole]] AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Action: "sts:AssumeRole" ManagedPolicyArns: - !Ref SQSReadPolicy - !Ref DDBReadWritePolicy SQSReadPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - sqs:* - ecs:* Resource: "*" DDBReadWritePolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - dynamodb:PutItem Resource: !GetAtt SQSContainerMappingTable.Arn # ECS Task running event SQSECSConsumerStartRule: Type: AWS::Events::Rule Properties: Name: "TaskRunningRule" Description: "ECS rule for ECS Task state running" RoleArn: !GetAtt EBRoleToLogs.Arn EventPattern: source: - aws.ecs detail-type: - "ECS Task State Change" detail: desiredStatus: - "RUNNING" lastStatus: - "RUNNING" State: "ENABLED" Targets: - Arn: Fn::GetAtt: - "SNSSQSSubscriptionResource" - "Arn" Id: "TargetId5" - Arn: Fn::GetAtt: - "ECSCWEventsLogsGroup" - "Arn" Id: "TargetId6" # ECS Task stopped event SQSECSConsumerStopRule: Type: AWS::Events::Rule Properties: Name: "TaskSoppedRule" Description: "ECS rule for ECS Task state stopped" RoleArn: !GetAtt EBRoleToLogs.Arn EventPattern: source: - aws.ecs detail-type: - "ECS Task State Change" detail: desiredStatus: - "STOPPED" lastStatus: - "STOPPED" State: "ENABLED" Targets: - Arn: Fn::GetAtt: - "SNSSQSCleanupResource" - "Arn" Id: "TargetId7" - Arn: Fn::GetAtt: - "ECSCWEventsLogsGroup" - "Arn" Id: "TargetId8" ECSCWEventsLogsGroup: Type: AWS::Logs::LogGroup Properties: RetentionInDays: 30 SNSSQSSubscriptionResource: Type: AWS::Serverless::Function Properties: CodeUri: sns-sqs-subscription-service/ Handler: app.lambda_handler Runtime: python3.8 Environment: Variables: SERVICE_NAME: SNS-SQS-SubscriptionService SQS_CONTAINER_MAPPING_TABLE: SQSContainerMappping TOPIC_NAME: Ref: DynamicSubTopicName Policies: - Statement: - Sid: CreateSNSSubscriptionPolicy Effect: "Allow" Action: - "sns:*" - "sqs:*" - "dynamodb:*" Resource: "*" SNSSQSCleanupResource: Type: AWS::Serverless::Function Properties: CodeUri: sns-sqs-cleanup-service/ Handler: app.lambda_handler Runtime: python3.8 Environment: Variables: SERVICE_NAME: SNS-SQS-CleanupService SQS_CONTAINER_MAPPING_TABLE: SQSContainerMappping TOPIC_NAME: Ref: DynamicSubTopicName Policies: - Statement: - Sid: SNSSQSCleanupPolicy Effect: "Allow" Action: - "sns:*" - "sqs:*" - "dynamodb:*" Resource: "*" ECSStartEventRulePermissionToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: Ref: "SNSSQSSubscriptionResource" Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: Fn::GetAtt: - "SQSECSConsumerStartRule" - "Arn" ECSStopEventRulePermissionToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: Ref: "SNSSQSCleanupResource" Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: Fn::GetAtt: - "SQSECSConsumerStopRule" - "Arn" EBRoleToLogs: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: - 'sts:AssumeRole' Policies: - PolicyName: "myPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: "arn:aws:logs:*:*:*" Outputs: # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function # Find out more about other implicit resources you can reference within SAM # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api SQSContainerMappingTable: Description: "Amazon DynamoDB table ARN for SQSContainer mapping table" Value: !Ref SQSContainerMappingTable