serverless-application-model icon indicating copy to clipboard operation
serverless-application-model copied to clipboard

Conditional event attachment

Open glenveegee opened this issue 7 years ago • 27 comments

If conditional resource creation is eventually supported you might also have to support conditional events as in the following example where in some environments a bucket might not already exist and so it can be created and the event attached to the lambda. While in other environments the bucket may already exist and the trigger needs to be manually defined because cloudformation doesn't yet use existing resources.

For example

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Description: Lambda that responds to S3 events

Parameters:
    PreExistingBucket:
        Description: "Does an existing bucket exist (not managed by cloudformation)"
        Type: String
        Default: 'no'
        AllowedValues:
            - 'yes'
            - 'no'
        ConstraintDescription: must specify yes or no.

Conditions:
  NeedsSomeBucket: !Equals [ !Ref PreExistingBucket, 'no' ]

Resources:
    BucketEventConsumer:
        Type: AWS::Serverless::Function
        Properties:
            Handler: BucketEventConsumer.main.lambda_handler
            Runtime: python3.6
            CodeUri: bundle.zip
            Events:
                CreateMetaEvent:
                    Condition: NeedsSomeBucket
                    Type: S3
                    Properties:
                        Bucket: !Ref SomeBucket
                        Events: "s3:ObjectCreated:*"
                        Filter:
                            S3Key:
                                Rules:
                                    -
                                        Name: suffix
                                        Value: meta.json
    SomeBucket:
        Condition: NeedsSomeBucket
        Type: "AWS::S3::Bucket"
        Properties:
            BucketName: 'some-bucket-somewhere'
        DeletionPolicy: Retain

I'd like to draw your attention to the following crucial lines:

                CreateMetaEvent:
                    Condition: NeedsSomeBucket

Can this already be acheived using Fn::If?

glenveegee avatar Nov 27 '17 09:11 glenveegee

Closing in favor of #142

brettstack avatar Jun 27 '18 00:06 brettstack

This one was created as a new Feature-Request based on the discussion in #142 .

@ glenveegee That is an interesting thought and idea! Would you mind create a new issue with this use case? It will help us prioritize this in the future and will ensure it does not get lost or forgotten about in this issue (since they are slightly different).

Closing this in favor of #142 doesn't make sense. Both of these are separate and useful features.

jaypadia-frame avatar Jul 27 '18 14:07 jaypadia-frame

Any update on this? I've run into a similar use case and would like to use this feature as well.

glenak1911 avatar Oct 17 '18 15:10 glenak1911

There are currently higher priority items we are focusing on. I've increased the priority label for visibility during planning and hope to get to this one along with #142 soon

brettstack avatar Oct 22 '18 13:10 brettstack

Thanks for the feedback @brettstack !

glenak1911 avatar Oct 23 '18 18:10 glenak1911

@brettstack Any update on this? I would like to conditionally enable or disable the event rule. I tried also using mapping but it seems it doesn't work either... so I'm wondering which kind of intrinsic functions (see doc) are supported for real inside SAM template. Is there any other way to conditionally enable events? Thank you!

made2591-eb avatar Oct 15 '19 10:10 made2591-eb

@made2591-eb We have passed this along to our product team for possible prioritization. Please +1 on the feature to help with prioritization.

keetonian avatar Oct 15 '19 17:10 keetonian

+1 I appreciate if you prioritise this issue. I have the exactly same issue as #1229 and have to manually disable a CloudWatch event once it is deployed in a staging environment. Thank you!

takeshi4126 avatar Jun 11 '20 02:06 takeshi4126

Ran across this with an AWS::Serverless::Function using EventSource - unable to conditionally apply it. Would be great to see.

ghost avatar Aug 25 '20 15:08 ghost

+1

andrescastillol avatar Sep 08 '20 15:09 andrescastillol

+1 This will help lessen a lot of cost savings and manual maintenance in dev stages.

jiminkk avatar Sep 24 '20 23:09 jiminkk

Issue was opened in 2017 Soon 2021 arrives Can you prioritize a bit more?

Catastropha avatar Oct 08 '20 12:10 Catastropha

+1

RichDavis1 avatar Oct 22 '20 18:10 RichDavis1

+1

jiojanen avatar Feb 16 '21 07:02 jiojanen

+1

sleekmountaincat avatar Feb 18 '21 23:02 sleekmountaincat

+1

