EventBridge Rules to Invoke Lambda and StepFunction
AWS EventBridge is an event bus system for loosely coupling different services and resources in a publish/subscribe pattern. A service can publish an event (i.e., a message) and multiple other services can subscribe to that event.
The subscription mechanism is done through individual rules that define the pattern of an event to be catched on the event bus and define multiple targets to be called upon that event, e.g. a Lambda function or a StepFunction state machine.
Motivation
This post is intended to serve as a blueprint for how to invoke these two services with rules and how to deal with typical authorization challenges along the way. The required resources are defined as a CloudFormation template in JSON, but can easily be converted to YAML.
I created a test Lambda function and a test StepFunction state machine to be called by their respective EventBridge rule. I refer to these resources in CloudFormation as TestLambdaFunction
and TestStateMachine
, but these are just their logical IDs and will be replaced with ARNs when deployed.
Invoke StepFunction State Machine
This CloudFormation template defines an event rule with ID InvokeStateMachineEventRule
and name start-state-machine
that listens on the default
event bus for events whose detail-type
is set to start-state-machine
.
Targets are resources that are invoked when a rule is triggered. The intrinsic function Fn::GetAtt
returns the ARN of TestStateMachine
specified as the target resource.
Additionally, each target must define an IAM role as RoleArn
which is used to invoke the resource.
{ "InvokeStateMachineEventRule": { "Type": "AWS::Events::Rule", "Properties": { "EventBusName": "default", "EventPattern": { "detail-type": ["start-state-machine"], }, "Name": "start-state-machine", "State": "ENABLED", "Targets": [ { "Arn": { "Fn: :GetAtt": ["TestStateMachine","Arn"] }, "Id": "TestStateMachine", "RoleArn": { "Fn: :GetAtt": ["InvokeStateMachineIamRole","Arn"] }, }, ], }, }, }
The IAM role is defined as InvokeStateMachineIamRole
and contains an inline policy that allows the states:StartExecution
action for the TestStateMachine
resource. Since the role is used by an EventBridge rule to invoke another service resource (i.e. StepFunction state machine), an additional AssumeRolePolicyDocument
is provided to allow the role to be taken from EventBridge (i. e. principal events.amazonaws.com
).
{ "InvokeStateMachineIamRole": { "Type": "AWS::IAM::Role", "Properties": { "Policies": [ { "PolicyName": "InvokeStateMachineRolePolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["states:StartExecution"], "Resource": [ { "Fn: :GetAtt": ["TestStateMachine", "Arn"] } ], }, ], }, }, ], }, }, "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": ["events.amazonaws.com"], }, "Action": "sts:AssumeRole", }, ], }, }
Invoke Lambda Function
This event rule InvokeLambdaFunctionEventRule
is similar to the previous one, except that it is named start-lambda-function
and listens for events whose detail-type
is set to start-lambda-function
. Also, the target resource is TestLambdaFunction
and RoleArn
was intentionally omitted.
{ "InvokeLambdaFunctionEventRule": { "Type": "AWS::Events::Rule", "Properties": { "EventBusName": "default", "EventPattern": { "detail-type": ["start-lambda-function"], }, "Name": "start-lambda-function", "State": "ENABLED", "Targets": [ { "Arn": { "Fn: :GetAtt": ["TestLambdaFunction","Arn"] }, "Id": "TestLambdaFunction", }, ], }, }, }
If we provide a RoleArn
for the TestLambdaFunction
target, it will throw an error when deploying it:
UPDATE_FAILED: InvokeLambdaFunctionEventRule (AWS::Events::Rule)
RoleArn is not supported for target arn:aws:lambda:eu-central-1:xxxxxxxxxxxx:function:eventbridge-rule-example-dev-test.
Unlike other services, Lambda uses resource-based permissions to allow other AWS services to invoke this function on your behalf. Therefore, we need to add an InvokeLambdaFunctionPermission
definition that allows the Lambda:InvokeFunction
action for the TestLambdaFunction
resource (i.e. function name). Also this permission is used by an EventBridge rule to invoke this Lambda function. Therefore, we need to specify the principal events.amazonaws.com
and the ARN of InvokeLambdaFunctionEventRule
that actually calls the function.
{ "InvokeLambdaFunctionPermission": { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName": { "Fn: :GetAtt": ["TestLambdaFunction", "Arn"], }, "Action": "lambda:InvokeFunction", "Principal": "events.amazonaws.com", "SourceArn": { "Fn: :GetAtt": ["InvokeLambdaFunctionEventRule", "Arn"] }, }, }, }
GitHub Repository
I deployed all resources using the Serverless Framework, but only to quickly create the Lambda function and StepFunction state machine. All the important resources are defined as AWS CloudFormation templates and are valid when deployed in other ways, e.g. AWS SAM, AWS CLI, etc.
Feel free to check out the full template and deploy it on your own AWS account: GitHub respository