serverless-application-model
serverless-application-model copied to clipboard
Conditional event attachment
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?
Closing in favor of #142
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.
Any update on this? I've run into a similar use case and would like to use this feature as well.
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
Thanks for the feedback @brettstack !
@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 We have passed this along to our product team for possible prioritization. Please +1 on the feature to help with prioritization.
+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!
Ran across this with an AWS::Serverless::Function using EventSource - unable to conditionally apply it. Would be great to see.
+1
+1 This will help lessen a lot of cost savings and manual maintenance in dev stages.
Issue was opened in 2017 Soon 2021 arrives Can you prioritize a bit more?
+1
+1
+1
+1
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
+1
+1
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
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 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?
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.
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.
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.
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.htmlHaven'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!
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
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.