AWSEventBridgeLambda | 6 Min Read

Scheduling Events in AWS with the EventBridge Scheduler and AWS CDK

Learn how to create recurring and one-off schedules using the AWS EventBridge Scheduler using the AWS CDK and TypeScript.

AWS introduced the EventBridge Scheduler feature to EventBridge late last year (November 2022) and since then it’s allowed us to easily create both one-time and recurring schedules to invoke other AWS services.

EventBridge Scheduler replaces previous ways of achieving recurring schedules such as cron job event rules in EventBridge (which I cover in this post) as well as things like DynamoDB TTL. In summary, the new EventBridge Scheduler is a much nicer and cleaner way of handling recurring and one-off schedules inside AWS.

So, in this post, we’re going to cover how we can create both one-off and recurring schedules using the EventBridge Scheduler via the AWS CDK and TypeScript.

EventBridge Scheduler CDK Construct

This will almost certainly change in the future but at the time of writing (April 2023), there is no custom L2 construct for EventBridge Scheduler in the AWS CDK. There is currently an open GitHub issue but for right now, we’ll need to rely on an L1 construct (`CfnResource`) to deploy EventBridge Scheduler through the CDK.

You may not have seen the `CfnResource` construct before but essentially, it’s a CDK construct that we can use to basically write CloudFormation code in the CDK to deploy resources to AWS. So, while it’s a lot nicer and a much better option to use L2 constructs, not having an L2 construct is not a reason not to use the CDK to deploy your infrastructure.

So, with all that said and covered, let’s jump into the actual tutorial portion of this post!

Lambda functions

To get started, use either an existing CDK project or create a new one. Then ensure you have a lambda function or two that you’d like to trigger on a one-off and recurring basis. For this example, I’m going to create two new functions inside a `./resources` folder with the below code.

./resources/one-off.ts
1// ./resources/one-off.ts
2
3export const handler = () => console.log('one off');
ts
./resources/recurring.ts
1// ./resources/recurring.ts
2
3export const handler = () => console.log('recurring');
ts

We’re then going to define the Lambda functions in our CDK stack file inside our `lib` directory with the below code.

