EventBridge Rules to Invoke Lambda and StepFunction

// #aws#cloud#serverless#tutorial // Comment on DEV

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