AWSでのCron表記でハマったので仕様を確認しておく

CloudWatch EventでのCron表記と一般的なCron表記の差異を確認しました。
2021.09.30

Parameter ScheduleExpression is not valid

ある日CloudFormationでCloudWatch Event Ruleを含むスタックを作成しようとして以下のエラーが発生しました。

Parameter ScheduleExpression is not valid. (Service: AmazonCloudWatchEvents; Status Code: 400; Error Code: ValidationException; Request ID: 4c6a5825-3159-4425-833f-3108086e0b0d; Proxy: null)

そのとき指定していたルールは以下のようなものです。

15 12 * * 1-5 * #月〜金曜日の21時15分

これがnot validだというので見直してみると...

正しい表記

CloudWatch Eventにおける正しい表記は以下になります。

15 12 ? * 1-5 *

ドキュメントを確認すると以下のような制限事項が見つかります。

cron 式の日フィールドと曜日フィールドを同時に指定することはできません。一方のフィールドに値 (または *) を指定する場合、もう一方のフィールドで ? (疑問符) を使用する必要があります。

今回の場合、曜日(1-5)フィールドを指定しているのに日付フィールド(*) も指定しているのでこの制限に当てはまります。

ここで?(アスタリスク)は以下のような記号です。

? (疑問符) ワイルドカードはいずれかを意味します。[日] フィールドに 7 と入力し、7 日が何曜日であってもかまわない場合、[曜日] フィールドに ? を入力できます。

一般的なcron表記

ところでLinuxのcrontabで使われている表記では今回誤って指定した表記は問題なく動くように思います。手元のmacでcrontab(5)を確認すると以下のように記載されています。

The format of a cron command is very much the V7 standard, with a number of upward-compatible extensions.  Each line has five time and date fields, followed by a user name (with optional ``:<group>'' and ``/<login-class>'' suffixes) if
this is the system crontab file, followed by a command.  
Commands are executed by cron(8) when the minute, hour, and month of year fields match the current time, and when at least one of the two day fields (day of month, or day of week)
matches the current time (see ``Note'' below).  cron(8) examines cron entries once every minute.  

The time and date fields are:

field          allowed values
-----          --------------
minute         0-59
hour           0-23
day of month   1-31
month          1-12 (or names, see below)
day of week    0-7 (0 or 7 is Sunday, or use names)

A field may contain an asterisk (*), which always stands for "first-last".
(略)

Note: The day of a command's execution can be specified by two fields -- day of month, and day of week.  If both fields are restricted (ie, are not *), the command will be run when either field matches the current time.  For example, ``30
4 1,15 * 5'' would cause a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday

最後のNoteは日付と曜日の両方にアスタリスク以外が指定されていた場合について言及されています。アスタリスクの場合には冒頭の表記なども踏まえるとstart-endのどの値にもマッチする(=アスタリスクのフィールドは制限しないが、他方のフィールドで指定される)という解釈なのかなと思いました。

AWSにおけるcron表記の確認方法

cron表記が有効か確認する簡単な方法は以下のようにルールを作成してみることです。

# エラー
> aws events put-rule --schedule-expression "cron(15 12 * * 1-5 *)" --name MyRule
An error occurred (ValidationException) when calling the PutRule operation: Parameter ScheduleExpression is not valid.

# OK
> aws events put-rule --schedule-expression "cron(5 12 ? * 1-5 *)" --name MyRule1

{
    "RuleArn": "arn:aws:events:ap-northeast-1:XXXXXXX:rule/MyRule1"
}
# 忘れず削除
> aws-vault exec personal -- aws events delete-rule  --name MyRule

まとめ

日付と曜日は同時に指定しない