cloudformation.yaml (1,654 lines of code) (raw):
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Validates mobile purchases
Parameters:
MobileAccountId:
Type: AWS::SSM::Parameter::Value<String>
Default: 'mobileAccountId'
MembershipAccountId:
Type: AWS::SSM::Parameter::Value<String>
Default: 'membershipAccountId'
Stack:
Description: Stack name
Type: String
App:
Description: Application name
Type: String
Stage:
Description: Stage name
Type: String
AllowedValues:
- CODE
- PROD
DeployBucket:
Description: Bucket where RiffRaff uploads artifacts on deploy
Type: String
HostedZoneId:
Description: HostedZoneId
Type: String
HostedZoneName:
Description: HostedZoneName
Type: String
ApiCertArn:
Description: ACM Certificate for api use
Type: String
AppCertArn:
Description: ACM Certificate for app use
Type: String
AppDNS:
Description: DNS used by app
Type: String
GooglePubSubSecret:
Type: String
Description: The secret used by google's pubsub
NoEcho: true
ApplePubSubSecret:
Type: String
Description: The secret used by apple's pubsub
NoEcho: true
FeastApplePubSubSecret:
Type: String
Description: The secret used by apple's pubsub for the feast app
NoEcho: true
FeastGooglePubSubSecret:
Type: String
Description: The secret used by google's pubsub for the feast app
NoEcho: true
AlarmTopic:
Type: String
Description: The ARN of the SNS topic to send all the cloudwatch alarms to
Mappings:
StageVariables:
CODE:
Schedule: 'rate(365 days)'
AlarmActionsEnabled: FALSE
SoftOptInConsentSetterStage: CODE
PROD:
Schedule: 'rate(30 minutes)'
AlarmActionsEnabled: TRUE
SoftOptInConsentSetterStage: PROD
Conditions:
IsCode: !Equals [!Ref "Stage", "CODE"]
IsProd: !Equals [!Ref "Stage", "PROD"]
Resources:
MobilePurchasesLambdasRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: logs
PolicyDocument:
Statement:
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- cloudwatch:putMetricData
Resource: "*"
- PolicyName: config
PolicyDocument:
Statement:
Action:
- ssm:GetParametersByPath
Effect: Allow
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${App}/${Stage}/${Stack}/*
- PolicyName: iosuserpurchases-config
PolicyDocument:
Statement:
Action:
- ssm:GetParametersByPath
Effect: Allow
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${App}-iosuserpurchases/${Stage}/${Stack}
- PolicyName: iosvalidatereceipts-config
PolicyDocument:
Statement:
Action:
- ssm:GetParametersByPath
Effect: Allow
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${App}-iosvalidatereceipts/${Stage}/${Stack}
- PolicyName: googleoauth-config
PolicyDocument:
Statement:
Action:
- ssm:GetParametersByPath
Effect: Allow
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${App}/${Stage}/google-oauth-lambda
- PolicyName: google-access-tokens
PolicyDocument:
Statement:
Action:
- s3:GetObject
- s3:PutObject
Effect: Allow
Resource: !Sub arn:aws:s3:::gu-mobile-access-tokens/${Stage}/google-play-developer-api/*
- PolicyName: dynamo
PolicyDocument:
Statement:
Effect: Allow
Action:
- "dynamodb:GetItem"
- "dynamodb:BatchGetItem"
- "dynamodb:BatchWriteItem"
- "dynamodb:PutItem"
- "dynamodb:UpdateItem"
- "dynamodb:Query"
Resource:
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-${Stack}-user-purchases
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscription-events
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscription-events-v2
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscriptions
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscriptions-parallel-test
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-user-subscriptions
- PolicyName: Sqs
PolicyDocument:
Statement:
Effect: Allow
Action: sqs:*
Resource:
- !GetAtt GoogleSubscriptionsQueue.Arn
- !GetAtt AppleSubscriptionsQueue.Arn
- !GetAtt FeastAppleSubscriptionsQueue.Arn
- !GetAtt FeastGoogleSubscriptionsQueue.Arn
- !Sub arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:${App}-${Stage}-apple-historical-subscriptions
- !Sub arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:${App}-${Stage}-google-historical-subscriptions
- PolicyName: Kms
PolicyDocument:
Statement:
Effect: Allow
Action: ["kms:GenerateDataKey", "kms:Decrypt"]
Resource:
- !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/0215d06c-81c4-4896-a5da-c818770ea8db
LogGroupValidateReceipts:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Sub /aws/lambda/${App}-iosvalidatereceipts-${Stage}
RetentionInDays: 7
LogGroupUserPurchases:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Sub /aws/lambda/${App}-iosuserpurchases-${Stage}
RetentionInDays: 7
MobilePuchasesApi:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage
MethodSettings:
[
{
"MetricsEnabled": True,
"LoggingLevel": "OFF",
"ResourcePath": "/*",
"HttpMethod": "*",
},
]
DefinitionBody:
swagger: "2.0"
info:
version: "1.0.0"
title: !Sub ${App}-${Stage}
paths:
"/google/pubsub":
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GooglePubSubLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "Successfully processed a Live App purchase receipt from Google Play Store"
"/google/subscription/{subscriptionId}/status":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GooglePlaySubStatusLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "200 response"
"/apple/subscription/status":
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AppleSubStatusLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "200 response"
"/apple/pubsub":
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApplePubSubLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "Successfully processed a Live App purchase receipt from the Apple App Store"
"/apple/linkToSubscriptions":
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AppleLinkUserToSubLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "Successfully handled link request from iOS Live App"
"/google/linkToSubscriptions":
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GoogleLinkUserToSubLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "Successfully handled link request from Android Live App"
"/user/subscriptions/{userId}":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserSubscriptionsLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "200 response"
"/apple/fetchOfferDetails":
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AppleFetchOfferDetailsLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "200 response (+)"
"feast/apple/pubsub":
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FeastApplePubSubLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "Successfully processed a Feast App purchase receipt from the Apple App Store"
"/feast/google/pubsub":
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FeastGooglePubSubLambda.Arn}/invocations
consumes: [application/json]
produces: [application/json]
responses:
"200":
"description": "Successfully processed a Feast App purchase receipt from the Google Play Store"
"/healthcheck":
get:
responses:
"200":
description: "200 response"
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
httpMethod: GET
responses:
default:
statusCode: "200"
ApiDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
CertificateArn: !Ref ApiCertArn
DomainName: !Sub ${App}.${HostedZoneName}
AppDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
CertificateArn: !Ref AppCertArn
DomainName: !Ref AppDNS
ApiRoute53:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Ref HostedZoneId
RecordSets:
- Name: !Ref ApiDomainName
Type: A
AliasTarget:
HostedZoneId: Z2FDTNDATAQYW2
DNSName: !GetAtt
- ApiDomainName
- DistributionDomainName
ApiMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
DomainName: !Ref ApiDomainName
RestApiId: !Ref MobilePuchasesApi
Stage: !Ref Stage
AppMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
DomainName: !Ref AppDomainName
RestApiId: !Ref MobilePuchasesApi
Stage: !Ref Stage
GoogleOAuthLambda:
Type: AWS::Serverless::Function
Properties:
Handler: com.gu.mobilepurchases.googleoauth.lambda.GoogleOAuth::handler
Runtime: java8
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-google-oauth/${App}-google-oauth.jar
FunctionName: !Sub ${App}-googleoauth-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Description: Fetches access tokens for the Google Play Developer API
MemorySize: 512
Timeout: 45
Events:
Schedule:
Type: Schedule
Properties:
Schedule: rate(15 minutes)
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
GooglePubSubLambda:
Type: AWS::Serverless::Function
Properties:
Handler: google-pubsub.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-google-pubsub/google-pubsub.zip
FunctionName: !Sub ${App}-googlepubsub-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Secret: !Ref GooglePubSubSecret
QueueUrl: !Ref GoogleSubscriptionsQueue
Description: Records play store events
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/google/pubsub"
Method: POST
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
ApplePubSubLambda:
Type: AWS::Serverless::Function
Properties:
Handler: apple-pubsub.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-apple-pubsub/apple-pubsub.zip
FunctionName: !Sub ${App}-applepubsub-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Secret: !Ref ApplePubSubSecret
QueueUrl: !Ref AppleSubscriptionsQueue
Description: Records play store events
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/apple/pubsub"
Method: POST
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
GoogleLinkUserToSubLambda:
Type: AWS::Serverless::Function
Properties:
Handler: google-link-user-subscription.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-google-link-user-subscription/google-link-user-subscription.zip
FunctionName: !Sub ${App}-google-link-user-subscription-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
QueueUrl: !Ref GoogleSubscriptionsQueue
Description: Links users to subscriptions
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/google/linkToSubscriptions"
Method: POST
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
AppleLinkUserToSubLambda:
Type: AWS::Serverless::Function
Properties:
Handler: apple-link-user-subscription.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-apple-link-user-subscription/apple-link-user-subscription.zip
FunctionName: !Sub ${App}-apple-link-user-subscription-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
QueueUrl: !Ref AppleSubscriptionsQueue
Description: Links users to subscriptions
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/apple/linkToSubscriptions"
Method: POST
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
GooglePlaySubStatusLambda:
Type: AWS::Serverless::Function
Properties:
Handler: google-subscription-status.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-google-subscription-status/google-subscription-status.zip
FunctionName: !Sub ${App}-google-subscription-status-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Description: Checks the status of a Play Store subscription using the Google Play Developer API
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/google/subscription/{subscriptionId}/status"
Method: GET
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
AppleSubStatusLambda:
Type: AWS::Serverless::Function
Properties:
Handler: apple-subscription-status.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-apple-subscription-status/apple-subscription-status.zip
FunctionName: !Sub ${App}-apple-subscription-status-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Description: Checks the status of an Apple App store subscription using the apple API
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/apple/subscription/status"
Method: POST
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
AppleFetchOfferDetailsLambda:
Type: AWS::Serverless::Function
Properties:
Handler: apple-fetch-offer-details.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-apple-fetch-offer-details/apple-fetch-offer-details.zip
FunctionName: !Sub ${App}-apple-fetch-offer-details-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Description: apple fetch offer details lambda (+)
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/apple/fetchOfferDetails"
Method: POST
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
FeastGooglePubSubLambda:
Type: AWS::Serverless::Function
Properties:
Handler: feast-google-pubsub.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-feast-google-pubsub/feast-google-pubsub.zip
FunctionName: !Sub ${App}-feastgooglepubsub-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Secret: !Ref FeastGooglePubSubSecret
QueueUrl: !Ref FeastGoogleSubscriptionsQueue
Description: Records Google play store events for Feast app
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/feast/google/pubsub"
Method: POST
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
FeastApplePubSubLambda:
Type: AWS::Serverless::Function
Properties:
Handler: feast-apple-pubsub.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-feast-apple-pubsub/feast-apple-pubsub.zip
FunctionName: !Sub ${App}-feastapplepubsub-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
QueueUrl: !Ref FeastAppleSubscriptionsQueue
Secret: !Ref FeastApplePubSubSecret
Description: Records app store events for Feast app
MemorySize: 128
Timeout: 29
Events:
PostApi:
Type: Api
Properties:
Path: "/feast/apple/pubsub"
Method: POST
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
GoogleSubscriptionsQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-${Stage}-google-subscriptions-to-fetch
RedrivePolicy:
deadLetterTargetArn: !GetAtt GoogleSubscriptionsQueueDlq.Arn
maxReceiveCount: 8
KmsMasterKeyId: alias/aws/sqs
Tags:
- Key: Stage
Value: !Ref Stage
- Key: Stack
Value: !Ref Stack
- Key: App
Value: !Ref App
GoogleSubscriptionsQueueDlq:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-${Stage}-google-subscriptions-to-fetch-dlq
KmsMasterKeyId: alias/aws/sqs
Tags:
- Key: Stage
Value: !Ref Stage
- Key: Stack
Value: !Ref Stack
- Key: App
Value: !Ref App
AppleSubscriptionsQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-${Stage}-apple-subscriptions-to-fetch
RedrivePolicy:
deadLetterTargetArn: !GetAtt AppleSubscriptionsQueueDlq.Arn
maxReceiveCount: 8
KmsMasterKeyId: alias/aws/sqs
Tags:
- Key: Stage
Value: !Ref Stage
- Key: Stack
Value: !Ref Stack
- Key: App
Value: !Ref App
AppleSubscriptionsQueueDlq:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-${Stage}-apple-subscriptions-to-fetch-dlq
KmsMasterKeyId: alias/aws/sqs
Tags:
- Key: Stage
Value: !Ref Stage
- Key: Stack
Value: !Ref Stack
- Key: App
Value: !Ref App
FeastAppleSubscriptionsQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-${Stage}-feast-apple-subscriptions-to-fetch
RedrivePolicy:
deadLetterTargetArn: !GetAtt FeastAppleSubscriptionsQueueDlq.Arn
maxReceiveCount: 8
KmsMasterKeyId: alias/aws/sqs
Tags:
- Key: Stage
Value: !Ref Stage
- Key: Stack
Value: !Ref Stack
- Key: App
Value: !Ref App
FeastAppleSubscriptionsQueueDlq:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-${Stage}-feast-apple-subscriptions-to-fetch-dlq
KmsMasterKeyId: alias/aws/sqs
Tags:
- Key: Stage
Value: !Ref Stage
- Key: Stack
Value: !Ref Stack
- Key: App
Value: !Ref App
FeastGoogleSubscriptionsQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-${Stage}-feast-google-subscriptions-to-fetch
RedrivePolicy:
deadLetterTargetArn: !GetAtt FeastGoogleSubscriptionsQueueDlq.Arn
maxReceiveCount: 8
KmsMasterKeyId: alias/aws/sqs
Tags:
- Key: Stage
Value: !Ref Stage
- Key: Stack
Value: !Ref Stack
- Key: App
Value: !Ref App
FeastGoogleSubscriptionsQueueDlq:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-${Stage}-feast-google-subscriptions-to-fetch-dlq
KmsMasterKeyId: alias/aws/sqs
Tags:
- Key: Stage
Value: !Ref Stage
- Key: Stack
Value: !Ref Stack
- Key: App
Value: !Ref App
GoogleTokenRefreshFailureAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
AlarmName: !Sub mobile-purchases-${Stage}-google-oauth-token-refresh-failure
AlarmDescription: !Sub Trigger the GoogleOAuth lambda manually to refresh the token
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: FunctionName
Value: !Ref GoogleOAuthLambda
EvaluationPeriods: 1
MetricName: Errors
Namespace: AWS/Lambda
Period: 60
Statistic: Sum
Threshold: 1
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-google-oauth
GooglePlaySubsStatus5xxErrors:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
AlarmName: !Sub mobile-purchases-${Stage}-play-subscription-status-check-errors
AlarmDescription: |
More than 20% of attempts to check Play subscription status resulted in a 5XX error.
Runbook: https://docs.google.com/document/d/1OwNDf_xSK3hhq1K0DP_4Uq7vIBFgfvlkd4zfiRZDczw/edit#heading=h.cnuchxbdu0tl
Metrics:
- Id: e1
Label: Percentage of requests which result in a 5XX error
Expression: "100*(FILL(m1,0)/FILL(m2,1))"
- Id: m1
Label: Number of 5XX responses
MetricStat:
Metric:
MetricName: 5XXError
Namespace: AWS/ApiGateway
Dimensions:
- Name: ApiName
Value: !Sub ${App}-${Stage}
- Name: Method
Value: GET
- Name: Resource
Value: /google/subscription/{subscriptionId}/status
- Name: Stage
Value: !Ref Stage
Period: 600
Stat: Sum
ReturnData: false
- Id: m2
Label: Total number of requests
MetricStat:
Metric:
MetricName: Count
Namespace: AWS/ApiGateway
Dimensions:
- Name: ApiName
Value: !Sub ${App}-${Stage}
- Name: Method
Value: GET
- Name: Resource
Value: /google/subscription/{subscriptionId}/status
- Name: Stage
Value: !Ref Stage
Period: 600
Stat: Sum
ReturnData: false
ComparisonOperator: GreaterThanOrEqualToThreshold
EvaluationPeriods: 1
Threshold: 20
Tags:
- Key: App
Value: mobile-purchases-google-subscription-status
UpdateGoogleSubscriptionsLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${App}-google-update-subscriptions-${Stage}
Code:
S3Bucket: !Ref DeployBucket
S3Key: !Sub ${Stack}/${Stage}/${App}-google-update-subscriptions/google-update-subscriptions.zip
Environment:
Variables:
App: !Sub ${App}
Stack: !Sub ${Stack}
Stage: !Sub ${Stage}
HistoricalQueueUrl: !Sub https://sqs.${AWS::Region}.amazonaws.com/${AWS::AccountId}/${App}-${Stage}-google-historical-subscriptions
Description: Consomes subscription data updates from google playstore from sqs and stores them in dynamo
Handler: google-update-subscriptions.handler
MemorySize: 512
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Timeout: 25
Runtime: nodejs20.x
UpdateGoogleSubscriptionsEventSource:
Type: AWS::Lambda::EventSourceMapping
Properties:
FunctionName: !Ref UpdateGoogleSubscriptionsLambda
Enabled: true
EventSourceArn: !GetAtt GoogleSubscriptionsQueue.Arn
BatchSize: 1
AppleSubsStatus5xxErrors:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmActions:
- Ref: AlarmTopic
AlarmName: !Sub mobile-purchases-${Stage}-apple-subscription-status-check-errors
AlarmDescription: |
More than 10% of attempts to check Apple subscription status resulted in a 5XX error over a 20min period.
Metrics:
- Id: e1
Label: Percentage of requests which result in a 5XX error
Expression: "100*(FILL(m1,0)/FILL(m2,1))"
- Id: m1
Label: Number of 5XX responses
MetricStat:
Metric:
MetricName: 5XXError
Namespace: AWS/ApiGateway
Dimensions:
- Name: ApiName
Value: !Sub ${App}-${Stage}
- Name: Method
Value: POST
- Name: Resource
Value: /apple/subscription/status
- Name: Stage
Value: !Ref Stage
Period: 1200
Stat: Sum
ReturnData: false
- Id: m2
Label: Total number of requests
MetricStat:
Metric:
MetricName: Count
Namespace: AWS/ApiGateway
Dimensions:
- Name: ApiName
Value: !Sub ${App}-${Stage}
- Name: Method
Value: POST
- Name: Resource
Value: /apple/subscription/status
- Name: Stage
Value: !Ref Stage
Period: 1200
Stat: Sum
ReturnData: false
ComparisonOperator: GreaterThanOrEqualToThreshold
EvaluationPeriods: 1
Threshold: 10
Tags:
- Key: App
Value: mobile-purchases-apple-subscription-status
ApplePubsub5xxErrors:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
AlarmName: !Sub mobile-purchases-${Stage}-apple-pubsub-check-errors
AlarmDescription: Two HTTP requests to the iOS pubsub endpoint resulted in 5XX errors.
Dimensions:
- Name: ApiName
Value: !Sub ${App}-${Stage}
- Name: Method
Value: POST
- Name: Resource
Value: /apple/pubsub
- Name: Stage
Value: !Ref Stage
ComparisonOperator: GreaterThanOrEqualToThreshold
EvaluationPeriods: 1
MetricName: 5XXError
Namespace: AWS/ApiGateway
Period: 300
Statistic: Sum
Threshold: 2
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-apple-pubsub
FeastApplePubsub5xxErrors:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
AlarmName: !Sub mobile-purchases-${Stage}-feast-apple-pubsub-check-errors
AlarmDescription: Two HTTP requests to the iOS Feast pubsub endpoint resulted in 5XX errors.
Dimensions:
- Name: ApiName
Value: !Sub ${App}-${Stage}
- Name: Method
Value: POST
- Name: Resource
Value: /feast/apple/pubsub
- Name: Stage
Value: !Ref Stage
ComparisonOperator: GreaterThanOrEqualToThreshold
EvaluationPeriods: 1
MetricName: 5XXError
Namespace: AWS/ApiGateway
Period: 300
Statistic: Sum
Threshold: 2
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-feast-apple-pubsub
GooglePubsub5xxErrors:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
AlarmName: !Sub mobile-purchases-${Stage}-google-pubsub-check-errors
AlarmDescription: Two HTTP requests to the Google pubsub endpoint resulted in 5XX errors.
Dimensions:
- Name: ApiName
Value: !Sub ${App}-${Stage}
- Name: Method
Value: POST
- Name: Resource
Value: /google/pubsub
- Name: Stage
Value: !Ref Stage
ComparisonOperator: GreaterThanOrEqualToThreshold
EvaluationPeriods: 1
MetricName: 5XXError
Namespace: AWS/ApiGateway
Period: 300
Statistic: Sum
Threshold: 2
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-google-pubsub
FeastGooglePubsub5xxErrors:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
AlarmName: !Sub mobile-purchases-${Stage}-feast-google-pubsub-check-errors
AlarmDescription: Two HTTP requests to the Feast Google pubsub endpoint resulted in 5XX errors.
Dimensions:
- Name: ApiName
Value: !Sub ${App}-${Stage}
- Name: Method
Value: POST
- Name: Resource
Value: /feast/google/pubsub
- Name: Stage
Value: !Ref Stage
ComparisonOperator: GreaterThanOrEqualToThreshold
EvaluationPeriods: 1
MetricName: 5XXError
Namespace: AWS/ApiGateway
Period: 300
Statistic: Sum
Threshold: 2
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-feast-google-pubsub
UserSubscriptionsLambda:
Type: AWS::Serverless::Function
Properties:
Handler: user-subscriptions.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-user-subscriptions/user-subscriptions.zip
FunctionName: !Sub ${App}-user-subscriptions-${Stage}
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Environment:
Variables:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Description: Retrieves subscription details for a given user
MemorySize: 128
Timeout: 29
Events:
InternalAccess:
Type: Api
Properties:
Path: "/user/subscriptions/{userId}"
Method: GET
RestApiId: !Ref MobilePuchasesApi
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
UpdateAppleSubscriptionsLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${App}-apple-update-subscriptions-${Stage}
Code:
S3Bucket: !Ref DeployBucket
S3Key: !Sub ${Stack}/${Stage}/${App}-apple-update-subscriptions/apple-update-subscriptions.zip
Environment:
Variables:
App: !Sub ${App}
Stack: !Sub ${Stack}
Stage: !Sub ${Stage}
HistoricalQueueUrl: !Sub https://sqs.${AWS::Region}.amazonaws.com/${AWS::AccountId}/${App}-${Stage}-apple-historical-subscriptions
Description: Consumes subscription data updates from app store from sqs and stores them in dynamo
Handler: apple-update-subscriptions.handler
MemorySize: 512
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Timeout: 25
Runtime: nodejs20.x
AppleSubscriptionsEventSource:
Type: AWS::Lambda::EventSourceMapping
Properties:
FunctionName: !Ref UpdateAppleSubscriptionsLambda
Enabled: true
EventSourceArn: !GetAtt AppleSubscriptionsQueue.Arn
BatchSize: 1
AppleSubscriptionDlqDepthAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmDescription: "Ensure that the apple subscription dead letter queue is empty"
Namespace: "AWS/SQS"
MetricName: ApproximateNumberOfMessagesVisible
Dimensions:
- Name: QueueName
Value: !GetAtt "AppleSubscriptionsQueueDlq.QueueName"
Period: 60
Statistic: Sum
EvaluationPeriods: 1
ComparisonOperator: GreaterThanThreshold
Threshold: 0
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-apple-update-subscriptions
GoogleSubscriptionDlqDepthAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmDescription: "Ensure that the google subscription dead letter queue is empty"
Namespace: "AWS/SQS"
MetricName: ApproximateNumberOfMessagesVisible
Dimensions:
- Name: QueueName
Value: !GetAtt "GoogleSubscriptionsQueueDlq.QueueName"
Period: 60
Statistic: Sum
EvaluationPeriods: 1
ComparisonOperator: GreaterThanThreshold
Threshold: 0
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-google-update-subscriptions
UpdateFeastAppleSubscriptionsLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${App}-feast-apple-update-subscriptions-${Stage}
Code:
S3Bucket: !Ref DeployBucket
S3Key: !Sub ${Stack}/${Stage}/${App}-feast-apple-update-subscriptions/feast-apple-update-subscriptions.zip
Environment:
Variables:
App: !Sub ${App}
Stack: !Sub ${Stack}
Stage: !Sub ${Stage}
HistoricalQueueUrl: !Sub https://sqs.${AWS::Region}.amazonaws.com/${AWS::AccountId}/${App}-${Stage}-apple-historical-subscriptions
Description: Consumes Feast subscription data updates from app store from sqs and stores them in dynamo
Handler: feast-apple-update-subscriptions.handler
MemorySize: 512
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Timeout: 25
Runtime: nodejs20.x
UpdateFeastGoogleSubscriptionsLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${App}-feast-google-update-subscriptions-${Stage}
Code:
S3Bucket: !Ref DeployBucket
S3Key: !Sub ${Stack}/${Stage}/${App}-feast-google-update-subscriptions/feast-google-update-subscriptions.zip
Environment:
Variables:
App: !Sub ${App}
Stack: !Sub ${Stack}
Stage: !Sub ${Stage}
HistoricalQueueUrl: !Sub https://sqs.${AWS::Region}.amazonaws.com/${AWS::AccountId}/${App}-${Stage}-google-historical-subscriptions
Description: Consumes Feast subscription data updates from Google Play store from sqs and stores them in dynamo
Handler: feast-google-update-subscriptions.handler
MemorySize: 512
Role: !GetAtt MobilePurchasesLambdasRole.Arn
Timeout: 25
Runtime: nodejs20.x
FeastAppleSubscriptionsEventSource:
Type: AWS::Lambda::EventSourceMapping
Properties:
FunctionName: !Ref UpdateFeastAppleSubscriptionsLambda
Enabled: true
EventSourceArn: !GetAtt FeastAppleSubscriptionsQueue.Arn
BatchSize: 1
FeastGoogleSubscriptionsEventSource:
Type: AWS::Lambda::EventSourceMapping
Properties:
FunctionName: !Ref UpdateFeastGoogleSubscriptionsLambda
Enabled: true
EventSourceArn: !GetAtt FeastGoogleSubscriptionsQueue.Arn
BatchSize: 1
FeastAppleSubscriptionDlqDepthAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [StageVariables, !Ref Stage, AlarmActionsEnabled]
AlarmDescription: "The Feast Apple subscription dead letter queue is over the threshold"
Namespace: "AWS/SQS"
MetricName: ApproximateNumberOfMessagesVisible
Dimensions:
- Name: QueueName
Value: !GetAtt "FeastAppleSubscriptionsQueueDlq.QueueName"
Period: 60
Statistic: Sum
EvaluationPeriods: 1
ComparisonOperator: GreaterThanThreshold
Threshold: 10
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-feast-apple-update-subscriptions
FeastGoogleSubscriptionDlqDepthAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
ActionsEnabled:
!FindInMap [ StageVariables, !Ref Stage, AlarmActionsEnabled ]
AlarmDescription: "Ensure that the Feast Android subscription dead letter queue is empty"
Namespace: "AWS/SQS"
MetricName: ApproximateNumberOfMessagesVisible
Dimensions:
- Name: QueueName
Value: !GetAtt "FeastGoogleSubscriptionsQueueDlq.QueueName"
Period: 60
Statistic: Sum
EvaluationPeriods: 1
ComparisonOperator: GreaterThanThreshold
Threshold: 0
AlarmActions:
- Ref: AlarmTopic
OKActions:
- Ref: AlarmTopic
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-feast-google-update-subscriptions
AppleRevalidateSubscriptionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: logs
PolicyDocument:
Statement:
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- cloudwatch:putMetricData
Resource: "*"
- PolicyName: dynamo
PolicyDocument:
Statement:
Effect: Allow
Action:
- "dynamodb:Scan"
Resource:
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscriptions/index/ios-endTimestamp-revalidation-index-with-platform
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscriptions
- PolicyName: sqs
PolicyDocument:
Statement:
Effect: Allow
Action:
- "sqs:SendMessage"
Resource:
- !Sub arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:${App}-${Stage}-apple-subscriptions-to-fetch
- !Sub arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:${App}-${Stage}-feast-apple-subscriptions-to-fetch
DeleteUserSubscriptionLambda:
Type: AWS::Serverless::Function
Properties:
Handler: delete-user-subscription.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-delete-user-subscription/delete-user-subscription.zip
FunctionName: !Sub ${App}-delete-user-subscription-${Stage}
Environment:
Variables:
App: !Sub ${App}
Stack: !Sub ${Stack}
Stage: !Sub ${Stage}
Description: Delete the link to user IDs when the subscription has reached its end of life
MemorySize: 512
Timeout: 60
Events:
Schedule:
Type: DynamoDB
Properties:
Stream:
Fn::ImportValue:
!Sub ${App}-${Stage}-subscriptions-stream-arn
StartingPosition: LATEST
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Policies:
- Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- cloudwatch:putMetricData
Resource: "*"
- Statement:
- Effect: Allow
Action:
- "dynamodb:GetRecords"
- "dynamodb:GetShardIterator"
- "dynamodb:DescribeStream"
- "dynamodb:ListStreams"
Resource:
- Fn::ImportValue:
!Sub ${App}-${Stage}-subscriptions-stream-arn
- Statement:
- Effect: Allow
Action:
- "dynamodb:Query"
- "dynamodb:DeleteItem"
Resource:
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-user-subscriptions
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-user-subscriptions/*
- Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource:
!Sub
- "arn:aws:iam::${MembershipAccountId}:role/membership-${SoftOptInConsentSetterStage}-soft-opt-in-consent-setter-QueueCrossAccountRole"
- SoftOptInConsentSetterStage: !FindInMap [ StageVariables, !Ref Stage, SoftOptInConsentSetterStage ]
- Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Sub "arn:aws:iam::${MembershipAccountId}:role/comms-${Stage}-EmailQueueCrossAccountRole"
- Statement:
- Effect: Allow
Action:
- ssm:GetParametersByPath
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${App}/${Stage}/${Stack}/*
AppleRevalidateReceiptsLambda:
Type: AWS::Serverless::Function
Properties:
Handler: apple-revalidate-receipts.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-apple-revalidate-receipts/apple-revalidate-receipts.zip
FunctionName: !Sub ${App}-apple-revalidate-receipts-${Stage}
Role: !GetAtt AppleRevalidateSubscriptionRole.Arn
Environment:
Variables:
App: !Sub ${App}
Stack: !Sub ${Stack}
Stage: !Sub ${Stage}
LiveAppSqsUrl: !Ref AppleSubscriptionsQueue
FeastAppSqsUrl: !Ref FeastAppleSubscriptionsQueue
Description: Finds recently expired subscriptions
MemorySize: 2048
Timeout: 180
Events:
Schedule:
Type: Schedule
Properties:
Schedule: rate(6 hours)
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
AcquisitionsDeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${App}-soft-opt-in-acquisitions-DLQ-${Stage}
SoftOptInAcquisitionsLambda:
Type: AWS::Serverless::Function
Properties:
Handler: soft-opt-in-acquisitions.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-soft-opt-in-acquisitions/soft-opt-in-acquisitions.zip
FunctionName: !Sub ${App}-soft-opt-in-acquisitions-${Stage}
Environment:
Variables:
App: !Sub ${App}
Stack: !Sub ${Stack}
Stage: !Sub ${Stage}
DLQUrl: !Ref AcquisitionsDeadLetterQueue
Description: Trigger setting soft-opt-in consents and sending emails based on Dynamo events
MemorySize: 512
Timeout: 60
Events:
Schedule:
Type: DynamoDB
Properties:
Stream:
Fn::ImportValue: !Sub ${App}-${Stage}-user-subscriptions-stream-arn
StartingPosition: LATEST
MaximumRecordAgeInSeconds: 28800
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Policies:
- Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource:
!Sub
- "arn:aws:iam::${MembershipAccountId}:role/membership-${SoftOptInConsentSetterStage}-soft-opt-in-consent-setter-QueueCrossAccountRole"
- SoftOptInConsentSetterStage: !FindInMap [ StageVariables, !Ref Stage, SoftOptInConsentSetterStage ]
- Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Sub "arn:aws:iam::${MembershipAccountId}:role/comms-${Stage}-EmailQueueCrossAccountRole"
- Statement:
- Effect: Allow
Action:
- "dynamodb:Query"
- "dynamodb:GetItem"
Resource:
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscriptions
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscriptions/*
- Statement:
- Effect: Allow
Action:
- ssm:GetParametersByPath
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${App}/${Stage}/${Stack}/*
- Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- cloudwatch:putMetricData
Resource: "*"
- Statement:
- Effect: Allow
Action:
- "dynamodb:GetRecords"
- "dynamodb:GetShardIterator"
- "dynamodb:DescribeStream"
- "dynamodb:ListStreams"
Resource:
- Fn::ImportValue: !Sub ${App}-${Stage}-user-subscriptions-stream-arn
- Statement:
- Effect: Allow
Action:
- sqs:DeleteMessage
- sqs:GetQueueAttributes
- sqs:ReceiveMessage
Resource: "*"
- Statement:
- Effect: Allow
Action:
- sqs:SendMessage
Resource: !GetAtt AcquisitionsDeadLetterQueue.Arn
AcquisitionsDLQProcessorLambda:
Type: AWS::Serverless::Function
Properties:
Handler: soft-opt-in-acquisitions-dlq-processor.handler
Runtime: nodejs20.x
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}-soft-opt-in-acquisitions-dlq-processor/soft-opt-in-acquisitions-dlq-processor.zip
FunctionName: !Sub ${App}-soft-opt-in-acquisitions-dlq-processor-${Stage}
Environment:
Variables:
App: !Sub ${App}
Stack: !Sub ${Stack}
Stage: !Sub ${Stage}
DLQUrl: !Ref AcquisitionsDeadLetterQueue
Description: Process DLQ messages
MemorySize: 512
Timeout: 60
Tags:
Stage: !Ref Stage
Stack: !Ref Stack
App: !Ref App
Events:
ScheduledRun:
Type: Schedule
Properties:
Schedule: !FindInMap [ StageVariables, !Ref Stage, Schedule]
Description: Runs AcquisitionsDLQProcessorLambda
Enabled: True
Policies:
- Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource:
!Sub
- "arn:aws:iam::${MembershipAccountId}:role/membership-${SoftOptInConsentSetterStage}-soft-opt-in-consent-setter-QueueCrossAccountRole"
- SoftOptInConsentSetterStage: !FindInMap [ StageVariables, !Ref Stage, SoftOptInConsentSetterStage ]
- Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Sub "arn:aws:iam::${MembershipAccountId}:role/comms-${Stage}-EmailQueueCrossAccountRole"
- Statement:
- Effect: Allow
Action:
- "dynamodb:Query"
- "dynamodb:GetItem"
Resource:
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscriptions
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${App}-${Stage}-subscriptions/*
- Statement:
- Effect: Allow
Action:
- ssm:GetParametersByPath
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${App}/${Stage}/${Stack}/*
- Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- cloudwatch:putMetricData
Resource: "*"
- Statement:
- Effect: Allow
Action:
- sqs:DeleteMessage
- sqs:GetQueueAttributes
- sqs:ReceiveMessage
Resource: "*"
failedSettingCancellationSoftOptIns:
Condition: IsProd
Type: AWS::CloudWatch::Alarm
DependsOn:
- DeleteUserSubscriptionLambda
Properties:
AlarmActions:
- Ref: AlarmTopic
AlarmName: !Sub ${App}-delete-user-subscription-${Stage} failed to set soft opt-ins for a user
AlarmDescription: >
An error occurred in the DeleteUserSubscriptionLambda and failed to set soft opt-ins for a user
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: Stage
Value: !Sub ${Stage}
EvaluationPeriods: 1
MetricName: failed_to_send_cancellation_message
Namespace: AWS/Lambda
Period: 3600
Statistic: Sum
Threshold: 1
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-delete-user-subscription
acquisitionsLambdaExceptionsAlarm:
Type: AWS::CloudWatch::Alarm
Condition: IsProd
DependsOn:
- SoftOptInAcquisitionsLambda
Properties:
AlarmActions:
- Ref: AlarmTopic
AlarmName: !Sub ${App}-soft-opt-in-acquisitions-${Stage} threw an unhandled exception and failed to set soft opt-ins for a user
AlarmDescription: >
An error occurred in the SoftOptInAcquisitionsLambda and failed to set soft opt-ins for a user
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: FunctionName
Value: !Ref SoftOptInAcquisitionsLambda
EvaluationPeriods: 1
MetricName: Errors
Namespace: AWS/Lambda
Period: 3600
Statistic: Sum
Threshold: 1
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-soft-opt-in-acquisitions
acquisitionsDlqProcessorExceptionsAlarm:
Type: AWS::CloudWatch::Alarm
Condition: IsProd
DependsOn:
- AcquisitionsDLQProcessorLambda
Properties:
AlarmActions:
- Ref: AlarmTopic
AlarmName: !Sub ${App}-soft-opt-ins-acquisitions-dlq-processor-${Stage} threw an unhandled exception
AlarmDescription: >
An error occurred in the AcquisitionsDLQProcessorLambda
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: FunctionName
Value: !Ref AcquisitionsDLQProcessorLambda
EvaluationPeriods: 1
MetricName: Errors
Namespace: AWS/Lambda
Period: 3600
Statistic: Sum
Threshold: 1
TreatMissingData: notBreaching
Tags:
- Key: App
Value: mobile-purchases-soft-opt-in-acquisitions-dlq-processor
MembershipCloudwatchRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${MembershipAccountId}:root
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: cloudwatch-list-tags
PolicyDocument:
Statement:
Effect: Allow
Action:
- cloudwatch:ListTagsForResource
Resource: "*"