tniemann avatar Mar 01 '21 07:03 tniemann

As a workaround, you can use AWS::Events::Rule

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html#aws-resource-events-rule-syntax.yaml

tniemann avatar Mar 01 '21 07:03 tniemann

+1

navigator3 avatar Mar 25 '21 10:03 navigator3

+1

rickard-andersson-wcar avatar May 10 '21 14:05 rickard-andersson-wcar

As a workaround, you can use AWS::Events::Rule

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html#aws-resource-events-rule-syntax.yaml

How do you add an AWS::Events::Rule to an AWS::Serverless::Function?

dave-kennedy avatar Jul 26 '21 20:07 dave-kennedy

@dave-kennedy

It's the other way around. As one of the Targets of the AWS::Events::Rule, specify the Lambda Function.

ExampleRule:
  Type: AWS::Events::Rule
  Properties:
    EventBusName: !Ref ExampleEventBus
    EventPattern: # the pattern to match, or use `ScheduleExpression` instead
    Targets:
    - Arn: !GetAtt ExampleFunction.Arn
      Id: Example # But use a unique value, natch.

And it's there in the resource that one can specify optionality. Either by Condition at the top level or something like !If on the Targets property.

chrisoverzero avatar Jul 28 '21 17:07 chrisoverzero

@chrisoverzero if the event emitter is an SNS topic, is there any difference between creating an AWS::SNS::Subscription between the topic and the function as opposed to using AWS::Events::Rule?

dave-kennedy avatar Oct 01 '21 21:10 dave-kennedy

I'm afraid I must not understand your question correctly. Are you asking whether you can use a rule to subscribe to a topic? SNS doesn't emit messages onto EventBridge buses, EventBridge does. An AWS::Events::Rule won't react to messages in SNS topics.

chrisoverzero avatar Oct 01 '21 21:10 chrisoverzero

The proposed workaround doesn't seem to work, at least not for SNS events. I believe it's the same issue as this one, even though the OP was asking about EventBridge.

Here's the relevant parts of my template:

Parameters:
  TestEventTopicArn:
    Type: String
    Description: The fully qualified ARN for the SNS topic

Conditions:
  SubscribeToTestEventTopic:
    !Not [!Equals [!Ref TestEventTopicArn, ""]]

Resources:
  TestEventHandlerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: test-event-handler-role
      Path: /
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
        - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess

  TestEventHandler:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ../../src/
      FunctionName: test-event-handler
      Handler: test_event_handler.handle_event
      Runtime: python3.8
      Role: !GetAtt TestEventHandlerRole.Arn
      AutoPublishAlias: live
      DeploymentPreference: AllAtOnce
      Timeout: 10
      MemorySize: 128

  TestEventSubscription:
    Type: AWS::SNS::Subscription
    Condition: SubscribeToTestEventTopic
    Properties:
      TopicArn: !Ref TestEventTopicArn
      Endpoint: !GetAtt TestEventHandler.Arn
      Protocol: lambda

With the above resources, the subscription was created and confirmed only when TestEventsTopicArn was set to a non-empty string, as desired, but the function was never invoked. Viewing the function in the Lambda console, it had no associated triggers.

I suspected the issue might have been the lack of a ":live" alias at the end of the endpoint ARN and updated the subscription like so:

  TestEventSubscription:
    Type: AWS::SNS::Subscription
    Condition: SubscribeToTestEventTopic
    Properties:
      TopicArn: !Ref TestEventTopicArn
      Endpoint:
        !Sub
          - "${test_event_handler_arn}:live"
          - test_event_handler_arn: !GetAtt TestEventHandler.Arn
      Protocol: lambda

This correctly added the ":live" alias to the subscription endpoint but the function but had the same issue, i.e. no events or triggers.

In the end I had to revert to defining the event source on the function itself:

  TestEventHandler:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ../../src/
      FunctionName: test-event-handler
      Handler: test_event_handler.handle_event
      Runtime: python3.8
      Role: !GetAtt TestEventHandlerRole.Arn
      AutoPublishAlias: live
      DeploymentPreference: AllAtOnce
      Timeout: 10
      MemorySize: 128
      Events:
        TestEventTopic:
          Type: SNS
          Properties:
            Topic: !Ref TestEventTopicArn

Of course, the problem now is I can't conditionally turn the subscription off, same as the OP.

dave-kennedy avatar Oct 05 '21 22:10 dave-kennedy

