AWS EventBridge Scheduler を始める前に
AWS EventBridge Scheduler は便利ですよね。任意のスケジュールで任意の何かを動かすことができます。
この記事は AWS EventBridge Scheduler を始めるにあたって知っておきたいことを列挙しています。そもそも AWS EventBridge Scheduler で何ができるのかとか、そういうことは解説しません。できることはなんとなくイメージがついている状態で、実際に使ってみる前に知っておくと便利そうな情報を提供することを目的としています。
サマリー
- Schedule Group と Schedule リソースがあり、これらの階層構造で認可が管理されている。
-
iam:PassRoleで Schedule が Role を付与され、その Role を使って Target を定期実行する。 - Target にはいろんなものを指定できる。Lambda とか AWS Batch Job とか。
IAM
EventBridge Scheduler におけるセキュリティポリシーの設定には、Schedule Group によるグルーピングを積極的に使っていくべきです。
Schedule Group は複数の Schedule を保持できる箱で、タグ付けできます。一方、Schedule はタグ付けできないリソースです。そのため、Schedule のポリシーを設定するには、基本的に Schedule Group を利用することになります。
scheduler:* はアクションの数も少なく、考えやすいです。scheduler:TagResource / scheduler:UntagResource が Schedule Group の中にあることからも、Schedule Group だけがタグ付け可能であることが読み取れます。
Schedule Group の作成
Schedule Group の ARN は user provided name を利用した ARN なので、ポリシー設計段階で ARN を特定できます。arn:aws:scheduler:xx-test-1:123412341234:schedule-group/my-group のようになります。
そのため、作成する Schedule Group の名前を決めればポリシーも自然と定まります。
{
"Sid": "AllowTaggedResourceCreation",
"Action": [
"scheduler:CreateScheduleGroup"
],
"Effect": "Allow",
"Resource": [
"arn:aws:scheduler:xx-test-1:123412341234:schedule-group/niaeashes-test"
]
}
$ aws scheduler create-schedule-group --name niaeashes-test --region xx-test-1 --tags "Key=Owner,Value=niaeashes"
{
"ScheduleGroupArn": "arn:aws:scheduler:xx-test-1:123412341234:schedule-group/niaeashes-test"
}
aws:RequestTag/{Tag} Condition を CreateScheduleGroup に設定する場合、CreateScheduleGroup 内部で TagResource を行う関係で、scheduler:TagResource の許可も書かないといけません。
{
"Sid": "AllowTaggedResourceCreation",
"Action": [
"scheduler:CreateScheduleGroup",
"scheduler:TagResource"
],
"Condition": {
"StringEquals": {
"aws:RequestTag/Owner": "niaeashes"
}
},
"Effect": "Allow",
"Resource": [
"arn:aws:scheduler:xx-test-1:123412341234:schedule-group/niaeashes-test"
]
}
これで、Owner = niaeashes でない CreateScheduleGroup は認可されず、TagResource もまた認可されません。さらにリソース名も絞られているので、最終的に「Owner = niaeashes である niaeashes-test Schedule Group の作成が許可される」となります。
Schedule Group の管理
考えることは scheduler:DeleteScheduleGroup を許可するかどうか、くらいでしょうか。scheduler:UntagResource も Schedule Group に対してのみ意味がある(Schedule はタグを持たない)ので、こちらも要検討ですね。安易に許可すると Tag を使った認可が壊れるかも。
Delete Schedule Group はかなり強力なアクションで、その Schedule Group に属しているすべての Schedule をまとめて削除します。そのため、重要な Schedule が多数入っている Schedule Group について付与すべきかしっかり検討すべきです。例えば、terraform で管理している場合、オペレーションのミスによる致命的な影響を避けるために prevent_destroy = true を設定すべきかもしれません。
Schedule の管理
Schedule Group と Schedule は ARN で相互に関連しています。
arn:aws:scheduler:xx-test-1:xxxxxxxxxxxx:schedule-group/niaeashes-test
arn:aws:scheduler:xx-test-1:xxxxxxxxxxxx:schedule/niaeashes-test/{name}
なので、基本的に「特定の Schedule Group 内部の Schedule に対する権限」という形でデザインするのが良いでしょうね。
Schedule の作成には Target が必要です。そして、Target には起動するものの ARN と、それに渡す Role が必要です。そのため、Schedule に対して Target を起動することができる IAM Role を Pass Role できる必要があります。
ややこしいですね。
- Schedule の Role = Target を起動できる
Role A - Schedule を管理する
Principal Xは Schedule にRole Aを Pass Role できる必要がある -
Role Aは scheduler.amazonaws.com から Assume Role できる必要がある
Schedule を管理する Principal
{
"Action": [
"iam:PassRole"
],
"Effect": "Allow",
"Resource": [
"arn:aws:iam::123412341234:role/SchedulerTestRunnerRole"
],
"Condition": {
"StringLike": {
"iam:AssociatedResourceArn": "arn:aws:scheduler:xx-test-1:123412341234:schedule/niaeashes-test/*"
},
"StringEquals": {
"iam:PassedToService": "scheduler.amazonaws.com"
}
}
}
例えば CLI で aws scheduler create-schedule する Role に必要な Policy Statement の例です。
PassRole 先の Schedule を制限するには iam:AssociatedResourceArn を使わなければなりません。(aws:ResourceTag/{Tag} は無視され、iam:ResourceTag/{Tag} は Role のタグを制限します)
Schedule 上で Target を実行する Role = Schedule Role
便宜上 Schedule Role と呼びます。
Trust Policy
scheduler.amazonaws.com が Assume Role して Target を実行する(実行が具体的に意味するところは、Target によって異なる。例えば Lambda なら Lambda Invoke)ための Role です。
Trust Policy で scheduler.amazonaws.com からの Assume Role を許可する必要があります。この辺りの制限の書き方はいろんなバリエーションと強度がありますが、ここでは割愛します。
Policy
すでに述べたとおり、例えば AWS Lambda Function を実行したい場合、lambda:InvokeFunction が必要です。必要最低限の権限に絞りましょう。
ListSchedules / ListScheduleGroups
ListSchedules は「特定の Group に属している Schedule をリストする」権限ではないです。そのため、ListSchedules / ListScheduleGroups は AWS アカウント内部の Schedule / Schedule Group を全て確認できるようになります。場合によってはもちろん付与しないほうがいいでしょう。いや、場合によってはというか、基本的に付与しないほうがいいです。
この手の権限はビジネスロジックを推測するのに十分な情報元になり得ます。
DeleteSchedule / DeleteScheduleGroup
すでに述べたとおり、Delete Schedule Group アクションは、内部でその Schedule Group に属する Schedule をすべて Delete Schedule します。Schedule が存在しなくても内部で Delete Schedule の実行権限を確認するので、Schedule Group の削除の際には、実質的に両方の権限が必要になります。
IaC ツールは場合によって Schedule を作り直す(Replace = Delete and Create)ので、結局のところ Delete Schedule が実行できる必要があることも多いです。
Default Schedule Group
あんまり使わないと思う。いつ使うんだろう。
Schedule の設定
Schedule にはいろんな設定項目があります。代表的なものを書いておくので、「こういうことができるんだなー」という感じで参考にしてください。
ちなみに、Schedule は個別で Enable / Disable にすることができます。使わないやつは Disable にするのが便利です。
Flexible Time Window
「実際に実行するタイミングをランダムに分散させてくれる」設定ができます。これにより、DB や外部 API に対して負荷分散をしたり、Bot の通知があたたかに分散してうれしくなったりします(?)。ランダムなので分散はするが、分散の“最適化”はないので注意。
例えば Lambda Function を呼び出す Schedule なら、Lambda Function の Rate Limit が問題になったりします。
なるべく厳密な時間に起動したい場合や、そもそも利用回数が少ない場合などは考慮する必要はあまりないですね。
Mode = "OFF" とすることで無効化できますが、一部の IaC ツールでは "OFF" が false 扱いになったりすることもあるようなので、うまくエスケープしてやる必要があるらしいです。
Schedule Expression
Schedule の実行頻度をコントロールします。
rate / cron / one-time の3種類があります。cron は UNIX とは違い、曜日か日のどちらかを ? でオプションにします。私はこの記法の差でハマったことがあります。
Schedule Expression Timezone という設定項目もあったりするので、使いたい人は使うのがいいです。
dead-letter queue (DLQ)
Amazon SQS の Queue を DLQ として設定できます。失敗した時にどういうメッセージが入っってくるかは、Target によるっぽいです。
- Schedule の DLQ は「Target の実行に失敗した時」に検知されます。例えば Lambda Function において Invoke Function に失敗した場合に「Schedule 側の失敗」として Retry や DLQ 送信の対象になります。
- Target 側での失敗は Schedule はケアしません。
- Schedule Role が SQS への Send Message ができなければなりません。設定するならこれがないと意味がないので、注意しましょう。Schedule Group の Monitoring タブで InvocationsFailedToBeSentToDeadLetterCount を確認することで、「DLQ への送信に失敗した数」を知ることができます。
- Retry Policy は DLQ への送信の条件などを設定します。基本的には、Retry でもどうにもならなかった場合に DLQ に情報が送信されます。
- Target 側での失敗は Schedule はケアしません。
Schedule の動作機序
DLQ などを踏まえて、Schedule の動作機序を確認しておくと理解しやすいかもしれません。公式のドキュメントに記述は(多分)ないと思うので、だいたいこんな感じというのを書いておきます。
- Schedule Expression + Flexible Time Window が評価されて Schedule が実行されることが決まる。
-
scheduler.amazonaws.comが Schedule Role に Assume Role する。Schedule Role には Target の実行権限が付与されているだろう、と期待する。 - Target ごとに決まっている方法で実行を試みる。(例えば Lambda なら Invoke Function)
- 実行に成功したら:あとは Target の責任。Schedule は役目を終えて消える。
- 実行に失敗したら:Retry Policy に応じてリトライを試み、それでもダメなら DLQ にログを積む。DLQ への Send Message に失敗したら何もできなくて終わる。
CloudWatch Metrics
便利なメトリクスがあります。Schedule Group は権限管理には使えるけど List Schedules には使えなくて不便ですが、Metrics は Schedule Group 単位で確認できます。
InvocationDroppedCount や InvocationsFailedToBeSentToDeadLetterCount は Schedule のデバッグに役立ちます。DLQ に送信されたイベントを確認することで、問題をレビューすることもできます。
EventBridge Scheduler における DLQ は失敗したメッセージを格納するためだけの場所で、EventBridge Scheduler と SQS の DLQ だけでリトライパイプラインを作ることはできません。そのため、Target が起動しない場合、まずは Metrics を見て原因が Schedule 側にあることを確認してから、IAM や DLQ を見ていくのが良いと思います。
AWS EventBridge Scheduler でできないこと
- ログは見えない。DLQ と Metrics だけ。
- EventBridge Rules や EventBridge Bus とは完全に別のシンプルな仕組み。アクションの名前空間も別だしね。
- Schedule だけで Payload を変動させることはできない。つまり、決まったことしかできない。
AWS Lambda Function
Target の典型的な宛先の一つでしょう。Schedule 側で設定した Payload がそのまま Lambda Function に渡されますが、未設定の場合は勝手にメタ情報が渡されます。
{
"version" => "0",
"id" => "15692dad-84a1-4af0-a3ad-40e050fa08ff",
"detail-type" => "Scheduled Event",
"source" => "aws.scheduler",
"account" => "123412341234",
"time" => "2025-12-01T15:00:20Z",
"region" => "xx-test-1",
"resources" => [
"arn:aws:scheduler:xx-test-1:123412341234:schedule/niaeashes-test/niaeashes-test-1"
],
"detail" => "{}"
}
逆に Schedule 側で Payload を指定すると、Payload がそのまま Invoke に渡されます。どうやら、Target = Lambda の場合に渡されるデフォルトペイロードは EventBridge Parameters のデフォルト値が利用される?ようです。が、全体がデフォルト値なのであってエンベローブではないため、Payload を設定すれば上書きされます。ノイズになるので Lambda を使う時は {} とでもしておくといいかもしれません。
Version / Alias を指定することもできます。
すでに述べている通り、Schedule Role に必要な権限は lambda:InvokeFunction です。
最後に
私のケースでは AWS Batch を起動したり、Step Functions を起動したりしています。
最初に AWS Batch を使いたい時は「なんで EventBridge Scheduler なんて全然関係ないものを利用しないといけないんだ?」とおもっていました。今でも思っています。terraform のような IaC に付与する権限が肥大化するし、権限をコンパクトにするにはしっかりサービスの設計を理解しないといけないので辛いですね。
Discussion