serverless: EventBridge event setting arn with ImportValue causes TypeError

Bug Report

Description

  1. What did you do?
    • Trying to import a Custom event bus arn from another stack caused a type error.
  2. What happened?
    • Failed with error
    • Retried using inline!ImportValue syntax also failed
    • Tried with a hardcoded arn and successfully deployed
  3. What should’ve happened?
    • Stack deploy with event from imported bus
  4. What’s the content of your serverless.yml file?
Framework Core: 1.59.1
Plugin: 3.2.5
SDK: 2.2.1
Components Core: 1.1.2
Components CLI: 1.4.0
service: Example

custom:
  env: ${file(.env.yml)} 

provider:
  name: aws
  profile: ${opt:aws-profile, self:custom.env.defaults.profile}
  stage: ${opt:stage, self:custom.env.defaults.stage}
  region: ${opt:region, self:custom.env.defaults.region}
  runtime: nodejs12.x

  functions:
    testFunc:
      handler: src/this/is/a/test.handler
      events:
        - eventBridge:
            eventBus: 
              Fn::ImportValue: OtherStackBusArn
            # also tried - 
            #  eventBus: !ImportValue OtherStackBusArn
            pattern:
              source:
                - event.test
  1. What’s the output you get when you use the SLS_DEBUG=* environment variable (e.g. SLS_DEBUG=* serverless deploy)
TypeError: EventBus.startsWith is not a function
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/plugins/aws/package/compile/events/eventBridge/index.js:121:30
      at Array.forEach (<anonymous>)
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/plugins/aws/package/compile/events/eventBridge/index.js:29:28
      at Array.forEach (<anonymous>)
      at AwsCompileEventBridgeEvents.compileEventBridgeEvents (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/plugins/aws/package/compile/events/eventBridge/index.js:23:31)
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/classes/PluginManager.js:489:55
  From previous event:
      at PluginManager.invoke (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/classes/PluginManager.js:489:22)
      at PluginManager.spawn (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/classes/PluginManager.js:509:17)
      at Deploy.<anonymous> (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/plugins/deploy/deploy.js:115:50)
  From previous event:
      at Object.before:deploy:deploy [as hook] (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/plugins/deploy/deploy.js:100:30)
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/classes/PluginManager.js:489:55
  From previous event:
      at PluginManager.invoke (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/classes/PluginManager.js:489:22)
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/classes/PluginManager.js:524:24
  From previous event:
      at PluginManager.run (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/classes/PluginManager.js:524:8)
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/Serverless.js:115:33
      at processImmediate (internal/timers.js:439:21)
      at process.topLevelDomainCallback (domain.js:130:23)
  From previous event:
      at Serverless.run (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/lib/Serverless.js:102:74)
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/bin/serverless.js:72:30
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/node_modules/graceful-fs/graceful-fs.js:111:16
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/node_modules/graceful-fs/graceful-fs.js:45:10
      at FSReqCallback.oncomplete (fs.js:146:23)
  From previous event:
      at /######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/bin/serverless.js:72:8
      at processImmediate (internal/timers.js:439:21)
      at process.topLevelDomainCallback (domain.js:130:23)
  From previous event:
      at Object.<anonymous> (/######/.nvm/versions/node/v12.13.1/lib/node_modules/serverless/bin/serverless.js:61:4)
      at Module._compile (internal/modules/cjs/loader.js:959:30)
      at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
      at Module.load (internal/modules/cjs/loader.js:815:32)
      at Function.Module._load (internal/modules/cjs/loader.js:727:14)
      at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)
      at internal/main/run_main_module.js:17:11

Similar or dependent issues:

  • This issue from the forum about partner events seems at least tangentially related

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 10
  • Comments: 41 (31 by maintainers)

Most upvoted comments

@medikoo I have completed work on the branch linked to above to deploy EventBridge resources using CloudFormation. I have no doubt there will be changes needed as it is not a trivial change and I’ve had to do a lot learning about how the framework is operation to complete it. Are you happy for me to create a PR off of it and we can go from there?

Glad to help!

Just to recap, the workaround is not to use an imported value but passing the eventbus’ name instead of the ARN.

This virtually removes the ability of using eventbus’ ARN (it will only work when it’s hardcoded). I believe that’s fine as we can still use eventbus’ name. But, yes, it should be clarified in the documentation.

Summing up:

- eventBridge:
    eventBus: arn:aws:events:us-east-1:12345:event-bus/custom-private-events   # this will work! :)


- eventBridge:
    eventBus: custom-private-events   # this will work! :)


- eventBridge:
    eventBus: !Ref EventBus   # this will work! :)


- eventBridge:
    eventBus: !GetAtt EventBus.Name   # this will work! :)


- eventBridge:
    eventBus: !ImportValue EventBusName   # this will work as long as the Output refers to eventbus' name! :|


- eventBridge:
    eventBus: !GetAtt EventBus.Arn   # this WON'T work! :(

@DaveLo I’ve looked into that more deeply, and to me it appears that there’s inconsistency in our config design.

e.g. for S3 buckets or cognito user pools, resources are referenced by name, and via additional existing: true setting we eventually mark that given resource already exists, so there should not be creation attempt.

While in case of event bridge, we resolve the intention from the name format (if it’s an ARN, we imply existing: true, and if not, then we create).

I believe right way to approach it, is to bring same existing: true setting support to event bridge configuration (and ensure that that CF instructions are supported for name), that way it’ll work for you as expected. (Unfortunately to not break things in v1, we still need to support ARN resolution).