Not entirely sure but can this request not already be solved:

Resources:
    BucketEventConsumer:
        Type: AWS::Serverless::Function
        Properties:
            Handler: BucketEventConsumer.main.lambda_handler
            Runtime: python3.6
            CodeUri: bundle.zip
            Events:
                !If
                    - NeedsSomeBucket
                    - 
                        CreateMetaEvent:
                            Condition: NeedsSomeBucket
                            Type: S3
                            Properties:
                                Bucket: !Ref SomeBucket
                                Events: "s3:ObjectCreated:*"
                                Filter:
                                    S3Key:
                                        Rules:
                                            -
                                                Name: suffix
                                                Value: meta.json
                    - !Ref "AWS::NoValue"

The yaml is a little uggly. But is is similar to example 4 here https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html

Haven't testes this yet. The way described above could be syntactic sugar then.

Jacco avatar May 10 '22 17:05 Jacco

Not entirely sure but can this request not already be solved:

Resources:
    BucketEventConsumer:
        Type: AWS::Serverless::Function
        Properties:
            Handler: BucketEventConsumer.main.lambda_handler
            Runtime: python3.6
            CodeUri: bundle.zip
            Events:
                !If
                    - NeedsSomeBucket
                    - 
                        CreateMetaEvent:
                            Condition: NeedsSomeBucket
                            Type: S3
                            Properties:
                                Bucket: !Ref SomeBucket
                                Events: "s3:ObjectCreated:*"
                                Filter:
                                    S3Key:
                                        Rules:
                                            -
                                                Name: suffix
                                                Value: meta.json
                    - !Ref "AWS::NoValue"

The yaml is a little uggly. But is is similar to example 4 here https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html

Haven't testes this yet. The way described above could be syntactic sugar then.

Using the !If statement within the Events block does not work, as doing so throws the validation off. Having such a conditional in your template results in sam validate returning you an error like Event with id [Fn::If:] is invalid. 'list' object has no attribute 'get'.

@aws: It's been more than 4.5 years since this feature was requested. Please implement a solution for this!

mluypaert avatar Jun 16 '22 15:06 mluypaert

I had a similar issue trying to conditionally add a Schedule event to a SAM function. I post here all the relevant parts for a workaround (including the necessary permission to invoke the function) in case anyone needs it.

Parameters:
  TargetFunctionSchedule:
    Type: String
    Default: ""

Conditions:
  EnableSchedule: !Not [ !Equals [ !Ref TargetFunctionSchedule, "" ] ]

Resources:

  TargetFunction:  # function whose execution may be scheduled or not
    Type: AWS::Serverless::Function
    Properties: ...

  FunctionScheduleRule:
    Condition: EnableSchedule
    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: !Ref TargetFunctionSchedule
      Targets:
        Id: InvokeFunction  # an arbitrary name
        Arn: !GetAtt TargetFunction.Arn

  FunctionSchedulePermission:
    Condition: EnableSchedule
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref TargetFunction
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt FunctionScheduleRule.Arn

rrobby86 avatar Aug 26 '22 13:08 rrobby86

This is now doable.

Save the following as template.yaml:

Transform:
  - AWS::LanguageExtensions
  - AWS::Serverless-2016-10-31

Parameters:
  NeedBucket:
    Type: String

Conditions:
  NeedBucketCondition: !Equals [!Ref NeedBucket, "yes"]

Resources:
  MyBucket:
    Condition: NeedBucketCondition
    Type: AWS::S3::Bucket

  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.8
      Handler: foo
      InlineCode: bar
      Events:
        S3Event:
          Fn::If:
            - NeedBucketCondition
            - Type: S3
              Properties:
                Bucket: !Ref MyBucket
                Events: s3:ObjectCreated:*
            - !Ref AWS::NoValue

Then deploy with the S3 event:

sam deploy --region us-west-2 --resolve-s3 --capabilities CAPABILITY_IAM --stack-name test-214 --template template.yaml --parameter-overrides NeedBucket=yes

Or without the S3 event:

sam deploy --region us-west-2 --resolve-s3 --capabilities CAPABILITY_IAM --stack-name test-214 --template template.yaml --parameter-overrides NeedBucket=no

Adding AWS::LanguageExtensions ensures intrinsic functions that are known before deployment (such as the above) are resolved; see #2533.

hoffa avatar Dec 07 '22 21:12 hoffa