我们有一个由旧团队使用Serverless创建的微服务集。我现在正在尝试更新服务的节点版本(无其他更改),并且出现错误TypeError: Cannot read property 'Fn::GetAtt' of undefined
。
我看不出任何明显的原因。
附带说明,该任务实际上是由Lerna运行的,但是无论是通过Lerna运行还是直接与sls
这是我的略作编辑的serverless.yml文件:
service: email-service
plugins:
- serverless-step-functions
- serverless-pseudo-parameters
- serverless-domain-manager
- serverless-plugin-browserifier
package:
exclude: ${file(../../config/package-exclude.yml):exclude}
individually: true
custom:
stage: ${opt:stage, 'dev'}
authorizerServiceStackName: ${file(../zzzz--authorizer/serverless.yml):service}-${self:custom.stage}
authorizerArn: ${cf:${self:custom.authorizerServiceStackName}.AuthorizerArn}
pdfServiceStackName: ${file(../zzzz--pdf/serverless.yml):service}-${self:custom.stage}
tabularPDFServiceStackName: ${file(../zzzz--tabular-pdf/serverless.yml):service}-${self:custom.stage}
generatePDFLambdaArn: ${cf:${self:custom.pdfServiceStackName}.GeneratePDFLambdaArn}
generateTabularPDFLambdaArn: ${cf:${self:custom.tabularPDFServiceStackName}.GenerateHTMLLambdaFunctionArn}
savePDFToBucketLambdaArn: ${cf:${self:custom.pdfServiceStackName}.SavePDFToBucketLambdaArn}
saveAttachmentHtmlToTmpBucketLambdaArn: ${cf:${self:custom.pdfServiceStackName}.SaveAttachmentHtmlToTmpBucketLambdaArn}
pdfTmpBucketArn: ${cf:${self:custom.pdfServiceStackName}.PdfTmpBucketArn}
customDomain:
domainName: ${file(../../config/domains.yml):domains.${self:custom.stage}} # Change this to your domain.
basePath: 'email' # This will be prefixed to all routes
stage: ${self:custom.stage}
createRoute53Record: true
ssm:
kmsKey: alias/aws/ssm
postmarkKey: /api-keys/postmark
provider:
name: aws
runtime: nodejs10.x
stage: ${self:custom.stage}
region: us-west-2
stackTags:
Owner: zzzz@zzzz.com
CostCenter: zzzzzz
DeploymentDate: ${file(../../config/now.js)}
ServiceName: ${self:service}
Expiry: never
environment:
STAGE: ${self:custom.stage}
functions:
attachmentIterator:
handler: handlers/attachment-iterator.handler
memorySize: 128
isolateNextAttachment:
handler: handlers/isolate-next-attachment.handler
memorySize: 128
isolateNextTable:
handler: handlers/isolate-next-table.handler
memorySize: 128
pushGeneratedPdfToArray:
handler: handlers/push-generated-pdf-to-array.handler
memorySize: 128
attachmentCount:
handler: handlers/attachment-count.handler
memorySize: 128
tableCount:
handler: handlers/table-count.handler
memorySize: 128
sendEmail:
role: ${self:resources.Outputs.ExecuteSaveAttachmentLambdaExecuteRoleArn.Value}
memorySize: 128
timeout: 30 # limited by api gateway
handler: handlers/send-email.handler
environment:
SendEmailStateMachineNameARN: ${self:resources.Outputs.SendEmailStateMachineNameARN.Value}
SaveAttachmentHtmlToTmpBucketLambdaArn: ${self:custom.saveAttachmentHtmlToTmpBucketLambdaArn}
events:
- http:
path: /send
method: POST
private: true
authorizer:
arn: ${self:custom.authorizerArn}
resultTtlInSeconds: 0
identitySource: method.request.header.x-zzzz-api-key
identityValidationExpression: .*
cors: ${file(../../config/cors.yml)}
sendToPostmark:
role: ${self:resources.Outputs.SendEmailToProviderExecutionRoleArn.Value}
handler: handlers/postmark/send-template.handler
memorySize: 128
timeout: 60
environment:
SSM_POSTMARKKEY: ${self:custom.ssm.postmarkKey}
EMAIL_TO: mailtrap-${self:custom.stage}@email.zzzz.io
EMAIL_ARCHIVE_ADDRESS: mail-archive-${self:custom.stage}@email.zzzz.io
emailDeliveryStatus:
role: ${self:resources.Outputs.GetEmailStatusFromProviderExecutionRoleArn.Value}
handler: handlers/postmark/delivery-status.handler
memorySize: 128
environment:
SSM_POSTMARKKEY: ${self:custom.ssm.postmarkKey}
attachmentUploadStatus:
role: ${self:resources.Outputs.GetAttachmentStatusFromProviderExecutionRoleArn.Value}
handler: handlers/attachment-upload-status.handler
memorySize: 128
stepFunctions:
stateMachines:
sendEmailWithTabularPDF:
name: sendEmailWithTabularPDF-${self:service}-${self:custom.stage}
definition:
Comment: "Sends an email, with a tabular pdf by passing details to a transactional email API"
StartAt: CountTables
States:
CountTables:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-tableCount
ResultPath: $.count
InputPath: $
Next: ChoiceHasTable
ChoiceHasTable:
Type: Choice
Choices:
- Variable: $.count
NumericGreaterThanEquals: 1
Next: ConfigureIterator
- Variable: $.count
NumericEquals: 0
Next: SendEmail
Default: FailUnableCountTables
ConfigureIterator:
Type: Pass
Result:
index: "-1"
step: 1
ResultPath: $.iterator
Next: Iterator
Iterator:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-attachmentIterator
ResultPath: $.iterator
Next: IterateRecords
IterateRecords:
Type: Choice
Choices:
- Variable: $.iterator.continue
BooleanEquals: true
Next: IsolateNextAttachment
Default: SendEmail
IsolateNextAttachment:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-isolateNextAttachment
ResultPath: $.nextAttachment
OutputPath: $
Next: GenerateTabularPDF
GenerateTabularPDF:
Type: Task
Resource: ${self:custom.generateTabularPDFLambdaArn}
InputPath: $.nextAttachment
ResultPath: $.execArnInformation
OutputPath: $
Next: AttachmentStatus
Catch:
- ErrorEquals: ['WkhtmltopdfError']
Next: FailCouldNotGeneratePDF
- ErrorEquals: ['States.Timeout']
Next: FailPDFGenerateTimeOut
AttachmentStatus:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-attachmentUploadStatus
InputPath: $.execArnInformation
ResultPath: $.generatedPdf
Retry:
- ErrorEquals: ["AttachmentWaiting"]
IntervalSeconds: 2
MaxAttempts: 5
BackoffRate: 2.0
- ErrorEquals: ["AttachmentError"]
IntervalSeconds: 2
MaxAttempts: 5
BackoffRate: 2.0
Next: PushGeneratedPdfToArray
PushGeneratedPdfToArray:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-pushGeneratedPdfToArray
Next: Iterator
SendEmail:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-sendToPostmark
ResultPath: $.emailDetails
Next: DelayDeliveryStatus
Catch:
- ErrorEquals: ['PostmarkError']
Next: FailCouldNotSendEmail
DelayDeliveryStatus:
Type: Wait
Seconds: 2
Next: DeliveryStatus
DeliveryStatus:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-emailDeliveryStatus
InputPath: $.emailDetails.MessageID
ResultPath: $.emailDetails.DeliveryStatus
Retry:
- ErrorEquals: ["EmailQueued"]
IntervalSeconds: 2
MaxAttempts: 5
BackoffRate: 2.0
- ErrorEquals: ["PostmarkError"]
IntervalSeconds: 2
MaxAttempts: 5
BackoffRate: 2.0
End: true
# FailSendingEmail:
# Type: Fail
# Cause: Unable to send email via service.
FailUnableCountTables:
Type: Pass
End: true
FailPDFGenerateTimeOut:
Type: Pass
End: true
FailCouldNotGeneratePDF:
Type: Pass
End: true
FailCouldNotSendEmail:
Type: Pass
End: true
events:
- http:
path: send/template
method: POST
authorizer:
type: CUSTOM
authorizerId:
Ref: AuthorizerApiGatewayAuthorizer
sendEmail:
name: sendEmail-${self:service}-${self:custom.stage}
definition:
Comment: "Sends an email, by passing details to a transactional email API"
StartAt: CountAttachments
States:
CountAttachments:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-attachmentCount
ResultPath: $.count
InputPath: $
Next: ChoiceHasPDF
ChoiceHasPDF:
Type: Choice
Choices:
- Variable: $.count
NumericGreaterThanEquals: 1
Next: ConfigureIterator
- Variable: $.count
NumericEquals: 0
Next: SendEmail
Default: FailUnableCountAttachments
ConfigureIterator:
Type: Pass
Result:
index: "-1"
step: 1
ResultPath: $.iterator
Next: Iterator
Iterator:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-attachmentIterator
ResultPath: $.iterator
Next: IterateRecords
IterateRecords:
Type: Choice
Choices:
- Variable: $.iterator.continue
BooleanEquals: true
Next: IsolateNextAttachment
Default: SendEmail
IsolateNextAttachment:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-isolateNextAttachment
ResultPath: $.nextAttachment
OutputPath: $
Next: GeneratePDF
GeneratePDF:
Type: Task
Resource: ${self:custom.generatePDFLambdaArn}
InputPath: $.nextAttachment
ResultPath: $.generatedPdf
OutputPath: $
Next: PushGeneratedPdfToArray
Catch:
- ErrorEquals: ['WkhtmltopdfError']
Next: FailCouldNotGeneratePDF
- ErrorEquals: ['States.Timeout']
Next: FailPDFGenerateTimeOut
PushGeneratedPdfToArray:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-pushGeneratedPdfToArray
Next: Iterator
SendEmail:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-sendToPostmark
ResultPath: $.emailDetails
Next: DelayDeliveryStatus
Catch:
- ErrorEquals: ['PostmarkError']
Next: FailCouldNotSendEmail
DelayDeliveryStatus:
Type: Wait
Seconds: 2
Next: DeliveryStatus
DeliveryStatus:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:custom.stage}-emailDeliveryStatus
InputPath: $.emailDetails.MessageID
ResultPath: $.emailDetails.DeliveryStatus
Retry:
- ErrorEquals: ["EmailQueued"]
IntervalSeconds: 2
MaxAttempts: 5
BackoffRate: 2.0
- ErrorEquals: ["PostmarkError"]
IntervalSeconds: 2
MaxAttempts: 5
BackoffRate: 2.0
End: true
# FailSendingEmail:
# Type: Fail
# Cause: Unable to send email via service.
FailUnableCountAttachments:
Type: Pass
End: true
FailPDFGenerateTimeOut:
Type: Pass
End: true
FailCouldNotGeneratePDF:
Type: Pass
End: true
FailCouldNotSendEmail:
Type: Pass
End: true
resources:
Resources:
ApiGatewayRestApi:
Properties:
ApiKeySourceType: AUTHORIZER
WriteLambdaLogsPolicy:
Type: AWS::IAM::Policy
Properties:
Roles:
- Ref: ExecuteSaveAttachmentLambdaExecuteRole
- Ref: SendEmailToProviderExecutionRole
- Ref: GetEmailStatusFromProviderExecutionRole
- Ref: GetAttachmentStatusFromProviderExecutionRole
PolicyName: canWriteToCloudwatchLogs
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- 'Fn::Join':
- ':'
-
- 'arn'
- Ref: 'AWS::Partition'
- 'logs'
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'log-group:/aws/lambda/*:*:*'
GetValuesfromSSMStoreAndDecryptPolicy:
Type: AWS::IAM::Policy
Properties:
Roles:
- Ref: SendEmailToProviderExecutionRole
- Ref: GetEmailStatusFromProviderExecutionRole
PolicyName: getAndDecodePostmarkSSMKeys
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
# Note: No '/' between parameter and key name, as the keyname starts with a '/'
- "arn:aws:ssm:#{AWS::Region}:#{AWS::AccountId}:parameter${self:custom.ssm.postmarkKey}"
- Effect: "Allow"
Action:
- kms:Decrypt
Resource: "arn:aws:kms:#{AWS::Region}:#{AWS::AccountId}:key/${self:custom.ssm.kmsKey}"
ExecuteSaveAttachmentLambdaExecuteRole:
Type: AWS::IAM::Role
Properties:
Path: /zzzz/service/email/
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: executeSaveAttachmentLambdaExecuteRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: ${self:custom.saveAttachmentHtmlToTmpBucketLambdaArn}
- Effect: Allow
Action:
- "states:StartExecution"
Resource: ${self:resources.Outputs.SendEmailStateMachineNameARN.Value}
SendEmailToProviderExecutionRole:
Type: AWS::IAM::Role
Properties:
Path: /zzzz/service/email/
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: retrievePDFFromBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: "Allow"
Action:
- s3:GetObject # getObject is Resource Level - ensure your resource ends in '/*'
# - s3:GetObjectAcl
# - s3:GetObjectVersion # this should nto be needed...
# - s3:*
Resource:
- 'Fn::Join':
- '/'
-
- ${self:custom.pdfTmpBucketArn}
- '*'
GetEmailStatusFromProviderExecutionRole:
Type: AWS::IAM::Role
Properties:
Path: /zzzz/service/email/
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
GetAttachmentStatusFromProviderExecutionRole:
Type: AWS::IAM::Role
Properties:
Path: /zzzz/service/email/
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: readExecutionArnStatus
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: "Allow"
Action:
- states:DescribeExecution
Resource:
- 'Fn::Join':
- ':'
-
- 'arn:aws:states'
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'execution'
- 'Fn::ImportValue': ${self:custom.pdfServiceStackName}:SavePDFStateMachineName
- '*'
Outputs:
SendEmailStateMachineNameARN:
Value:
# SendEmailDashemailDashserviceDash${self:custom.stage} should be
# SendEmailDash${self:service}Dash${self:custom.stage} however servicename has a dash in it.
Ref: SendEmailDashemailDashserviceDash${self:custom.stage}
SendEmailStateMachineName:
Value:
'Fn::GetAtt':
# SendEmailDashemailDashserviceDash${self:custom.stage} should be
# SendEmailDash${self:service}Dash${self:custom.stage} however servicename has a dash in it.
- SendEmailDashemailDashserviceDash${self:custom.stage}
- Name
Export:
Name: ${self:service}-${self:custom.stage}:SendEmailStateMachineName
SendEmailWithTabularPDFStateMachineName:
Value:
'Fn::GetAtt':
- SendEmailWithTabularPDFDashemailDashserviceDash${self:custom.stage}
- Name
Export:
Name: ${self:service}-${self:custom.stage}:SendEmailWithTabularPDFStateMachineName
ExecuteSaveAttachmentLambdaExecuteRoleArn:
Value:
'Fn::GetAtt': [ExecuteSaveAttachmentLambdaExecuteRole, Arn]
SendEmailToProviderExecutionRoleArn:
Value:
'Fn::GetAtt': [SendEmailToProviderExecutionRole, Arn]
GetEmailStatusFromProviderExecutionRoleArn:
Value:
'Fn::GetAtt': [GetEmailStatusFromProviderExecutionRole, Arn]
GetAttachmentStatusFromProviderExecutionRoleArn:
Value:
'Fn::GetAtt': [GetAttachmentStatusFromProviderExecutionRole, Arn]