@pmuens What do you think about that?

Thank you both @cjuega and @capgadsx - I believe I know what the issue is here. In order to generate proper AWS:SourceArn we need to have the ability to extract name of the event bus. It was possible to do when using explicit arn passed as a string, however, when passing arn with CF intrinsic command, it’s impossible to reason about name, which results in invalid SourceArn being generated. The proper approach in that situation would be to pass imported name as suggested in workaround by @cjuega. I believe we should clarify that in the documentation.

What is the current situation regarding this case? Is a bug fix planned?

Thanks @DaveLo for looking into that.

Firstly, this issue comes from fact, that CloudFormation instructions are not normally supported on serverless configuration level. Technically they can be supported only if given setting value is propagated as is to CloudFormation template property (where given instruction is supported), but it requires further effort to implement such support.

Ideally Framework should fail with a meaningful error on such attempts, but it doesn’t (hence ugly errors as EventBus.startsWith is not a function). We plan to address that at #6562

Now, concerning your proposal (on bringing support for CF instruction format to eventBus property):

but exporting the Ref rather than the full Arn it seems to not realize the eventbus already exists.

Currently the framework assumes event bus exists, only if value passed to eventBus resolves to string that starts with arn. See, this setting is propagated as is to custom resource:

https://github.com/serverless/serverless/blob/2938a95b8711d3bfab1e665318f46f66db91d6a4/lib/plugins/aws/package/compile/events/eventBridge/index.js#L94

and then read here:

https://github.com/serverless/serverless/blob/2938a95b8711d3bfab1e665318f46f66db91d6a4/lib/plugins/aws/customResources/resources/eventBridge/lib/eventBridge.js#L10

Hey, I thought I might take a crack at this. I looked at how it’s handled in SQS events, but I don’t think it tracks exactly.

From looking at the code it looks like we always want the event bus name not the arn so that it can be used in constructing the rule arn. maybe it’s easier to import the name by exporting the external Ref (name for EventBridge)? Then you could look for Fn::ImportValue ((typeof EventBus !== 'string' && 'Fn::ImportValue' in EventBus) from sqs event) and workflow from there?

ruleResource = {
  'Fn::Join': [
    ':',
    [
      'arn:aws:events',
      { Ref: 'AWS::Region' },
      { Ref: 'AWS::AccountId' },
      { 
        'Fn::Join': [
          "/",
          [
            'rule',
            {'Fn::ImportValue':  EventBus['Fn::ImportValue' },
            RuleName
          ]
        ]
      }
    ],
  ],
};

@pgrzesik, do you mean before using the new useCloudFormation: true feature? I used to pass it explicitly in an envvar. Using !ImportValue thrown an EventBus.startsWith is not a function error as described in the bug report.

According to serverless’ doc, this should be supported:

- eventBridge:
    eventBus: arn:aws:events:us-east-1:12345:event-bus/custom-private-events
    pattern:
      source:
        - custom.private
    inputTransformer:
      inputPathsMap:
        eventTime: '$.time'
      inputTemplate: '{"time": <eventTime>, "key1": "value1"}'

After the changes implemented at https://github.com/serverless/serverless/pull/8437, I would expect the following to also be supported:

- eventBridge:
    eventBus: !GetAtt EventBus.Arn
    pattern:
      source:
        - custom.private
    inputTransformer:
      inputPathsMap:
        eventTime: '$.time'
      inputTemplate: '{"time": <eventTime>, "key1": "value1"}'

am I missing something?

I’m experiencing issues with this implementation in serverless@v2.28.0. Here is my setup:

provider:
  ...
  eventBridge:
    useCloudFormation: true

I have the following resource defined:

Resources:
  DomainEventBus:
    Type: AWS::Events::EventBus
    Properties:
      Name: ${env:PROJECT}-${env:STAGE}-events

Outputs:
  DomainEventBusArn:
    Value: !GetAtt DomainEventBus.Arn
    Export:
      Name: ${env:PROJECT}-${self:provider.stage}-DomainEventBusArn

Then, in my lambda definitions, I use !GetAtt DomainEventBus.Arn or !ImportValue DomainEventBusArn to indicate which eventbus triggers a lambda:

  events:
    - eventBridge:
        eventBus: !GetAtt DomainEventBus.Arn

Deployment works fine. It seems everything is correct. However, my lambdas are not triggered when events are sent to the bus. After some troubleshooting I have found lambdas’ Resource-based policy is wrong. In particular, AWS:SourceArn condition has a wrong ARN. It seems eventbus’ name is not extracted from the ARN in order to build rule’s ARN.

The quick workaround is to use !Ref DomainEventBus to use eventbus’ name instead of its ARN.

@stuartforrest-infinity, do you think your change may have broken this? Using ARN used to work when using custom resources to create the event bus.

I think guidance on importing the resources can be provided and potentially a helper script to output the required CloudFormation

@stuartforrest-infinity if it’s the case, then definitely we can include that, and all the instructions can be put into documentation of deprecation (we document each one here: https://www.serverless.com/framework/docs/deprecations/)

not authorized to perform: events:CreateEventBus

If you mean that this crash happens in custom resource lambda (during deployment), then it means that setup of access rules which this lambda needs, needs to be updated here:

https://github.com/serverless/serverless/blob/fbf99fa2abf9ce3bc13fc4a6c8439a650d3eaa4e/lib/plugins/aws/package/compile/events/eventBridge/index.js#L152-L168