tl;dr

In order to auto-adjust cron expressions of EventBridge rules to summer-/wintertime, you have to use a lambda function. There is a github project which already solves this, but for my use case it was not sufficient. So I created my own implementation, which you can find on this github repository.

Long version

Originally I always worked with Terraform as an IaC tool. In my opinion, Terraform has some advantages over CloudFormation. Recently, however, I got into the situation of having to use CloudFormation in a customer project. In doing so, I ran into a problem. The aim was to define EventBridge rules that carry out certain actions at regular intervals (e.g. start Lambda functions). The problem: EventBridge rules can only be specified in UTC time format. However, UTC does not differentiate between winter and summer time. The cron expressions of these rules would have to be adjusted twice a year. This is ugly and a potential source of error. So I looked around for alternative solutions. I came across this project here, which sounded promising at first.

On closer inspection, however, a few problems became apparent:

  1. The EventBridge rules must be tagged. However, CloudFormation does not currently support the tagging of EventBridge rules, which is why this would have to be done manually. However, there is a risk that the tags will be overwritten or deleted when the CloudFormation stack is executed again.
  2. The example is initially without CloudFormation code. I would have to create this first, which in principle is not a problem with the help of SAM. However, the project could not be built without problems with SAM in a first attempt.
  3. The project cannot cope with more complex cron expressions in which a function is started several times in one day. Example: cron (45 18,20,30? * 2-6 *).

Inspired by this project, I quickly solved the problem myself. The following approach solves the problems mentioned by using a lambda function, which ensures that the cron expressions are set correctly, taking into account the time zone as well as summer and winter time. Here´s how the workflow looks like: This Lambda function runs twice a day. In addition, it is executed after each deployment in the target environment (qa or prod) in order to carry out an adjustment if necessary.

In order to be able to use this automatic adjustment, certain tags are also required here for the EventBridge rule. However, since it is not possible in CloudFormation to specify tags for EventBridge rules, this is also done by the Lambda function. To do this, the Lambda function needs to know when the rule should actually be executed. Therefore, the cron expressions in the CloudFormation code must already be specified in the way at which this rule is actually to be executed (i.e. local time, not in UTC format). As already mentioned, every deployment triggers the Lambda function. The Lambda function uses an environment variable to determine which rules to adjust. To add a rule to this variable, the CloudFormation code must be expanded and the rule name must be specified.

Example

Imagine you want a rule to be executed at 18:45 german time - regardless if winter- or summertime. Since you have to specify the expression for EventBridge rules in UTC, there is a mismatch now: You want the rule to be triggered at 18:45, but for EventBridge you have to specify it for 17:45 (if wintertime applies). The lambda function can help here. Just specify the time you want the rule to be executed.

After rollout, you will see the following in your AWS account:

As you can see, the lambda function has automatically added a tag called local-time to the rule. Furthermore, it has adapted the cron expression in such a way that it executes a 18:45 german time now.

It is also possible to use more advanced cron expressions such as the following:

If you do not specify a timezone, the lambda function assumes Europe/Berlin as the local timezone. If you want to specify a timezone, you have to do that manually via an additional tag:

Caution: This tag won´t be removed during stack rollout as long as you do not do any changes to the rule which lead to a recreation of it. If a recreation is required, the tag gets lost!

Caveats

The solution already helped us a lot in our project. However, it is not perfect, so there are some caveats here which are important to know:

Cron expression

The following formats of cron expressions are currently supported by the function:

cron(45 18,20,30 ? * 2-6 *)

cron(45 18 ? * 2-6 *)

cron(45 * ? * 2-6 *)

cron(* * ? * 2-6 *)

cron(* 18 ? * 2-6 *)

The function does only adapt the hours part of the cron expressions. It does not support expressions with comma-separated minutes such as: cron(45,20,10 18,20,30 ? * 2-6 *)

Timezone-tag may get lost

This tag won´t be removed during deployment as long as you do not do any changes to the rule which lead to a recreation of it. If a recreation is required, the tag gets lost.