2
votes

I have a serverless resource for SNS topic in the resources section in the serverless.yml something like this,

resources:
  Resources:
    SNSTopic:
      Type: AWS::SNS::Topic
      Properties:
        DisplayName: SNS Topic
        TopicName: ${self:service}-${self:provider.stage}-Topic 

When I am trying to bind this SNS topic to my lambda event as given below, lambda is not triggering by the SNS event. When I check AWS console for that lambda function the SNS event bound with wrong ARN value.

Function:
    handler: src/sample/file.lambdaHandler
    role: s3FullAccessRole
    events: SNSTopic
    Properties:
      Policies:
        - AWSLambdaExecute
        - Statement:
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'

I have tried with changing event with all the different ways mentioned in here, https://serverless.com/framework/docs/providers/aws/events/sns/. The only way I found is to hard code the SNS Topic ARN value in the lambda event, which is not ideal for my situation. Any help is really appreciated.

2

2 Answers

1
votes

You could actually create a variable in custom with the arn of the sns topic

custom:
  region: ${opt:region, self:provider.region}
  snsTopic: ${self:service}-${self:provider.stage}-Topic 
  snsTopicArn: { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.snsTopic}" ] ]  }

then just use the arn on the places you need.

Or you can use the plugin https://github.com/silvermine/serverless-plugin-external-sns-events to basic reference the topic name.

If you have only 1 serverless.yml and don't want to have a separate cloudformation file I would use the first option

EDIT:

To use the arn follow the instructions on serverless https://serverless.com/framework/docs/providers/aws/events/sns#using-a-pre-existing-topic

functions:
  dispatcher:
    handler: <handler>
    events:
      - sns:
          arn: ${self:custom.snsTopicArn}

since you have the sns topic on the same serverless.yml, you can even ignore the snsTopicArn variable and build it like one of the suggestions using !Ref which should be a better option for you:

functions:
  dispatcher:
    handler: <handler>
    events:
      - sns:
          arn: !Ref SNSTopic
          topicName: ${self:custom.snsTopic}

full example:

service: testsns
provider:
  name: aws
  runtime: nodejs12.x
  region: eu-west-1

functions:
  hello:
    handler: handler.hello
    events:
      - sns:
          arn: !Ref SuperTopic
          topicName: MyCustomTopic
    Properties:
      Policies:
        - AWSLambdaExecute
        - Statement:
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
resources:
  Resources:
    SuperTopic:
      Type: AWS::SNS::Topic
      Properties:
        TopicName: MyCustomTopic
1
votes

Finally got it!

I end up removing my SNS TOPIC declaration from resource section of serverless.yml added under iamRoleStatements, something like this,

 iamRoleStatements:
    - Effect: Allow
      Action:
        - SNS:Publish
      Resource: { "Fn::Join" : ["", ["arn:aws:sns:${self:provider.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.mySnsTopic}" ] ]  }

And added variables in the custom section

custom:  
  mySnsTopic: "${self:service}-${self:provider.stage}-sns-consume"
  mySnsTopicArn: { "Fn::Join" : ["", ["arn:aws:sns:${self:provider.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.mySnsTopic}" ] ]  }

then mapped this to the lambda function events

Function:
    handler: src/sample/file.lambdaHandler
    role: s3FullAccessRole
    events: ${self:custom.mySnsTopicArn}
    Properties:
      Policies:
        - AWSLambdaExecute

For reference link