./lib/*-stack.ts
1export class EventbridgeSchedulerStack extends Stack {
2 constructor(scope: Construct, id: string, props?: StackProps) {
3 super(scope, id, props);
4
5 // 1. Defining our Lambda Functions
6 const recurringLambda = new NodejsFunction(this, 'RecurringLambda', {
7 entry: './resources/recurring.ts',
8 handler: 'handler',
9 runtime: Runtime.NODEJS_16_X,
10 timeout: Duration.seconds(10),
11 });
12
13 const oneOffLambda = new NodejsFunction(this, 'OneOffLambda', {
14 entry: './resources/one-off.ts',
15 handler: 'handler',
16 runtime: Runtime.NODEJS_16_X,
17 timeout: Duration.seconds(10),
18 });
19 }
20}
ts

So, now we have two simple Lambda functions that console log a message to show when they’re running. Let’s now move on to the interesting part of this article and get started with setting up our scheduler. 🤩

IAM Role and Policy

Before we can actually create our scheduler, we need to define a new role for the scheduler to use to invoke our Lambda functions. To get started with this, define a new role that assumes the `scheduler.amazonaws.com` service principal using the code below.

./lib/*-stack.ts
1// ...lambda definitions
2
3// 2. Define the IAM role for the scheduler to invoke our Lambda functions with
4const schedulerRole = new Role(this, 'schedulerRole', {
5 assumedBy: new ServicePrincipal('scheduler.amazonaws.com'),
6});
ts

Then we need to define a new policy that we can attach to our new role. This policy will give the role permission to invoke our Lambda functions by passing in the ARNs of the functions.

./lib/*-stack.ts
1// ...our role definition
2
3// 2a. Create the policy that will allow the role to invoke our functions
4const invokeLambdaPolicy = new Policy(this, 'invokeLambdaPolicy', {
5 document: new PolicyDocument({
6 statements: [
7 new PolicyStatement({
8 actions: ['lambda:InvokeFunction'],
9 resources: [recurringLambda.functionArn, oneOffLambda.functionArn],
10 effect: Effect.ALLOW,
11 }),
12 ],
13 }),
14});
ts

Finally, to finish off our role definition, we need to attach our new policy to the role we created earlier. We can achieve this with the below code.

./lib/*-stack.ts
1// ...our policy definition
2
3// 2b. Attach the policy to the role
4schedulerRole.attachInlinePolicy(invokeLambdaPolicy);
ts

So, at this point, we have our Lambda functions defined as well as the role we’re going to use to invoke them from the scheduler. So, now let’s finish our CDK stack by defining the two schedules we’re going to use.

Creating Our Schedules

Let’s now create our two schedules using the `CfnResource` construct mentioned earlier. Below the role and policy code you just added, add the below code to define the two schedules.

./lib/*-stack.ts
1// 3. Defining our one-off schedule
2new CfnResource(this, 'oneOffSchedule', {
3 type: 'AWS::Scheduler::Schedule',
4 properties: {
5 Name: 'oneOffSchedule',
6 Description: 'Runs a schedule at a fixed time',
7 FlexibleTimeWindow: { Mode: 'OFF' },
8 ScheduleExpression: 'at(2023-04-21T07:20:00)',
9 Target: {
10 Arn: oneOffLambda.functionArn,
11 RoleArn: schedulerRole.roleArn,
12 },
13 },
14});
15
16// 4. Defining our recurring schedule
17new CfnResource(this, 'recurringSchedule', {
18 type: 'AWS::Scheduler::Schedule',
19 properties: {
20 Name: 'recurringSchedule',
21 Description: 'Runs a schedule for every 5 minutes',
22 FlexibleTimeWindow: { Mode: 'OFF' },
23 ScheduleExpression: 'cron(*/5 * * * ? *)',
24 Target: {
25 Arn: recurringLambda.functionArn,
26 RoleArn: schedulerRole.roleArn,
27 },
28 },
29});
ts

The two definitions are largely the same, with the key difference being the `ScheduleExpression` property. With the one-off schedule, we use the `at` value to specify a single time when we want the schedule to run and trigger our Lambda function. Where with the recurring schedule, we use the `cron` value to specify a cron expression to trigger the schedule and invoke our Lambda function. In the case of this example, our cron expression is equal to every 5 minutes.

Deploying and Testing

Finally, with our stack fully defined and ready to go, make sure to install `esbuild` (as we’re using the `NodeJsFunction` construct for our Lambda functions) with `npm i esbuild` and then run `cdk deploy` to deploy your CDK stack.

Then once your stack is deployed wait for the time required for your schedules to trigger and then check your CloudWatch logs to make sure your Lambda functions have been triggered. If you followed this tutorial you should now see a log group per Lambda function with the relevant message inside the logs.

Once you’re happy that everything is working as intended and you’re finished with this tutorial, make sure to run `cdk destroy` to destroy any resources provisioned to your AWS account to avoid any unexpected bills coming your way in the future.

Conclusion/Outro

So, to recap, we’ve covered what is the EventBridge Scheduler as well as how we can deploy it via the CDK using the L1 construct `CfnResource` to trigger Lambda functions on both a one-off and recurring basis.

I hope you found this post helpful and if you’d like to see the full example repository for this project make sure to check out the further reading links below.

And, until next time, thank you for reading.

Further Reading



Content

Latest Blog Posts

Below is my latest blog post and a link to all of my posts.

View All Posts

Content

Latest Video

Here is my YouTube Channel and latest video for your enjoyment.

View All Videos
AWS Bedrock Text and Image Generation Tutorial (AWS SDK)

AWS Bedrock Text and Image Generation Tutorial (AWS SDK)

Contact

Join My Newsletter

Subscribe to my weekly newsletter by filling in the form.

Get my latest content every week and 0 spam!