I have one service in a multi-service monorepo, that consistently fails, even when I’ve deep cleaned out all the resources and fully deleted everything and started from fresh. I think it’s very similar to this error: Keep getting error - <resource> already exist but I don’t know enough about serverless file syntax to know what I’m accidentally double declaring.
Additional context is that this appears to work on my colleague’s computer, but not mine. I am running the latest version of framework core (2.2.0) and I am not sure what he is running, so this is potentially something where the latest version infers a resource creation which would not have been inferred in a previous one. The resource specifically that is failing is “MailsBucket”.
This is the content of this serverless file, with the service name redacted:
service:
name: [xxxxxx]
package:
exclude:
- '**/node_modules/aws-sdk/**'
- '**/node_modules/@types/**'
custom:
build: npm run ${env:SLS_BUILD, 'build'}
scriptHooks:
'deploy:function:initialize': ${self:custom.build}
'package:initialize': ${self:custom.build}
'invoke:local:loadEnvVars': ${self:custom.build}
'after:deploy:finalize': build/ses-dest-add.js
stage: ${opt:stage, env:STAGE, self:provider.stage}
region: ${opt:region, env:AWS_REGION, self:provider.region}
sendingEnabled:
prod: true
dev: false
sesRulesEnabled:
prod: true
dev: true
# dotenv:
# logging: false
queue:
name: ${self:service}-emails-send-${self:custom.stage}
arn: { Ref: MailsQueue }
bucket:
name: ${self:service}-${self:custom.stage}
prefix: mailtumble-incoming-emails
configSet: ${self:service}-email-info-${self:custom.stage}
statTopic:
name: ${self:service}-stat-${self:custom.stage}
arn: { Ref: StatTopic }
table:
name:
Fn::ImportValue: { Fn::Join : [ '-', [ '[xxxxxx]-next-core', '${self:custom.stage}', 'table' ] ] }
arn:
Fn::ImportValue: { Fn::Join : [ '-', [ '[xxxxxx]-next-core', '${self:custom.stage}', 'table-arn' ] ] }
remover:
buckets:
- ${self:custom.bucket.name}
plugins:
- serverless-scriptable-plugin
- serverless-dotenv-plugin
- serverless-export-env
- serverless-s3-remover
provider:
name: aws
runtime: nodejs12.x
memorySize: 128
environment:
DEFAULT_DOMAIN: ${env:DEFAULT_DOMAIN}
FROM_EMAIL: ${env:FROM_EMAIL}
TABLE: ${self:custom.table.name}
TABLE_ARN: ${self:custom.table.arn}
MAILS_QUEUE: ${self:custom.queue.name}
MAILS_QUEUE_URL: ${self:custom.queue.arn}
MAILS_BUCKET: ${self:custom.bucket.name}
MAILS_BUCKET_PREFIX: ${self:custom.bucket.prefix}
SEND_EMAIL_CONFIG_SET: ${self:custom.configSet}
STAT_TOPIC: ${self:custom.statTopic.name}
STAT_TOPIC_ARN: ${self:custom.statTopic.arn}
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- ${self:custom.table.arn}
- { Fn::Join : [ '', [ '${self:custom.table.arn}', '/index/*' ] ] }
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectAcl
Resource: { Fn::Join : [ '', [ { Fn::GetAtt: ['MailsBucket', 'Arn'] }, '/*' ] ] }
- Effect: Allow
Action:
- sqs:GetQueueAttributes
- sqs:SendMessage
- sqs:DeleteMessage
- sqs:ReceiveMessage
Resource: { Fn::GetAtt: ['MailsQueue', 'Arn'] }
- Effect: Allow
Action:
- ses:SendEmail
- ses:SendRawEmail
Resource: '*'
functions:
ProcessIncoming:
handler: dist/handler/incoming/incoming.handle
SendEmails:
handler: dist/handler/send/send.handle
timeout: 120
memorySize: 256
events:
- schedule:
rate: rate(1 minute)
enabled: ${self:custom.sendingEnabled.${self:custom.stage}, self:custom.sendingEnabled.dev}
UpdateSendingStat:
handler: dist/handler/stat/stat.handle
memorySize: 256
events:
- sns:
arn: ${self:custom.statTopic.arn}
topicName: ${self:custom.statTopic.name}
resources:
Resources:
# --------------------------------------------------------------------
# Resources
# --------------------------------------------------------------------
# S3 Bucket to save emails
MailsBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
BucketName: ${self:custom.bucket.name}
LifecycleConfiguration:
Rules:
- Id: DeleteOldEmails
Status: Enabled
ExpirationInDays: 365
Prefix: ${self:custom.bucket.prefix}
# Queue for messages to send
MailsQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: ${self:provider.environment.MAILS_QUEUE}
MessageRetentionPeriod: 1209600
VisibilityTimeout: 60
# SNS topic for SES to send email sending stat (bounces, complains and etc)
StatTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: ${self:custom.statTopic.name}
# --------------------------------------------------------------------
# Permissions
# --------------------------------------------------------------------
# Allows SES to call process incoming lambda
MailtumbleSESProcessIncomingLambdaFunctionPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: { 'Fn::GetAtt': ['ProcessIncomingLambdaFunction', 'Arn'] }
Principal: ses.amazonaws.com
Action: 'lambda:InvokeFunction'
SourceAccount: { Ref: AWS::AccountId }
# Allows SES to save emails in S3 bucket
[xxxxxx]SESMailsBucketPermissions:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: MailsBucket
PolicyDocument:
Statement:
- Principal:
Service: ses.amazonaws.com
Action:
- s3:PutObject
Effect: Allow
Sid: AllowSESPuts
Resource:
Fn::Join:
['', ['arn:aws:s3:::', Ref: 'MailsBucket', '/*']]
Condition:
StringEquals:
'aws:Referer': { Ref: AWS::AccountId }
# --------------------------------------------------------------------
# SES Rules
# --------------------------------------------------------------------
# Incoming email triggers (works when email sent to DEFAULT_DOMAIN)
ProcessIncomingRule:
Type: AWS::SES::ReceiptRule
DependsOn:
- MailtumbleSESMailsBucketPermissions
- MailtumbleSESProcessIncomingLambdaFunctionPermission
Properties:
RuleSetName: default-rule-set
Rule:
Name: ${self:service}-process-incoming-${self:custom.stage}
Enabled: ${self:custom.sesRulesEnabled.${self:custom.stage}, self:custom.sesRulesEnabled.dev}
ScanEnabled: true
Recipients:
- ${self:provider.environment.DEFAULT_DOMAIN}
- ${self:provider.environment.FROM_EMAIL}
Actions:
- S3Action:
BucketName: ${self:custom.bucket.name}
ObjectKeyPrefix: ${self:custom.bucket.prefix}
- LambdaAction:
FunctionArn:
{ 'Fn::GetAtt': ['ProcessIncomingLambdaFunction', 'Arn'] }
InvocationType: Event
# Outgoing rules (working when sending email using SES)
# Cloudformation doesn't allow to use SNS as EventDestination check
# build/ses-dest-add.js for manually adding it via API
SendToSNSConfigSet:
Type: AWS::SES::ConfigurationSet
Properties:
Name: ${self:custom.configSet}