33

I want to be able to set a time to invoke an AWS Lambda function, then have that function be invoked then and only then. For example, I want my Lambda function to run at 9:00pm on December 19th, 2017. I don't want it to repeat, I don't want it to invoke now, just at 9:00pm on the 19th.

I understand that CloudWatch provides Scheduled Events, and I was thinking that when a time to schedule this reminder for is inputted, a CloudWatch Scheduled Events is created to fire in that amount of time from now (so like if you schedule it at 8:22pm to run at 9pm, it’ll be 38 mins), then it invokes the Lambda function at 9pm which then deletes the CloudWatch Scheduled Event. My issue with this is that when a CloudWatch Scheduled Event is created, it executes right then, then at the specified interval.

Any other ideas would be appreciated, as I can't think of another solution. Thanks in advance!

Ethan Harlig
  • 756
  • 1
  • 7
  • 22

5 Answers5

26

You can schedule lambda event using following syntax:

cron(Minutes Hours Day-of-month Month Day-of-week Year)

Note: All fields are required and time zone is UTC only

Please refer this AWS Documentation for Details.

Thanks

Kush Vyas
  • 5,813
  • 2
  • 26
  • 36
  • 5
    Maybe I am misunderstanding the documentation, but it seems like all of the examples given for cron expressions are repetitive events. Per their examples, it doesn't seem like I want to use that to invoke a Lambda function once at a given time. – Ethan Harlig Dec 19 '17 at 07:12
  • 12
    @EthanHarlig You misunderstand documentation. It is possible to setup cron for repetitive and one time events both. You have to specify year, month, day of month, hour and minutes for your task. – RredCat Dec 19 '17 at 08:16
  • 2
    @RredCat I see! So if I wanted to invoke a Lambda at 12:00pm on December 20th (UTC), I would use `cron(0 12 20 12 * 2017)` and that would specify that it is 12:00, on December 20, 2017, and it doesn't matter what day of week that is? – Ethan Harlig Dec 19 '17 at 08:20
  • 1
    @EthanHarlig I agree with RredCat , you can set it up one time and on repeat also. – Kush Vyas Dec 19 '17 at 08:42
  • ~~This doesn't seem to work for me? Trying to set UTC 03:20 (about 4 minutes from now): `aws events put-rule --name testing-trigger --schedule-expression 'cron(0 20 3 26 2 ? 2018)'` -> `An error occurred (ValidationException) when calling the PutRule operation: Parameter ScheduleExpression is not valid.`~~ Oops, never mind, starts with minutes, not seconds. – Simon Buchan Feb 26 '18 at 03:16
  • Note that CloudWatch does not guarantee exactly once firing. [This guy](https://forums.aws.amazon.com/thread.jspa?messageID=873044) sent emails twice and I'm seeing duplicated data in my croned reports. Because ["In rare cases, the same rule can be triggered more than once for a single event or scheduled time, or the same target can be invoked more than once for a given triggered rule"](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/CWE_Troubleshooting.html#RuleTriggeredMoreThanOnce). But I want to admit that I see at least one doubled shot every 2 days of 3 for 7 scheduled tasks. – madhead Jan 02 '19 at 10:56
  • @madhead you could have your lambda write to a DB field when it it triggered, so if it is triggered twice the second one knows not to execute – Blundell Mar 20 '19 at 19:52
  • Spot on, thanks! Let me emphasize one thing that caused this error to me (see the notes at the end of the [page linked above](http://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html) ): **"One of the day-of-month or day-of-week values must be a question mark (?)."** – Gergely M Jul 19 '19 at 10:14
  • https://dev.to/byrro/how-to-schedule-any-task-with-aws-lambda-3617 – U.Rush Apr 16 '20 at 13:04
  • 5
    The documentation isn't clear, if a schedule can never trigger again, is it automatically cleaned up? – Thayne May 16 '20 at 04:47
21

You can use DynamoDB TTL feature to implement this easily, simply do the following:

1- Put item with TTL, the exact time you want to execute or invoke a lambda function.

2- Configure DynamoDB Streams to trigger a lambda function on item's remove event.

Once the item/record is about to expire, your lambda will be invoked. you don't have to delete or cleanup anything as the item in dynamodb is already gone.

NOTE: However the approach is easy to implement and scales very well, but there's one precaution to mention; using DynamoDB TTL as a scheduling mechanism cannot guarantee exact time precision as there might be a delay. The scheduled tasks are executed couple of minutes behind.

Muhammad Soliman
  • 21,644
  • 6
  • 109
  • 75
  • 1
    you are genius! – RNA Feb 17 '20 at 07:42
  • 9
    So long as you're ok with it actually taking up to 48 hours. According to the docs: "DynamoDB typically deletes expired items within 48 hours of expiration. The exact duration within which an item truly gets deleted after expiration is specific to the nature of the workload and the size of the table. Items that have expired and have not been deleted still appear in reads, queries, and scans. These items can still be updated, and successful updates to change or remove the expiration attribute are honored." – Gordon Leigh Mar 06 '20 at 21:10
  • Excellent, thanks for the idea. This is exactly what I was looking for. – M Faisal Hameed Sep 11 '20 at 22:24
  • This is a really intriguing method. I really like that this would give the ability to pass arguments to the Lambda function too, unlike a cron execution. It's worth considering, however, deleting an item manually will also trigger the event. So you'll want to check the ExpirationTime in your Lambda. – freethebees Mar 20 '21 at 07:42
11

You can schedule a step function which can wait until a specific point in time before invoking the lambda with an arbitrary payload.

https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-wait-state.html

Something like this

const stepFunctions = new AWS.StepFunctions()
const payload = {
    stateMachineArn: process.env.SCHEDULED_LAMBDA_SF_ARN,
    name: `${base64.encode(email)}-${base64.encode(timestamp)}`, // Dedupe key
    input: JSON.stringify({
      timestamp,
      lambdaName: 'myLambdaName',
      lambdaPayload: {
        email,
        initiatedBy
      },
    }),
  }
await stepFunctions.startExecution(payload).promise()
david_adler
  • 9,690
  • 6
  • 57
  • 97
4

I understand its quite late to answer this question. But anyone who wants to use CRON expression to trigger an event(or call an API) only once can use following example:

This event will be triggered only once on January 1, 2025 - 12:00:00 GMT

00 12 01 01 ? 2025

For those who do not have much knowledge of cron syntax:

Minutes Hours DayOfMonth Month DayOfWeek Year

I am using this with AWS Cloudwatch Events and the result looks like this: enter image description here

Note: I did not have to specify Day of week, since I have given it a fixed date and thats obvious.

Haziq
  • 2,048
  • 1
  • 16
  • 27
  • 4
    keep in mind that you can create only 100 rules per account. There is option to ask support to increase this limit, but seems like this is not designed to be used for high scale. Anyone knows someone who uses like 100k such rules on single account? – Lukas Liesis Apr 05 '21 at 09:28
-1

Invoking a lambda function via Events is asynchronous invocation option. By using CloudWatchEvent to trigger Lambda function we can use cron job but we are still facing issues as Lambda function triggered multiple times for the same cron schedule.PFB Link: https://cloudonaut.io/your-lambda-function-might-execute-twice-deal-with-it/

But this needs Dynamo DB to be implemented in your account and then make your Lambda function Idempotent.