NO IMAGE

EventBridge Scheduler 本番運用ガイド Terraform DLQ タイムゾーン

NO IMAGE
目次

1. この記事について

fig01: EventBridge Scheduler 全体アーキテクチャ

この記事で得られること — 6 つの習得ポイント

  • タイムゾーン対応 (Asia/Tokyo): timezone = "Asia/Tokyo" を使った JST cron 設定と、UTC 誤解釈を防ぐための移行チェックリストを習得する
  • Flexible Time Window の実運用価値: Lambda cold start バースト防止を目的とした ±分ずらし設定の仕組みと、同時多発ジョブへの効果を理解する
  • Templated vs Universal targets の使い分け: Lambda / Step Functions / SQS / ECS RunTask 等の Templated target と任意 AWS API 呼び出しの Universal target の IAM 自動/手動設計の違いを習得する
  • Retry + DLQ 本番設計パターン: RetryPolicy (最大 185 回・最大 86400 秒) + DLQ (SQS) + CloudWatch Alarm の「失敗3回→DLQ→通知」完全設計パターンを取得する
  • Terraform 完全 HCL: aws_scheduler_schedule / aws_scheduler_schedule_group + IAM ロール / ポリシーの本番投入に使える完全 HCL と、よくある落とし穴 4 選を習得する
  • CloudWatch 運用監視基盤: InvocationAttemptCount / TargetErrorCount / TargetErrorThrottledCount / InvocationThrottleCount / InvocationDroppedCount の 5 指標 + アラート設計 + CloudTrail 証跡の運用基盤を構築する
この記事の対象読者 — 前提知識チェックリスト

  • ✅ CloudWatch Events の Rule schedule (aws_cloudwatch_event_rule + aws_cloudwatch_event_target) を本番環境で実装・運用した経験がある
  • ✅ Terraform の基本構文 (resource / variable / locals / output) の読み書きができる (中級以上)
  • ✅ AWS IAM ロール・ポリシーの設計原則 (最小権限・assume_role_policy の仕組み) を理解している
  • ✅ EventBridge Scheduler の存在は知っているが、CWE Rule schedule との具体的な違いや移行手順がわからない
  • ❌ 対象外: EventBridge / CloudWatch Events そのものが初めての方は、先に入門記事をご参照ください

1-1. 本記事のゴール

CloudWatch Events の Rule schedule (aws_cloudwatch_event_rule + aws_cloudwatch_event_target) は、AWS における定期実行の主役として長く使われてきました。Lambda の定期起動、Step Functions のスケジュール実行、ECS Task の夜間バッチなど幅広い用途で実績あるサービスです。しかし 2022 年 11 月に GA した EventBridge Scheduler は CWE Rule schedule の後継として設計されており、本番運用で必要な機能が大幅に強化されています。

本番運用の観点で特に重要な強化点は 3 つです。第一に タイムゾーン指定: CWE Rule schedule では UTC 固定だった cron を、Asia/Tokyo を含む 300 以上のタイムゾーンで指定できるようになりました。第二に Flexible Time Window: 同一時刻に起動する複数ジョブを指定した時間幅の中でランダムにずらし、Lambda cold start のバーストを防止します。第三に 30 種類以上の直接 Target: CWE Rule schedule の数種類に対して、Lambda / Step Functions / SQS / ECS RunTask / CodeBuild 等 30 種類以上を直接呼び出せる Templated targets に拡張されています。

本記事では以下の 6 軸すべてについて、概念説明・実装例・Terraform HCL・運用上の注意点をセットで解説します。

本記事での解説内容
タイムゾーン指定Asia/Tokyo による JST cron 設定、UTC 誤解釈の検出方法、既存 CWE Rule からの移行確認事項
Flexible Time WindowFLEXIBLE / OFF モードの使い分け、Lambda cold start バースト防止の実運用価値、設定範囲 (1-1440 分) の選定基準
Templated vs Universal targetsLambda / Step Functions / SQS / ECS RunTask 等 Templated target の IAM 自動化、Universal target の手動 IAM 設計と注意点
Retry + DLQRetryPolicy パラメータ詳細 (maximum_retry_attempts / maximum_event_age_in_seconds)、DLQ の SQS ARN 設定、CloudWatch Alarm 連動
Terraform 実装aws_scheduler_schedule / aws_scheduler_schedule_group リソース設計、IAM ロール/ポリシーの完全 HCL、落とし穴 4 選
運用監視CloudWatch Metrics 5 種の意味と推奨アラート閾値、CloudTrail 証跡の確認ポイント、1M invocations $1.00 のコスト見積もり注意点

本記事を読んで変わること:

局面現状本記事読了後
JST cron 設定UTC でオフセット計算して cron 式を手動管理timezone = "Asia/Tokyo" で直接 JST 指定
Flexible TW未設定・全ジョブが同時刻に一斉起動し cold start バースト同時多発 cold start を ±N 分で平滑化
Target 選定Lambda 経由で全サービスを呼び出しコスト増Templated/Universal の使い分けで Lambda 削減
失敗対策失敗が無音で消える (DLQ/Retry 未設定)3回失敗→DLQ→CWAlarm の完全フローを設計できる
Terraform HCLサンプルが最小限で IAM ロール設計が不完全本番投入直結の完全 HCL をコピー&ペーストで着手できる
コスト管理無料枠があると誤解したまま見積もり漏れ無料枠なし・1M $1.00 の正確な見積もり方法を習得

1-2. 読者像

想定読者プロファイル:

CloudWatch Events (CWE) の Rule schedule を本番運用した経験を持ち、EventBridge Scheduler への移行や新規採用を検討している AWS エンジニアを主な対象としています。以下のいずれかのシナリオに当てはまる場合、本記事は特に有用です。

  • 移行シナリオ: 既存の aws_cloudwatch_event_rule + aws_cloudwatch_event_target を Terraform で管理しており、システムリニューアルや AWS ベストプラクティス対応として Scheduler への移行を担当することになった。移行時に何が変わり、何に気をつければよいかを体系的に把握したい。
  • 新規設計シナリオ: 新しいシステムで定期実行が必要になり、CWE Rule schedule と EventBridge Scheduler のどちらを選ぶべきか判断材料が欲しい。技術比較と本番事例を踏まえて設計判断したい。
  • 品質改善シナリオ: 既存のスケジュール実行に失敗やタイムゾーン誤認識の問題が発生している。Retry + DLQ 設計を見直し、本番品質のアーキテクチャに改善したい。

スキルレベルの目安: Terraform / IAM / Lambda のいずれかを本番環境で扱った経験がある中級以上のエンジニアを想定しています。

本記事は入門ガイドではなく「本番運用完全ガイド」を意図しています。aws_cloudwatch_event_rule の基本的な使い方や、Lambda / SQS / Step Functions の基礎概念の説明は省略します。これらが初めての場合は、先に入門記事をご参照の上、本記事に戻ってください。

なお、本記事の Terraform HCL は実際の本番環境を想定しており、variables.tf / locals.tf / schedule.tf / iam.tf に分割した構成で提示します。ファイル分割は小規模プロジェクトでは省略しても構いませんが、スケジュール数が多くなるほど管理しやすくなります。

1-3. なぜ今これを書くか

EventBridge Scheduler が GA してから約 2 年が経過した現在、AWS 公式ベストプラクティスでは「新規の定期実行には EventBridge Scheduler を推奨する」という姿勢が明確になっています。しかし実際の現場では既存の CWE Rule schedule をそのまま継続しているシステムが多く、「いつ・どのように移行するか」の判断材料が不足しているケースをよく目にします。本記事を執筆した背景には以下の 3 つの課題があります。

課題 1: 移行時のハマりポイントが体系化されていない
タイムゾーン未設定による UTC 誤解釈、flexible_time_window の設定競合、DLQ の ARN と Queue URL の混同、delete_after_completion の適用範囲誤解など、CWE からの移行でよく踏む落とし穴は公式ドキュメントを読むだけでは見えにくい。本記事ではこれらを「よくあるエラー 10 選」として体系化し、事前に回避できる情報を提供する。

課題 2: Terraform HCL の実用的なサンプルが不足している
Terraform Registry の aws_scheduler_schedule ドキュメントにあるサンプルは最小限の設定しか示しておらず、IAM ロールの完全設計・DLQ 統合・schedule_group の活用まで含めた実用サンプルが少ない。本記事では本番投入に直結する完全 HCL を提供する。

課題 3: Flexible Time Window の実運用価値が認知されていない
「少しずらす程度の機能」と思われがちだが、多数の Lambda 関数が同一時刻に一斉起動する場合の cold start バースト制御として、運用規模が大きくなるほど重要になる機能である。本記事では具体的な設定範囲の選定基準と効果を示す。

これら 3 つの課題を解決することで、「CWE Rule schedule から Scheduler への移行」を安全かつ確実に実行できる状態を目指します。

1-4. 差別化軸 — 既存記事との峻別

本記事は EventBridge Scheduler を本番運用の観点で体系的に解説する記事として、以下の 5 軸で差別化しています。

差別化点詳細
CWE Rule schedule との移行比較タイムゾーン非対応→対応・Target制限→30+対応・cron 式読み替え表の 3 軸で移行判断を即解決
Flexible TW の実運用価値を明示Lambda cold start バースト防止として、同時多発ジョブ規模別の設定目安と効果を具体的に示す
Templated/Universal 使い分け表IAM 自動化・手動設計・対応サービス・retry 挙動を比較表で整理し、設計選択を即判断可能に
JST cron の具体実例毎朝 09:00 JST・月末バッチ・毎週月曜など日本読者の実ニーズを Asia/Tokyo 指定で解説
Retry+DLQ 完全本番設計「失敗3回→DLQ→CloudWatch Alarm→SNS」の完全設計 + Terraform HCL + 監視クエリをワンセットで提供

1-5. 関連記事

本記事と関連性の高い公開済み記事を以下に示します。EventBridge Scheduler はイベントバス (EventBridge Rules / Pipes) とは異なるサービスです。本記事では Scheduler (定期実行) に特化しており、イベント駆動パターンとは分離して解説します。

記事タイトル関連度補足
EventBridge VPC Lattice Fargate 統合イベントバス用途。Scheduler とは別機能のため本記事との重複なし

1-6. 本記事の執筆方針 — 3点セットと検証環境

本記事は 2026-04 時点の AWS 仕様 に基づき執筆しています。各ハンズオン実装例は以下の 3 点セットで構成し、手元環境で再現可能なレベルで記述します。

  • Terraform HCL: aws_scheduler_schedule / aws_scheduler_schedule_group / aws_iam_role の完全 HCL。変数 (variable) と locals 分離・モジュール化可能な構成で記述
  • AWS CLI v2: aws scheduler create-schedule / aws scheduler update-schedule による同等操作。学習・デバッグ・スクリプト組込みの用途向け
  • コンソール手順: スクリーンショットは掲載しませんが、操作ステップをリスト形式で記述。視覚的学習者や権限制限環境での参照向け

3点セット適用章の一覧:

TerraformAWS CLIコンソール手順
§3 スケジュール作成
§4 ターゲット設計
§5 Terraform 実装✅ (主体)参照のみ
§6 信頼性設計参照のみ
§7 監査+運用✅ (CloudWatch CLI)コンソール中心

コード例はすべて ap-northeast-1 (東京リージョン) で検証済みです。AWS アカウント ID (123456789012) / 関数名 (my-function) / キュー名は例示であり、実際の値に置き換えてください。Terraform バージョンは 1.9 以上、AWS Provider は 5.x 以上を前提とします。サービス仕様の変更により内容が変化する可能性があります。最新情報は AWS 公式ドキュメントをご参照ください。

AWS EventBridge Scheduler 公式ドキュメントを確認する


2. 前提・環境・準備

fig02: 検証環境と IAM ロール構成

2-1. 前提環境

本記事のハンズオンは以下のツール・バージョン・権限の組み合わせで検証しています。Terraform と AWS CLI は最新 stable を推奨しますが、以下のバージョン以上であれば動作します。

ツール / サービスバージョン / 条件備考
Terraform1.9.x 以上hashicorp/aws provider ~> 5.0 が必要
AWS CLIv2.x 以上aws scheduler サブコマンドが v2 で利用可能
aws provider aws_scheduler_schedule5.x 以上Terraform Registry での対応バージョン
AWS アカウント管理者相当権限で検証本番では最小権限 IAM ロールを使用 (下記参照)
リージョンap-northeast-1 (東京)リージョン依存のサービスは本文中に注記

必要な IAM 権限 (実行ユーザー / ロール):

EventBridge Scheduler のスケジュールを作成・管理するために、以下の権限が必要です。本番環境では Resource を実際のリソース ARN に絞り、最小権限化することを推奨します。

{
  "Version": "2012-10-17",
  "Statement": [
 {
"Effect": "Allow",
"Action": [
  "scheduler:CreateSchedule",
  "scheduler:UpdateSchedule",
  "scheduler:DeleteSchedule",
  "scheduler:GetSchedule",
  "scheduler:ListSchedules",
  "scheduler:CreateScheduleGroup",
  "scheduler:DeleteScheduleGroup",
  "scheduler:ListScheduleGroups",
  "lambda:InvokeFunction",
  "iam:CreateRole",
  "iam:PutRolePolicy",
  "iam:PassRole",
  "sqs:SendMessage",
  "sqs:GetQueueAttributes",
  "cloudwatch:PutMetricAlarm",
  "cloudwatch:GetMetricStatistics"
],
"Resource": "*"
 }
  ]
}
</code>

iam:PassRole は Scheduler が Target (Lambda 等) を呼び出す際の実行 IAM ロールを Scheduler サービスに渡すために必要です。この権限が欠落すると AccessDeniedException でスケジュール作成に失敗します。

Terraform Provider バージョン確認:

terraform {
  required_version = ">= 1.9"
  required_providers {
 aws = {
source  = "hashicorp/aws"
version = "~> 5.0"
 }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}
</code>

aws_scheduler_schedule リソースは Terraform AWS Provider v4.67 以降でサポートされています。~> 5.0 (v5 系最新) を推奨します。v4.67 でも動作しますが、flexible_time_window の一部引数でバリデーションの挙動が異なる場合があります。本記事のコード例は v5.x で検証しています。

2-2. 使用技術スタック

本記事で扱うサービス・ツールのスタックを以下に示します。

技術スタック一覧

  • EventBridge Scheduler (2022-11 GA): スケジュール管理・Flexible Time Window・30+ ターゲット直接呼び出しを提供するマネージドスケジューラサービス
  • Schedule Group: スケジュールの論理グループ化。タグ付け・一括削除・IAM 条件キーによるアクセス制御が可能。料金への影響なし
  • AWS Lambda: Templated target として最も多用されるサーバーレス関数実行環境。Scheduler から直接 InvokeFunction を呼び出せる
  • AWS Step Functions: Scheduler から直接 startExecution を呼び出す際の Templated target として使用
  • Amazon SQS: DLQ (Dead Letter Queue) として失敗イベントを受け取る。Templated target として Scheduler からの直接メッセージ送信先にも使用
  • Amazon ECS: RunTask を Templated target として呼び出すコンテナ実行環境。バッチ処理等で活用
  • Terraform: aws_scheduler_schedule / aws_scheduler_schedule_group / aws_iam_role による Infrastructure as Code 管理
  • Amazon CloudWatch: Scheduler のメトリクス 5 種監視・アラート設計・CloudTrail 連携による可観測性基盤

2-3. ゴール状態の定義

本記事を通じて構築する検証環境のゴール状態を以下に示します。この状態が動作確認できれば、各 §§ の実装が正しく完了しています。

ゴール: JST 定期実行 + Flexible TW + Retry+DLQ の 3 点セット構成

  1. JST cron スケジュール: 毎朝 09:00 JST に Lambda 関数 (my-scheduled-function) を起動する Schedule が Schedule Group (production-group) 配下に存在し、timezone = "Asia/Tokyo" が設定されている
  2. Flexible Time Window: mode = "FLEXIBLE" / maximum_window_in_minutes = 15 で ±15 分の起動ずれを許容し、同時多発 cold start バーストを防止している
  3. RetryPolicy: 失敗時に最大 3 回リトライ (2 時間以内) を行い、3 回とも失敗した場合は DLQ (SQS) にイベントを配送する設定が有効である
  4. DLQ (SQS): 失敗イベントが SQS (scheduler-dlq) に蓄積される。CloudWatch Alarm (scheduler-dlq-alarm) が ApproximateNumberOfMessagesVisible > 0 でアラートを発火し、SNS 経由でオンコールへ通知する
  5. CloudWatch Metrics モニタリング: TargetErrorCount のアラームが設定され、Target 呼び出しエラーを即時検知できる体制が整っている
  6. Terraform 完全管理: 上記すべてが aws_scheduler_schedule_group + aws_scheduler_schedule + aws_iam_role + aws_sqs_queue + aws_cloudwatch_metric_alarm の Terraform HCL で管理されており、terraform plan で差分確認・terraform apply で再現が可能

ゴール確認コマンド (§§ 完了後に実行):

# スケジュール一覧確認 (production-group 内)
aws scheduler list-schedules --group-name production-group --region ap-northeast-1

# スケジュール詳細 + ターゲット確認
aws scheduler get-schedule--name "my-daily-job"--group-name "production-group"--region ap-northeast-1--query '{Expression:ScheduleExpression,Timezone:ScheduleExpressionTimezone,FlexTW:FlexibleTimeWindow,Target:Target}'

# DLQ メッセージ数確認
aws sqs get-queue-attributes--queue-url "https://sqs.ap-northeast-1.amazonaws.com/123456789012/scheduler-dlq"--attribute-names ApproximateNumberOfMessages
</code>

2-4. 全体アーキテクチャ

QG-1: EventBridge Scheduler 全体アーキテクチャ — Scheduler / Schedule Group / Target 30+ / DLQ / IAM の俯瞰

EventBridge Scheduler のアーキテクチャは以下の 5 レイヤで構成されます。各レイヤの役割を理解することが、Terraform HCL 設計・IAM 権限設計・DLQ 設計の土台になります。

レイヤ 1: Schedule Group (論理グループ)
Schedule Group はスケジュールの論理コンテナです。デフォルトで default グループが存在しますが、本番環境では用途別 (production / batch / maintenance 等) にグループを分けることを推奨します。グループ単位でタグを付けられるため、コスト配分・IAM 条件キー (scheduler:GroupName) によるアクセス制御・一括削除が容易になります。

レイヤ 2: Schedule (スケジュール定義)
Schedule は Group の配下に属し、「いつ」「誰に」「何を」「どのように失敗対策するか」を定義します。3 種類のスケジュール式をサポートします:

  • cron 式 (6 フィールド): cron(分 時 日 月 曜日 年) — 年フィールドを含む。CWE の 5 フィールドと異なる点が最大の移行注意事項
  • rate 式: rate(1 hour) / rate(30 minutes) / rate(7 days) — 固定間隔での繰り返し実行
  • at 式 (one-time): at(2026-12-31T23:59:00) — ISO 8601 形式による 1 回限りの実行

timezone パラメータで Asia/Tokyo を指定すると、cron/rate 式を JST 基準で評価します。UTC 固定の CWE Rule schedule から最も恩恵を受ける機能です。

レイヤ 3: Target (呼び出し先)
Scheduler のターゲットは 2 種類に分類されます:

  • Templated targets (推奨): Lambda / Step Functions / SQS / ECS RunTask / CodeBuild / CodePipeline / Kinesis Data Streams / Inspector / Glue 等 30+ サービス。Scheduler が IAM を内部管理し、role_arn を指定するだけで呼び出しが完結する
  • Universal targets: 上記 30+ 以外の任意 AWS API (例: rds:StartDBInstance / ec2:StopInstances) を呼び出す。IAM ポリシーを手動設計する必要があり、sts:AssumeRole 権限の付与が必須

レイヤ 4: IAM ロール (Scheduler 実行用)
Scheduler が Target を呼び出す際、scheduler.amazonaws.comassume_role_policy の Principal に指定した IAM ロールが必要です。このロールには対象 Target に応じた権限を付与します (Lambda であれば lambda:InvokeFunction・SQS DLQ 書き込みには sqs:SendMessage)。

レイヤ 5: DLQ + CloudWatch (信頼性・可観測性)
RetryPolicy で設定した回数・時間内に Target 呼び出しが成功しなかった場合、イベントは DLQ (SQS キュー) に配送されます。DLQ の ApproximateNumberOfMessagesVisible メトリクスを CloudWatch Alarm で監視し、SNS トピック経由でオンコールへ通知する構成が本番標準です。

全体フロー (正常系・異常系)

正常系:
EventBridge Scheduler
  ↓ cron/rate/at 式 + timezone 評価
Schedule Group (production-group)
  ↓ Flexible TW: ±15分ランダムずらし
Schedule (my-daily-job)
  ↓ IAM Role (scheduler.amazonaws.com AssumeRole)
Target: Lambda (my-scheduled-function)
  ↓ 実行成功 → InvocationAttemptCount +1

異常系:
Lambda → 実行失敗 (タイムアウト / エラー)
  ↓ RetryPolicy: max_retry_attempts=3 / max_event_age=7200s
  再試行 (最大3回)
  ↓ 全試行失敗
DLQ (SQS: scheduler-dlq) にイベント配送
  ↓ ApproximateNumberOfMessagesVisible > 0
CloudWatch Alarm → SNS Topic → Email / Slack 通知
</code>

2-5. Scheduler と CWE Rule schedule の共存方針

EventBridge Scheduler への移行は一斉切り替えではなく、段階的移行を推奨します。AWS は CWE Rule schedule (EventBridge Rules の schedule イベントソース) を当面維持する方針を示しており、既存のルールはそのまま稼働し続けます。

共存期間中の対応方針:

対象推奨アクション優先度
新規の定期実行スケジュールEventBridge Scheduler で実装
既存 CWE Rule schedule (正常稼働中)稼働継続・次回改修時に移行を検討
既存 CWE Rule schedule (タイムゾーン問題あり)優先的に Scheduler へ移行
Scheduler でサポートされない TargetCWE Rule schedule を継続使用

CWE Rule schedule と Scheduler が同一 Lambda 関数を二重起動しないよう、移行中は実行ソースを Lambda のログに記録し、移行完了後に旧 CWE Rule を terraform destroy または AWS コンソールで削除することを推奨します。

Terraform の管理においても、aws_cloudwatch_event_rule (CWE) と aws_scheduler_schedule (Scheduler) は別リソースとして独立して管理できます。移行完了後は旧 CWE リソースを state から削除し、terraform apply で実リソースを確実に削除することで、課金の重複を防ぎます。

移行フェーズ例 (3フェーズ移行):

フェーズ内容期間目安
フェーズ 1: 並行運用Scheduler を新規追加。既存 CWE Rule は停止せず両方稼働させ、ログで実行を確認1-2 週間
フェーズ 2: CWE 無効化CWE Rule を enabled = false に設定。Scheduler 単独稼働で問題がないことを確認1 週間
フェーズ 3: CWE 削除terraform destroy または AWS コンソールで CWE Rule を削除。課金停止を確認1 日

フェーズ 1 の並行運用期間中は 同一 Lambda が 2 回起動するため、Lambda 側でべき等性を担保 (実行済みフラグ / DynamoDB 条件付き書き込み等) することを推奨します。DynamoDB のべき等キーとして Scheduler のコンテキスト変数 <aws.scheduler.execution-id> を活用できます。


3. なぜ EventBridge Scheduler か (CWE 比較・移行メリット・料金)

3-1. 旧 CWE Rule schedule の制約

CloudWatch Events (現 Amazon EventBridge) の Rule schedule は、2016 年ごろから AWS の定期実行の主役として広く使われてきました。しかし本番運用を続ける中で、以下の制約が課題として顕在化しています。

制約 1: タイムゾーン非対応 (UTC 固定)
CWE Rule schedule の cron 式はすべて UTC で評価されます。JST (UTC+9) で「毎朝 09:00 に実行」したい場合、cron(0 0 * * ? *) と記述しなければならず、担当者が変わるたびにオフセット計算ミスが発生するリスクがあります。また「月末最終営業日 JST 17:00」のような日付依存の cron 式は実現が困難です。

制約 2: ターゲット種類の制限
CWE Rule schedule で直接 Target として指定できるサービスは Scheduler の 30+ と比較して限られています。Kinesis Data Streams への直接送信や CodeBuild プロジェクトの起動は CWE ではカスタム Lambda が必要でしたが、Scheduler では Templated target として Lambda 不要で設定できます。

制約 3: Flexible Time Window の欠如
CWE Rule schedule は指定時刻に厳密に起動します。同一時刻に多数のスケジュールが集中する環境では、Lambda の同時実行数上限に達したり downstream サービスへの突発的な負荷を引き起こすリスクがあります。Flexible Time Window は Scheduler 固有の機能であり、CWE には実装されていません。

制約 4: Schedule の論理グループ化がない
CWE Rule schedule には Group の概念がなく、大量のスケジュールを用途別に管理する際はタグに依存するしかありません。Scheduler の Schedule Group では、グループ単位での一括削除・タグ継承・IAM 条件キー設定が可能になります。

3-2. Scheduler の新機能

EventBridge Scheduler は CWE Rule schedule の制約をすべて解消しつつ、以下の新機能を追加しています。

新機能 1: タイムゾーン指定 (300+ 種類)
timezone パラメータで IANA タイムゾーン名 (Asia/Tokyo / America/New_York 等 300 種類以上) を指定できます。以下のように記述するだけで JST 毎朝 09:00 の実行が正確に設定できます:

aws scheduler create-schedule \
  --name "daily-job" \
  --schedule-expression "cron(0 9 * * ? *)" \
  --schedule-expression-timezone "Asia/Tokyo" \
  --target '{"Arn": "arn:aws:lambda:ap-northeast-1:123456789012:function:my-function", "RoleArn": "arn:aws:iam::123456789012:role/scheduler-role"}' \
  --flexible-time-window '{"Mode": "OFF"}'
</code>

新機能 2: Flexible Time Window (1-1440 分)
FLEXIBLE モードを設定すると、実際の起動時刻をスケジュール時刻から maximum_window_in_minutes 分の範囲でランダムにずらします。同一時刻に 50 本のスケジュールが集中する場合、maximum_window_in_minutes = 30 に設定すると 30 分間でランダムに分散させることができ、Lambda の同時実行バーストを大幅に緩和できます。

新機能 3: Universal targets (任意 AWS API 呼び出し)
Scheduler の Universal targets 機能を使うと、AWS が公開している任意の API エンドポイント (例: rds:StartDBInstance / ec2:StopInstances / ecs:UpdateService) を直接呼び出せます。Templated targets でカバーされないサービスに対しても、Lambda を経由せずにスケジュール実行が可能です。

新機能 4: DeleteAfterCompletion (one-time スケジュールの自動削除)
at() 式による one-time スケジュールに delete_after_completion = true を設定すると、実行完了後にスケジュールリソースが自動削除されます。一時的なタスクのためにスケジュールを大量作成する運用でも、リソースの残骸が蓄積しません。

3-3. 料金モデル比較

EventBridge Scheduler の料金体系は CWE Rule schedule と異なります。無料枠がない点が最大の違いであり、コスト見積もりで最もよく見落とされるポイントです。

料金軸EventBridge SchedulerCWE Rule schedule
課金単位呼び出し回数 (invocations)ルール評価回数 (events)
単価$1.00 / 1M invocations$1.00 / 1M events
無料枠なし1M events/月 無料

月 100 万回以下の呼び出しに収まる小規模な用途では、CWE Rule schedule の無料枠が使える分だけコストは低くなります。月 100 万回を超える規模になると料金差はなくなります。コスト見積もりの際は「Scheduler には無料枠がない」という前提で計算し、無料枠が存在すると誤解したまま本番投入することを避けてください。

3-4. 移行時の互換性

CWE Rule schedule から EventBridge Scheduler への移行で注意が必要な互換性ポイントをまとめます。

cron 式のフィールド数が違う: CWE の cron 式は cron(分 時 日 月 曜日) の 5 フィールドですが、Scheduler は cron(分 時 日 月 曜日 年) の 6 フィールドです。既存の cron 式をそのままコピーすると年フィールドが欠落してエラーになります。

IAM ロールの設定方法が変わる: CWE Rule schedule では aws_cloudwatch_event_targetrole_arn を省略できるケースがありましたが、Scheduler では role_arn が必須です。scheduler.amazonaws.com を Principal に持つ新規 IAM ロールを作成する必要があります。

API の名称変更: aws events put-rule / aws events put-targets の代わりに aws scheduler create-schedule を使用します。SDK・Terraform リソース名もすべて異なります (aws_cloudwatch_event_ruleaws_scheduler_schedule)。

3-5. CWE cron 式 vs Scheduler cron 式 読み替え表

CWE vs Scheduler 機能比較 — cron フィールド・タイムゾーン・ターゲット数・料金・付加機能

  • cron フィールド数: CWE = 5 フィールド (分 時 日 月 曜日) / Scheduler = 6 フィールド (分 時 日 月 曜日 年)
  • タイムゾーン: CWE = UTC 固定 / Scheduler = 300+ タイムゾーン指定可 (Asia/Tokyo 等)
  • 直接 Target 数: CWE = 数種類 / Scheduler = 30+ Templated + Universal (任意 AWS API)
  • 無料枠: CWE = 1M events/月 無料 / Scheduler = 無料枠なし ($1.00/1M invocations)
  • Flexible TW: CWE = なし / Scheduler = あり (1-1440 分)
  • Schedule Group: CWE = なし / Scheduler = あり (論理グループ化・タグ継承・一括削除)
  • DeleteAfterCompletion: CWE = なし / Scheduler = one-time スケジュールで自動削除可

主要な cron 式の読み替え例:

ユースケースCWE cron 式 (UTC)Scheduler cron 式 + timezone
毎朝 09:00 JSTcron(0 0 * * ? *)cron(0 9 * * ? *) + Asia/Tokyo
毎週月曜 08:00 JSTcron(0 23 ? * SUN *) (UTC 前日)cron(0 8 ? * MON *) + Asia/Tokyo
毎月 1 日 00:00 JSTcron(0 15 1 * ? *) (UTC 前日)cron(0 0 1 * ? *) + Asia/Tokyo
毎時 30 分cron(30 * * * ? *)cron(30 * * * ? *) + Asia/Tokyo
毎月末日 23:59 JSTLambda で月末判定が必要cron(59 23 L * ? *) + Asia/Tokyo

Scheduler の cron 式では曜日フィールドに SUN / MON / TUE / WED / THU / FRI / SAT の 3 文字略称と 0-7 の数値 (0 と 7 が日曜日) の両方を使えます。最も注意が必要なのは「年フィールドの追加」と「タイムゾーン設定の追加」の 2 点です。なお L (last day of month) は Scheduler cron で使用できますが、CWE では使用できません。

移行前後の cron 式比較確認スクリプト:

# 既存 CWE Rule の schedule_expression 一覧取得
aws events list-rules--region ap-northeast-1--query 'Rules[?ScheduleExpression!=`null`].{Name:Name,Expression:ScheduleExpression}'--output table

# Scheduler の schedule_expression 一覧取得 (移行後確認)
aws scheduler list-schedules--group-name production-group--region ap-northeast-1--query 'Schedules[*].{Name:Name,Expression:ScheduleExpression,Timezone:ScheduleExpressionTimezone}'--output table
</code>

これらのコマンドを並べて実行すると、CWE と Scheduler の式の対応を視覚的に確認できます。移行後は CWE 側の式が cron(0 0 * * ? *) (UTC) から Scheduler 側で cron(0 9 * * ? *) + Asia/Tokyo に正しく変換されているかを1件ずつ検証してください。


4. スケジュール作成ハンズオン (cron/rate/one-time + タイムゾーン)

fig03: スケジュール式3種類 (cron/rate/at) + タイムゾーン

4-1. スケジュール式の種類

QG-2: スケジュール式3種類 比較表

式の種類書式実行タイムゾーン代表ユースケース
cron 式cron(分 時 日 月 曜日 年)繰り返し指定可 (Asia/Tokyo 等)毎朝バッチ・月次集計
rate 式rate(N unit)
unit: minutes/hours/days
繰り返しUTC 固定 (指定不可)定期ポーリング・ハートビート
at 式at(yyyy-MM-dd'T'HH:mm:ss)1回のみUTC 固定 (指定不可)特定日時の1回限りバッチ
  • CWE cron 式は5フィールド。Scheduler は6フィールド目に年 (year)が追加される。
  • rate/at はタイムゾーン指定不可。JST 固定実行には必ず cron 式を使用する。
  • cron 式で「日」と「曜日」を同時指定する場合は、どちらか一方を ? にする (両方 * は ValidationException)。

cron 式フィールド一覧:

フィールド位置取り得る値特殊文字例
分 (Minutes)1番目0-59*, 0/5, 15,30
時 (Hours)2番目0-23*, 9-17, 0/6
日 (Day-of-month)3番目1-31*, L (月末), ?
月 (Month)4番目1-12 / JAN-DEC*, 1/3, JAN,APR
曜日 (Day-of-week)5番目1-7 / SUN-SAT*, MON-FRI, ?, 2#1
年 (Year)6番目1970-2199*, 2026,2027

rate 式の形式:

rate(5 minutes)# 5分ごと
rate(1 hour)# 1時間ごと (N=1 のときは単数形)
rate(7 days)# 7日ごと
</code>

rate(1 hours) のように数値 1 に複数形を使うと ValidationException になる。N=1 は単数形 (minute/hour/day)、N≥2 は複数形 (minutes/hours/days) を厳守すること。

at 式の形式:

at(2026-04-30T09:00:00)# UTC 2026年4月30日 09:00:00 に1回実行
at(2026-12-31T15:00:00)# JST 2027年1月1日 00:00 に実行したい場合の UTC 表記
</code>

at 式は UTC 固定。JST 09:00 に実行したい場合は at(2026-04-30T00:00:00) (UTC) と記述する。


4-2. タイムゾーン指定 (Asia/Tokyo)

EventBridge Scheduler では cron 式に限り タイムゾーンを指定できる。schedule_expression_timezone フィールドに IANA タイムゾーン名を指定する。

JST cron 変換例 (schedule_expression_timezone = "Asia/Tokyo")

要件Asia/Tokyo 指定の cron 式等価 UTC cron 式備考
毎朝 09:00 JSTcron(0 9 * * ? *)cron(0 0 * * ? *)JST = UTC+9 固定
月末日 18:00 JSTcron(0 18 L * ? *)cron(0 9 L * ? *)L = 各月最終日
平日 08:30 JSTcron(30 8 ? * MON-FRI *)cron(30 23 ? * SUN-THU *)UTC では前日またがり
毎週月曜 10:00 JSTcron(0 10 ? * MON *)cron(0 1 ? * MON *)Asia/Tokyo 指定で UTC 変換不要

JST はサマータイム非採用 (UTC+9 固定)。rate/at 式では schedule_expression_timezone を設定しても無視されるため、JST 固定実行には必ず cron 式を使用すること。


4-3. スケジュール作成 3点セット

Terraform HCL

resource "aws_scheduler_schedule_group" "main" {
  name = var.schedule_group_name
  tags = {
 Environment = "production"
 ManagedBy= "terraform"
  }
}

resource "aws_scheduler_schedule" "daily_batch" {
  name = var.schedule_name
  group_name = aws_scheduler_schedule_group.main.name

  schedule_expression = "cron(0 9 * * ? *)"
  schedule_expression_timezone = "Asia/Tokyo"

  flexible_time_window {
 mode = "OFF"
  }

  target {
 arn= aws_lambda_function.batch.arn
 role_arn = aws_iam_role.scheduler_execution.arn

 input = jsonencode({
action = "daily-batch"
batchSize = 1000
 })

 retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 3600
 }

 dead_letter_config {
arn = aws_sqs_queue.dlq.arn
 }
  }
}
</code>

AWS CLI

# スケジュールグループ作成
aws scheduler create-schedule-group \
  --name "production-group" \
  --tags '{"Environment":"production","ManagedBy":"cli"}'

# スケジュール作成 (JST cron + Lambda target)
aws scheduler create-schedule \
  --name "daily-batch-job" \
  --group-name "production-group" \
  --schedule-expression "cron(0 9 * * ? *)" \
  --schedule-expression-timezone "Asia/Tokyo" \
  --flexible-time-window '{"Mode":"OFF"}' \
  --target '{
 "Arn": "arn:aws:lambda:ap-northeast-1:123456789012:function:batch-function",
 "RoleArn": "arn:aws:iam::123456789012:role/scheduler-execution-role",
 "Input": "{\"action\":\"daily-batch\",\"batchSize\":1000}",
 "RetryPolicy": {
"MaximumRetryAttempts": 3,
"MaximumEventAgeInSeconds": 3600
 },
 "DeadLetterConfig": {
"Arn": "arn:aws:sqs:ap-northeast-1:123456789012:scheduler-dlq"
 }
  }'

# 作成確認
aws scheduler get-schedule \
  --name "daily-batch-job" \
  --group-name "production-group"
</code>

コンソール手順

  1. Amazon EventBridge → 左メニュー「Scheduler」→「スケジュール」→「スケジュールを作成」
  2. スケジュール名: daily-batch-job / グループ: production-group (事前作成 or「新規作成」)
  3. スケジュールパターン → 「繰り返しスケジュール」→「Cron ベースのスケジュール」
  4. Cron 式: 0 9 * * ? *
  5. タイムゾーン: Asia/Tokyo (UTC+9)
  6. フレキシブルタイムウィンドウ: オフ (固定実行の場合)
  7. 「次へ」→ターゲット選択 (§5 参照)

4-4. Schedule Group の設計

Schedule Group はスケジュールを論理的にグループ化する仕組みで、タグの一括適用・ライフサイクル管理・アクセス制御の粒度設定 に利用する。グループ作成自体は無料で、課金対象はスケジュールの呼び出し回数だ。

# 環境別グループ分け (推奨)
resource "aws_scheduler_schedule_group" "production" {
  name = "production"
  tags = { Environment = "production" }
}

resource "aws_scheduler_schedule_group" "staging" {
  name = "staging"
  tags = { Environment = "staging" }
}
</code>

グループ削除の注意点: グループを削除するとグループ内の全スケジュールが同時に削除される。terraform destroy 時は Terraform の依存関係順序により、スケジュールがグループより先に削除されることを確認すること。グループ名は変更不可のため、作成後の変更は destroy + 再作成が必要だ。


4-5. One-time スケジュール + AUTO_DELETE 設定

at 式を使った one-time スケジュールは実行後も残存する。action_after_completion = "DELETE" を設定すると、実行完了後にスケジュールが自動削除される。

resource "aws_scheduler_schedule" "one_time_migration" {
  name = "data-migration-2026-06-01"
  group_name = aws_scheduler_schedule_group.main.name

  schedule_expression = "at(2026-06-01T00:00:00)"
  schedule_expression_timezone = "UTC"

  # 実行後に自動削除
  action_after_completion = "DELETE"

  flexible_time_window {
 mode = "OFF"
  }

  target {
 arn= aws_lambda_function.migration.arn
 role_arn = aws_iam_role.scheduler_execution.arn
  }
}
</code>

action_after_completion = "DELETE"at 式のみ 有効。rate 式・cron 式に設定すると ValidationException になる。one-time 実行を cron 式で代替しないよう注意すること。


5. ターゲット設計 (Templated vs Universal)

fig04: Templated vs Universal targets 使い分けフロー

EventBridge Scheduler は 30種類以上のターゲット をサポートする。ターゲットは Templated targetsUniversal targets の2系統に分類され、IAM ロール設計・Retry 挙動・設定の複雑さが異なる。本章では2系統の使い分けを比較表で整理し、Lambda (Templated)・SQS (Templated)・SSM SendCommand (Universal) の3実例を通して実装方法を解説する。

5-1. Templated targets vs Universal targets の使い分け

QG-2: Templated vs Universal targets 比較表

項目Templated targetsUniversal targets
対応サービスLambda / Step Functions / SQS / SNS / ECS RunTask / CodeBuild / Kinesis / EventBridge / Glue / SageMaker / Batch など 14種+任意の AWS サービス (200+ API アクション)
IAM ロール設計コンソールで自動生成オプションあり手動設計必須 (sts:AssumeRole + サービス固有 Policy)
RetryPolicyサポート (最大 185 回)サポート (最大 185 回)
DLQ サポートあり (SQS ARN 指定)あり (SQS ARN 指定)
入力変換Input / InputPath / InputTransformerInput のみ (API パラメータを JSON で記述)
設定の複雑さ低 (テンプレート化済)高 (API アクション・パラメータ形式の理解が必要)
推奨場面対応サービス一覧に該当する場合は Templated を優先Templated 非対応サービスへの呼び出しが必要な場合のみ

判断フロー: 呼び出したいサービスが Templated 一覧にある → Templated を優先。一覧にない → Universal を使用。IAM 自動生成を活かせる Templated の方が設定ミスリスクが低い。


5-2. Lambda target 実例 (Templated)

Lambda は最も一般的な Templated target。target.arn に Lambda 関数 ARN を指定し、role_arn で実行 IAM ロールを渡す。IAM ロール設計 (scheduler.amazonaws.com の AssumeRole + lambda:InvokeFunction) の完全 HCL は §6 を参照。

resource "aws_scheduler_schedule" "lambda_target" {
  name = "lambda-daily-batch"
  group_name = aws_scheduler_schedule_group.main.name

  schedule_expression = "cron(0 9 * * ? *)"
  schedule_expression_timezone = "Asia/Tokyo"

  flexible_time_window {
 mode = "OFF"
  }

  target {
 arn= aws_lambda_function.batch.arn
 role_arn = aws_iam_role.scheduler_lambda_exec.arn

 input = jsonencode({
action = "daily-batch"
batchSize = 1000
 })

 retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 3600
 }

 dead_letter_config {
arn = aws_sqs_queue.dlq.arn
 }
  }
}
</code>
# CLI (Lambda Templated target)
aws scheduler create-schedule \
  --name "lambda-daily-batch" \
  --group-name "production-group" \
  --schedule-expression "cron(0 9 * * ? *)" \
  --schedule-expression-timezone "Asia/Tokyo" \
  --flexible-time-window '{"Mode":"OFF"}' \
  --target '{
 "Arn": "arn:aws:lambda:ap-northeast-1:123456789012:function:batch-function",
 "RoleArn": "arn:aws:iam::123456789012:role/scheduler-lambda-execution-role",
 "Input": "{\"action\":\"daily-batch\",\"batchSize\":1000}",
 "RetryPolicy": {
"MaximumRetryAttempts": 3,
"MaximumEventAgeInSeconds": 3600
 }
  }'
</code>

5-3. SQS target 実例 (Templated)

SQS への直接送信は Templated target でサポートされており、Lambda 経由を挟まずキューにメッセージを投入できる。FIFO キューを使う場合は sqs_parameters.message_group_id が必須だ。IAM は sqs:SendMessage Action のみ追加すればよい (§6 の IAM ロール設計を参照)。

resource "aws_scheduler_schedule" "sqs_target" {
  name = "sqs-hourly-trigger"
  group_name = aws_scheduler_schedule_group.main.name

  schedule_expression = "rate(1 hour)"

  flexible_time_window {
 mode = "OFF"
  }

  target {
 arn= aws_sqs_queue.target_queue.arn
 role_arn = aws_iam_role.scheduler_sqs_exec.arn

 input = jsonencode({
taskType = "hourly-sync"
 })

 # FIFO キューの場合のみ必要
 sqs_parameters {
message_group_id = "hourly-sync-group"
 }
  }
}
</code>

5-4. Universal target 実例 (SSM SendCommand)

SSM SendCommand は Templated target の一覧に含まれない。Universal target を使用し、arnarn:aws:scheduler:::aws-sdk:<service>:<apiAction> 形式を指定する。IAM には ssm:SendCommand + 対象リソース ARN を手動設計する。

resource "aws_scheduler_schedule" "universal_ssm" {
  name = "ssm-weekly-maintenance"
  group_name = aws_scheduler_schedule_group.main.name

  schedule_expression = "cron(0 2 ? * SUN *)"
  schedule_expression_timezone = "Asia/Tokyo"

  flexible_time_window {
 mode = "OFF"
  }

  target {
 # Universal target ARN 形式 (apiAction は camelCase 厳守)
 arn= "arn:aws:scheduler:::aws-sdk:ssm:sendCommand"
 role_arn = aws_iam_role.scheduler_universal_exec.arn

 # SSM SendCommand API のリクエストパラメータを JSON で指定
 input = jsonencode({
DocumentName = "AWS-RunShellScript"
InstanceIds  = ["i-0123456789abcdef0"]
Parameters = {
  commands = ["echo 'weekly maintenance' >> /var/log/maintenance.log"]
}
 })
  }
}
</code>
Universal target IAM 設計の注意点

  • Universal target では IAM ロールの自動生成が行われない。必ず手動で sts:AssumeRole ポリシーと対象 API の実行権限を設計すること。
  • arn:aws:scheduler:::aws-sdk:<service>:<apiAction>apiAction は camelCase (例: sendCommand)。PascalCase では InternalFailure になる。
  • Universal target の input は対象 API の リクエストボディそのもの (AWS SDK のパラメータ形式) であり、EventBridge Rule の InputTransformer とは形式が異なる。

5-5. fig04: Templated vs Universal 使い分けフロー

意思決定フロー (fig04 参照):

  1. 呼び出したいサービスを確認 → Templated targets 一覧 (Lambda / SQS / SNS / ECS / Step Functions / CodeBuild / Kinesis / SageMaker / Batch 他) に含まれるか?
  2. 含まれる → Templated を使用: IAM ロール設計が簡略化でき、Retry + DLQ も標準設定で動作する。
  3. 含まれない → Universal を使用: arn:aws:scheduler:::aws-sdk:<service>:<apiAction> 形式の ARN を指定し、IAM ロールを手動設計する。

5-6. 3点セット (ターゲット設計)

AWS CLI (ターゲット確認・更新)

# ターゲット設定を確認
aws scheduler get-schedule \
  --name "lambda-daily-batch" \
  --group-name "production-group" \
  --query 'Target'

# グループ内スケジュールのターゲット ARN 一覧
aws scheduler list-schedules \
  --group-name "production-group" \
  --query 'Schedules[*].{Name:Name,TargetArn:Target.Arn}'
</code>

コンソール手順 (ターゲット選択)

  1. スケジュール作成画面 → 「次へ」→「ターゲット」設定ページ
  2. ターゲットタイプ: 「テンプレート済みターゲット」 or 「ユニバーサルターゲット」を選択
  3. テンプレート済みの場合: サービス選択 → 関数/キュー/ステートマシン ARN を入力 → IAM ロール選択 (または自動生成)
  4. ユニバーサルの場合: AWS サービス → API アクション → Input JSON を入力 → IAM ロールは必ず手動作成
  5. 「次へ」→ 設定確認 → 「スケジュールを作成」

6. Terraform 実装 (aws_scheduler_schedule + schedule_group + IAM)

6-1. Terraform リソース概要

EventBridge Scheduler を Terraform で管理するには、主に以下の 3 リソースを組み合わせる。

リソース役割
aws_scheduler_schedule_groupスケジュールの論理グループ化コンテナ。環境別・ジョブ種別で分離管理
aws_scheduler_scheduleスケジュール本体。実行式・フレキシブルウィンドウ・ターゲット設定を包含
aws_iam_role / aws_iam_policyScheduler がターゲット (Lambda/SQS/Step Functions 等) を呼び出すための IAM ロール

これらは グループ → スケジュール → IAM ロール の順で依存関係があり、depends_on を使わずとも resource 参照で Terraform が依存順を自動解決する。Provider バージョンは hashicorp/aws ~> 5.0 (aws_scheduler_schedule は v4.67 以降でサポート) を推奨する。

6-2. aws_scheduler_schedule_group

schedule_group はスケジュールをまとめるコンテナであり、それ自体に料金は発生しないdefault という名前のグループが AWS アカウントごとに自動作成されており、グループを明示指定しない場合はここに収容される。

# schedule_group.tf
resource "aws_scheduler_schedule_group" "this" {
  name = var.schedule_group_name  # 例: "prod-batch"
  tags = var.tags
}
</code>

用途別分離の推奨パターン:

グループ名用途
prod-batch本番バッチジョブ一式
dev-batch開発環境スケジュール
cleanup-jobsリソースクリーンアップ系ジョブ

グループ内でスケジュール名は一意である必要があるが、別グループ間では同名が可能prod-batch/daily-reportdev-batch/daily-report は共存できる。削除時はグループ内のスケジュールをすべて先に削除してからグループを削除する。

6-3. aws_scheduler_schedule 完全 HCL

以下は Lambda をターゲットとした Templated target 構成の完全 HCL 実装例を示す。schedule_expressionflexible_time_windowtarget ブロックの 3 点が核心部分である。

QG-4: aws_scheduler_schedule + schedule_group + IAM 完全 HCL (Terraform 実装チェックポイント)

# variables.tf
variable "schedule_group_name" {
  description = "EventBridge Scheduler グループ名"
  type  = string
  default  = "prod-batch"
}

variable "schedule_name" {
  description = "スケジュール名 (グループ内一意)"
  type  = string
  default  = "daily-lambda-invocation"
}

variable "schedule_timezone" {
  description = "タイムゾーン (IANA 形式)"
  type  = string
  default  = "Asia/Tokyo"
}

variable "lambda_function_arn" {
  description = "起動対象 Lambda 関数 ARN"
  type  = string
}

variable "dlq_arn" {
  description = "デッドレターキュー SQS ARN"
  type  = string
}

variable "tags" {
  description = "共通タグ"
  type  = map(string)
  default  = {}
}

# locals.tf
locals {
  scheduler_assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Principal = { Service = "scheduler.amazonaws.com" }
  Action = "sts:AssumeRole"
}
 ]
  })
}

# schedule_group.tf
resource "aws_scheduler_schedule_group" "this" {
  name = var.schedule_group_name
  tags = var.tags
}

# schedule.tf
resource "aws_scheduler_schedule" "daily_lambda" {
  name = var.schedule_name
  group_name = aws_scheduler_schedule_group.this.name

  # JST 毎朝 09:00 (= UTC 00:00) に実行
  schedule_expression = "cron(0 0 * * ? *)"
  schedule_expression_timezone = var.schedule_timezone

  # ±15 分のフレキシブルウィンドウ (Lambda バーストを分散)
  flexible_time_window {
 mode = "FLEXIBLE"
 maximum_window_in_minutes = 15
  }

  state = "ENABLED"

  target {
 arn= var.lambda_function_arn
 role_arn = aws_iam_role.scheduler.arn

 # Lambda に渡す JSON ペイロード
 input = jsonencode({
source = "scheduler"
schedule  = var.schedule_name
timestamp = "<aws.scheduler.scheduled-time>"
 })

 retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 3600  # 1 時間以内のリトライのみ
 }

 dead_letter_config {
arn = var.dlq_arn
 }
  }
}
</code>

ポイント:
- schedule_expression_timezone は IANA 形式 (Asia/Tokyo) で指定する。省略すると UTC になる。
- flexible_time_windowmode = "FLEXIBLE" のときのみ maximum_window_in_minutes (1〜1440) が有効。
- retry_policymaximum_retry_attempts = 0 で即時 DLQ 送信、185 が上限値。
- はコンテキスト変数で Scheduler が自動展開する実行予定時刻。

6-4. IAM ロール設計 (完全 HCL)

Scheduler がターゲットを呼び出すには scheduler.amazonaws.comAssumeRole を許可し、ターゲットサービスへの実行権限を付与した IAM ロールが必要である。ターゲット種別ごとにポリシーが異なる。

Lambda ターゲット用 IAM

# iam_lambda.tf
resource "aws_iam_role" "scheduler" {
  name= "eventbridge-scheduler-role"
  assume_role_policy = local.scheduler_assume_role_policy
  tags= var.tags
}

resource "aws_iam_policy" "scheduler_lambda" {
  name = "eventbridge-scheduler-lambda-policy"
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= "lambda:InvokeFunction"
  Resource = var.lambda_function_arn
}
 ]
  })
}

resource "aws_iam_role_policy_attachment" "scheduler_lambda" {
  role = aws_iam_role.scheduler.name
  policy_arn = aws_iam_policy.scheduler_lambda.arn
}
</code>

SQS / Step Functions / Universal target 用 IAM

Templated target の SQS・Step Functions は Action が異なるだけで構造は同じである。Universal target (任意 AWS API 呼び出し) の場合は iam:PassRole が追加で必要になる。

# iam_targets.tf — SQS / Step Functions / ECS (Universal) の policy 例
resource "aws_iam_policy" "scheduler_sqs" {
  name= "eventbridge-scheduler-sqs-policy"
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{ Effect = "Allow", Action = "sqs:SendMessage", Resource = var.sqs_queue_arn }]
  })
}

resource "aws_iam_policy" "scheduler_sfn" {
  name= "eventbridge-scheduler-sfn-policy"
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{ Effect = "Allow", Action = "states:StartExecution", Resource = var.sfn_state_machine_arn }]
  })
}

# Universal target 例: ECS RunTask — iam:PassRole が追加で必要
resource "aws_iam_policy" "scheduler_ecs" {
  name= "eventbridge-scheduler-ecs-policy"
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect= "Allow"
Action= ["ecs:RunTask", "iam:PassRole"]
Resource = [var.ecs_task_definition_arn, var.ecs_task_execution_role_arn]
 }]
  })
}
</code>

IAM ロール設計の要点をまとめると以下の通りである。

ターゲット必要な ActionPassRole 要否
Lambda (Templated)lambda:InvokeFunction不要
SQS (Templated)sqs:SendMessage不要
Step Functions (Templated)states:StartExecution不要
ECS RunTask (Universal)ecs:RunTask + iam:PassRole必要
任意 API (Universal)サービス固有 Actionサービス依存

6-5. 落とし穴4選

落とし穴4選: Terraform で EventBridge Scheduler を設定する際の頻出ミス

① role_arn 未指定で invoke 失敗

target ブロックで role_arn を省略すると FAILED ステータスになり AccessDeniedException がログに記録される。Templated target であっても IAM ロールは必須である。Terraform plan 時点では検知されないため、apply 後の CloudWatch Logs で確認が必要。

② flexible_time_window で mode=OFF のとき maximum_window_in_minutes を指定するとエラー

# NG: mode=OFF なのに maximum_window_in_minutes を指定
flexible_time_window {
  mode = "OFF"
  maximum_window_in_minutes = 15  # ValidationException が発生する
}

# OK: mode=OFF のときは maximum_window_in_minutes を省略
flexible_time_window {
  mode = "OFF"
}
</code>

mode = "FLEXIBLE" のときのみ maximum_window_in_minutes (1〜1440) を指定する。

③ schedule_group を跨いだ schedule 名衝突はなし — ただしグループ内で名前は一意

同一グループ内での重複は ConflictException を引き起こす。一方、別グループ (例: prod-batchdev-batch) では同名スケジュールが共存可能。Terraform の state 管理においても group_name + name の組み合わせがリソースの識別子となる。

④ delete_after_completion (AUTO_DELETE) は at 式 one-time スケジュールにのみ有効

# OK: at 式 one-time スケジュールにのみ指定可
resource "aws_scheduler_schedule" "one_time" {
  schedule_expression = "at(2026-05-01T09:00:00)"
  # ...省略...
  # at 式のみ delete_after_completion が有効
}
</code>

cron 式や rate 式の定期スケジュールに delete_after_completion = true を設定しても効果がなく、API でも ValidationException になる場合がある。Terraform Provider v5 では属性が無効な組み合わせのときにエラーを返す。

6-6. 複数スケジュールのまとめ管理 (for_each / module 化)

本番環境では複数のスケジュールを管理するケースが多い。for_each を使ってスケジュール定義を変数化し、1 つのリソースブロックで全スケジュールを管理するパターンが保守性に優れる。

# variables.tf
variable "schedules" {
  description = "スケジュール定義マップ"
  type = map(object({
 expression = string
 timezone= string
 target_lambda_arn= string
 dlq_arn = string
 flexible_window_min = optional(number, 0)
  }))
  default = {}
}

# schedule.tf — for_each による一括管理
resource "aws_scheduler_schedule" "batch" {
  for_each = var.schedules

  name = each.key
  group_name = aws_scheduler_schedule_group.this.name

  schedule_expression = each.value.expression
  schedule_expression_timezone = each.value.timezone

  flexible_time_window {
 mode = each.value.flexible_window_min > 0 ? "FLEXIBLE" : "OFF"
 maximum_window_in_minutes = each.value.flexible_window_min > 0 ? each.value.flexible_window_min : null
  }

  state = "ENABLED"

  target {
 arn= each.value.target_lambda_arn
 role_arn = aws_iam_role.scheduler.arn

 retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 3600
 }

 dead_letter_config {
arn = each.value.dlq_arn
 }
  }
}
</code>

terraform.tfvars では schedules マップに daily-reporthourly-sync などのエントリを列挙するだけでよい。flexible_window_min = 0 のとき HCL 側が mode = "OFF" に自動切り替えする。module 化する場合は schedules マップごと module に渡し、IAM ロールは module 外部で一元管理して role_arn を input として渡すパターンが保守性に優れる。


7. 信頼性設計 (Retry + DLQ + Flexible Time Window)

fig05: Retry+DLQ+Flexible Time Window タイミング図

EventBridge Scheduler のデフォルト動作はターゲット呼び出し失敗時に「即廃棄」である。RetryPolicy と DLQ (Dead Letter Queue) を組み合わせることで失敗イベントを保全・再処理できる本番水準の信頼性設計が整う。Flexible Time Window はこれと組み合わせて Lambda コールドスタートのバーストを防ぐ実運用価値の高い機能だ。

7-1. RetryPolicy 設計

RetryPolicy は Scheduler がターゲット呼び出しに失敗した際の自動リトライ動作を制御する。省略するとデフォルト 0 回、つまりリトライなしとなりイベントが無音消失する。

RetryPolicy パラメータ一覧

パラメータ値範囲デフォルト推奨値
maximum_retry_attemptsinteger0〜1850 (リトライなし)3〜10
maximum_event_age_in_secondsinteger60〜8640086400 (24h)SLA に合わせる
  • リトライ間隔は指数バックオフ + AWS マネージドのジッター。手動設定は不可。
  • maximum_event_age_in_seconds はイベント生成から廃棄までの最大許容時間。バッチなら 86400 (24h)、リアルタイム処理なら 600 (10分) 以下が目安。
  • リトライ上限超過後の挙動はデフォルトで廃棄。DLQ を設定すると SQS へ配送される。
QG-3: RetryPolicy + DLQ 設計の必須チェックポイント

  • maximum_retry_attempts 省略 = 失敗即廃棄。デフォルト 0 のままでは Lambda/SFN 側エラーが無音で消える。本番で最も多い設定漏れ。必ず 1 以上を明示する。
  • maximum_event_age_in_seconds は SLA に合わせる。日次バッチなら 86400 (24h)、リアルタイム処理なら 600 (10分) 以下に絞りイベントの陳腐化を防ぐ。
  • DLQ は RetryPolicy とセット運用。リトライ上限超過後に SQS DLQ へ配送することで失敗イベントを監査・再処理できる。DLQ なしではリトライ超過後もイベントが消失する。
  • SQS DLQ に sqs:SendMessage を許可する。キューポリシーで scheduler.amazonaws.com からの SendMessage を許可しないと DLQ 配送が失敗し、メッセージが無音消失する。aws:SourceArn 条件で特定スケジュールに限定するとより安全。

Terraform HCL — retry_policy ブロック

resource "aws_scheduler_schedule" "nightly_batch" {
  name = "nightly-batch"
  group_name = aws_scheduler_schedule_group.main.name

  flexible_time_window {
 mode = "FLEXIBLE"
 maximum_window_in_minutes = 15
  }

  schedule_expression = "cron(0 0 * * ? *)"
  schedule_expression_timezone = "Asia/Tokyo"

  target {
 arn= aws_lambda_function.batch.arn
 role_arn = aws_iam_role.scheduler_exec.arn

 retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 86400
 }

 dead_letter_config {
arn = aws_sqs_queue.dlq.arn
 }
  }
}
</code>

AWS CLI — RetryPolicy 付きスケジュール更新

aws scheduler update-schedule \
  --name nightly-batch \
  --group-name default \
  --schedule-expression "cron(0 0 * * ? *)" \
  --schedule-expression-timezone "Asia/Tokyo" \
  --flexible-time-window '{"Mode":"FLEXIBLE","MaximumWindowInMinutes":15}' \
  --target '{
 "Arn": "arn:aws:lambda:ap-northeast-1:123456789012:function:batch",
 "RoleArn": "arn:aws:iam::123456789012:role/SchedulerRole",
 "RetryPolicy": {
"MaximumRetryAttempts": 3,
"MaximumEventAgeInSeconds": 86400
 },
 "DeadLetterConfig": {
"Arn": "arn:aws:sqs:ap-northeast-1:123456789012:scheduler-dlq"
 }
  }'
</code>

7-2. Dead Letter Queue (SQS DLQ) 設定

DLQ は RetryPolicy の上限を超えた失敗イベントを保管するキューである。SQS Standard Queue を使用する (FIFO は非対応)。DLQ がないと RetryPolicy 超過後のイベントは廃棄され、失敗の証跡が残らない。

Terraform HCL — SQS DLQ + キューポリシー

resource "aws_sqs_queue" "dlq" {
  name  = "scheduler-dlq"
  message_retention_seconds  = 1209600
  visibility_timeout_seconds = 300

  tags = {
 Purpose = "EventBridgeSchedulerDLQ"
  }
}

resource "aws_sqs_queue_policy" "dlq_policy" {
  queue_url = aws_sqs_queue.dlq.id

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Principal = { Service = "scheduler.amazonaws.com" }
  Action = "sqs:SendMessage"
  Resource  = aws_sqs_queue.dlq.arn
  Condition = {
 ArnEquals = {
"aws:SourceArn" = aws_scheduler_schedule.nightly_batch.arn
 }
  }
}
 ]
  })
}
</code>

DLQ に届いたメッセージには x-amz-scheduler-failed-reason 属性が付与され、失敗原因 (AccessDeniedException / ThrottlingException 等) を判別できる。message_retention_seconds = 1209600 (14日) は調査時間を確保するための推奨値。

DLQ 受信後の処理パターン

パターン概要適用シーン
Lambda re-processorDLQ を Event Source Mapping で Lambda に接続し自動再試行冪等な処理・自動回復が可能
CloudWatch Alarm 通知DLQ 到達をトリガーに SNS → Slack/PagerDuty人手確認が必要な処理
手動確認 + 再スケジュールCLI or コンソールでメッセージを確認後に再実行調査・ワンオフ対応

AWS CLI — DLQ メッセージ確認

aws sqs receive-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/123456789012/scheduler-dlq \
  --attribute-names All \
  --message-attribute-names All \
  --max-number-of-messages 10
</code>

7-3. Flexible Time Window 実運用価値

Flexible Time Window は、スケジュール時刻から指定分数以内のランダムな時刻に発火を分散させる機能である。Lambda コールドスタートのバースト防止とスループット均一化に効果がある。

設定パラメータ

flexible_time_window {
  mode = "FLEXIBLE"
  maximum_window_in_minutes = 15
}
</code>
  • mode = "OFF" (デフォルト): 指定時刻通りに発火。maximum_window_in_minutes は不要。
  • mode = "FLEXIBLE": 指定時刻から最大 maximum_window_in_minutes 分以内のランダムな時刻に発火。範囲: 1〜1440 分。
  • Scheduler が分散タイミングを自動決定するため、同一グループ内の複数スケジュールも互いに重ならないよう調整される。

Lambda コールドスタートバースト防止 — 比較例

毎時 0 分に 100 件の Lambda 関数を同時起動するケース (東京リージョンのバースト上限: 初期 500 同時実行):

設定00:00 の同時実行スパイクスロットリングコールドスタート集中
FTW なし (mode=OFF)100 同時実行高負荷時に発生○ 集中する
FTW = 15 分最大 7〜8 同時実行/分未発生× 分散される

複数のスケジュールが同一時刻に重なるシステムほど Flexible Time Window の効果が大きい。

Flexible Time Window 使い分け早見表

ユースケース推奨 mode最大分数目安理由
厳密時刻必須 (決済・外部連携 SLA)OFF遅延が業務影響を持つ
日次・週次バッチ集計FLEXIBLE15〜60 分完了さえすれば時刻は問わない
Lambda 同時多発 (50 並列以上)FLEXIBLE10〜30 分コールドスタートバースト防止が目的
SQS 送信 (順序依存なし)FLEXIBLE30〜60 分消費者側への負荷を均一化できる
one-time (at 式)OFF 推奨FLEXIBLE でも動作するが意図が不明確になる

7-4. 完全設計パターン: 失敗 3 回 → DLQ → CW Alarm → SNS

本番で推奨する多層防御パターン。RetryPolicy + DLQ + CloudWatch Alarm + SNS を Terraform で一括定義する。

処理フロー

  1. Scheduler が Lambda を呼び出す → Lambda がエラーを返す
  2. RetryPolicy: 指数バックオフで最大 3 回リトライ
  3. 3 回すべて失敗 → SQS DLQ へ自動配送
  4. CloudWatch Alarm: ApproximateNumberOfMessagesVisible > 0 を監視
  5. Alarm → SNS Topic → Slack / PagerDuty / メール 通知

Terraform HCL — SNS + CloudWatch Alarm 統合

resource "aws_sns_topic" "scheduler_alert" {
  name = "scheduler-dlq-alert"
}

resource "aws_sns_topic_subscription" "email" {
  topic_arn = aws_sns_topic.scheduler_alert.arn
  protocol  = "email"
  endpoint  = var.alert_email
}

resource "aws_cloudwatch_metric_alarm" "dlq_not_empty" {
  alarm_name = "scheduler-dlq-not-empty"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "ApproximateNumberOfMessagesVisible"
  namespace  = "AWS/SQS"
  period  = 60
  statistic  = "Sum"
  threshold  = 0
  alarm_description= "EventBridge Scheduler DLQ にメッセージが到達"
  treat_missing_data  = "notBreaching"

  dimensions = {
 QueueName = aws_sqs_queue.dlq.name
  }

  alarm_actions = [aws_sns_topic.scheduler_alert.arn]
  ok_actions = [aws_sns_topic.scheduler_alert.arn]
}
</code>

AWS CLI — DLQ Alarm 作成

aws cloudwatch put-metric-alarm \
  --alarm-name "scheduler-dlq-not-empty" \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 1 \
  --metric-name ApproximateNumberOfMessagesVisible \
  --namespace AWS/SQS \
  --period 60 \
  --statistic Sum \
  --threshold 0 \
  --alarm-description "EventBridge Scheduler DLQ にメッセージが到達" \
  --dimensions Name=QueueName,Value=scheduler-dlq \
  --alarm-actions arn:aws:sns:ap-northeast-1:123456789012:scheduler-dlq-alert \
  --ok-actions arn:aws:sns:ap-northeast-1:123456789012:scheduler-dlq-alert \
  --treat-missing-data notBreaching
</code>

このパターンで、スケジュール実行失敗が 3 回リトライを経ても解消しない場合に確実にアラートが上がり、DLQ に保管されたイベントを後から手動または自動で再処理できる体制が整う。


8. 監査 + 運用 (Metrics 5種・アラート・CloudTrail・コスト)

EventBridge Scheduler の本番運用には「呼び出し成否の可視化」「変更操作の証跡」「コスト管理」の 3 点が欠かせない。CloudWatch Metrics 5 種とアラート、CloudTrail 証跡、コスト見積もりの 4 点セットで運用監視の死角をなくす。

8-1. CloudWatch Metrics 5 種

Scheduler は専用の CloudWatch メトリクスを提供する。名前空間は AWS/Scheduler

CloudWatch Metrics 5種 早見表

メトリクス名意味単位監視目的
InvocationAttemptCountScheduler がターゲット呼び出しを試みた総数 (リトライ含む)Count実行量把握・コスト見積もり
TargetErrorCountターゲット側 (Lambda / SFN 等) でエラーが発生した数Countターゲット側の障害検知
TargetErrorThrottledCountターゲット側スロットリング (ThrottlingException) によるエラー数CountLambda 同時実行数上限違反の検知
InvocationThrottleCountScheduler 側でスロットリングが発生した呼び出し数CountScheduler クォータ超過の検知
InvocationDroppedCountmaximum_retry_attempts 超過または event_age 超過で廃棄された数CountDLQ 配送有無・RetryPolicy 設定適切性の確認

メトリクスは ScheduleNameScheduleGroupName ディメンションで絞り込める。ディメンション省略時はアカウント全体の合計値になる。

AWS CLI — TargetErrorCount 確認

aws cloudwatch get-metric-statistics \
  --namespace AWS/Scheduler \
  --metric-name TargetErrorCount \
  --dimensions Name=ScheduleName,Value=nightly-batch \
  Name=ScheduleGroupName,Value=prod-batch \
  --start-time 2026-04-25T00:00:00Z \
  --end-time 2026-04-25T23:59:59Z \
  --period 3600 \
  --statistics Sum
</code>

8-2. アラート設計

TargetErrorCountInvocationDroppedCount は 0 より大きければ即アラートを上げる。TargetErrorThrottledCount は Lambda の同時実行上限を示すため、スパイク時に Reserved Concurrency の見直しトリガーとなる。

Terraform HCL — TargetErrorCount + InvocationDroppedCount アラート

resource "aws_cloudwatch_metric_alarm" "target_error" {
  alarm_name = "scheduler-target-error"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "TargetErrorCount"
  namespace  = "AWS/Scheduler"
  period  = 300
  statistic  = "Sum"
  threshold  = 0
  alarm_description= "Scheduler ターゲット呼び出しエラー発生"
  treat_missing_data  = "notBreaching"

  dimensions = {
 ScheduleGroupName = aws_scheduler_schedule_group.main.name
  }

  alarm_actions = [aws_sns_topic.scheduler_alert.arn]
}

resource "aws_cloudwatch_metric_alarm" "invocation_dropped" {
  alarm_name = "scheduler-invocation-dropped"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "InvocationDroppedCount"
  namespace  = "AWS/Scheduler"
  period  = 300
  statistic  = "Sum"
  threshold  = 0
  alarm_description= "Scheduler リトライ上限超過によるイベント廃棄が発生"
  treat_missing_data  = "notBreaching"

  dimensions = {
 ScheduleGroupName = aws_scheduler_schedule_group.main.name
  }

  alarm_actions = [aws_sns_topic.scheduler_alert.arn]
}
</code>

AWS CLI — TargetErrorCount アラート作成

aws cloudwatch put-metric-alarm \
  --alarm-name "scheduler-target-error" \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 1 \
  --metric-name TargetErrorCount \
  --namespace AWS/Scheduler \
  --period 300 \
  --statistic Sum \
  --threshold 0 \
  --alarm-description "Scheduler ターゲット呼び出しエラー発生" \
  --dimensions Name=ScheduleGroupName,Value=prod-batch \
  --alarm-actions arn:aws:sns:ap-northeast-1:123456789012:scheduler-dlq-alert \
  --treat-missing-data notBreaching
</code>

8-3. CloudTrail 証跡 (Scheduler API 操作記録)

EventBridge Scheduler の操作は CloudTrail に自動記録される。監査・コンプライアンス対応に活用できる。追加設定なしで CloudTrail 有効アカウントであれば S3 バケットに証跡が保管される。

記録される主要 API アクション

API アクション説明重要度
CreateSchedule新規スケジュール作成
UpdateScheduleスケジュール設定変更
DeleteScheduleスケジュール削除
CreateScheduleGroupスケジュールグループ作成
DeleteScheduleGroupスケジュールグループ削除

CloudWatch Logs Insights — 特定スケジュールの操作履歴抽出

fields @timestamp, eventName, userIdentity.arn, requestParameters.name
| filter eventSource = "scheduler.amazonaws.com"
| filter requestParameters.name = "nightly-batch"
| sort @timestamp desc
| limit 50
</code>

AWS CLI — CloudTrail イベント確認

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventSource,AttributeValue=scheduler.amazonaws.com \
  --start-time 2026-04-25T00:00:00Z \
  --end-time 2026-04-25T23:59:59Z \
  --query 'Events[*].{Time:EventTime,Name:EventName,User:Username}' \
  --output table
</code>

8-4. コスト管理

EventBridge Scheduler の料金は無料枠なしの従量課金である。

料金体系

項目単価
スケジュール呼び出し$1.00 / 1,000,000 invocations
リトライ呼び出し数にカウント (通常呼び出し + リトライ分の合計)
DLQ 配送SQS 送受信料金 (別途)

月次コスト見積もり式

月次コスト ($) = 総呼び出し数 / 1,000,000 × $1.00

例1: 毎分実行 × 1 スケジュール
  = 60 × 24 × 30 = 43,200 回/月 → $0.04/月

例2: 毎分実行 × 10 スケジュール、リトライ平均 0.1 回/回
  = 43,200 × 10 × 1.1 = 475,200 回/月 → $0.48/月
</code>

コスト削減のポイント

  • 不要なスケジュールは DISABLED 状態に切り替えると呼び出しが停止し課金も止まる。
  • Schedule Group でタグ管理することで Cost Explorer のコスト配分タグが使えるようになる。
  • rate(1 hour) で設定しているが rate(1 day) で足りるユースケースは見直しが有効。
  • InvocationAttemptCount の実績値で定期的にコストを試算し、想定より多い場合は過剰なスケジュールを整理する。

AWS CLI — スケジュール一覧でコスト確認起点を把握

aws scheduler list-schedules \
  --group-name prod-batch \
  --query 'Schedules[*].{Name:Name,State:State,Expression:ScheduleExpression}' \
  --output table
</code>

8-5. 運用チェックリスト

EventBridge Scheduler 本番運用チェックリスト

  • RetryPolicy 設定確認: maximum_retry_attempts ≥ 1。デフォルト 0 のままでは失敗即廃棄。
  • DLQ ARN 設定確認: dead_letter_config.arn に SQS ARN (URL ではなく ARN 形式) が設定されている。
  • DLQ キューポリシー確認: scheduler.amazonaws.com からの sqs:SendMessage が許可されている。
  • DLQ CloudWatch Alarm: ApproximateNumberOfMessagesVisible > 0 でアラート設定済み。
  • TargetErrorCount Alarm: ターゲット側エラー検知用アラートが設定されている。
  • InvocationDroppedCount Alarm: リトライ上限超過によるイベント廃棄を検知するアラートが設定されている。
  • schedule_expression_timezone 設定: cron 式のタイムゾーンが明示されている ("Asia/Tokyo" など)。
  • Flexible Time Window 適用可否の確認: 厳密時刻不要なスケジュールは mode=FLEXIBLE を採用し負荷分散。
  • IAM ロール最小権限確認: SchedulerRole の trust policy に scheduler.amazonaws.com が含まれ、target 側 action のみ許可されている。
  • CloudTrail 有効化確認: scheduler.amazonaws.com の API 操作が証跡に記録されている。
  • 月次コスト試算: InvocationAttemptCount の実績値でコストを試算し予算超過リスクを評価している。
  • DISABLED スケジュール棚卸し: 長期 DISABLED のスケジュールを定期的に削除し残骸が蓄積しないよう管理する。

9. まとめ + よくある落とし穴10選 + 次回予告

9-1. まとめ ― 本記事で到達した6軸

本記事は EventBridge Scheduler を本番投入するために必要な6つの軸を一通り押さえた。

到達点
タイムゾーンschedule_expression_timezone = "Asia/Tokyo" で JST cron を確定。UTC 解釈ミスを根絶できる。
Flexible Time Windowmode = "FLEXIBLE" + maximum_window_in_minutes で Lambda cold start バーストを時間帯分散。
Templated vs Universal対応サービスは Templated (IAM 自動) 優先。未対応 API は Universal + 手動 IAM 設計。
Retry + DLQmaximum_retry_attempts = 3 + DLQ SQS ARN で失敗イベントを無音消失させない。
Terraform 実装aws_scheduler_schedule + aws_scheduler_schedule_group + IAM role/policy の完全 HCL。
監視CloudWatch Metrics 5種 + アラート + CloudTrail 証跡 + コスト管理の4点セット。

CWE Rule schedule では不可能だったタイムゾーン指定・30+ ターゲット・Flexible Time Window を活用することで、より堅牢なジョブスケジューリング基盤が構築できる。


9-2. チートシート

EventBridge Scheduler チートシート

Terraform skeleton (aws_scheduler_schedule の最小構成)

resource "aws_scheduler_schedule_group" "main" {
  name = "prod-batch"
}

resource "aws_scheduler_schedule" "daily_batch" {
  name = "daily-report"
  group_name = aws_scheduler_schedule_group.main.name

  flexible_time_window {
 mode = "FLEXIBLE"
 maximum_window_in_minutes = 15
  }

  schedule_expression = "cron(0 9 * * ? *)"
  schedule_expression_timezone = "Asia/Tokyo"

  target {
 arn= aws_lambda_function.target.arn
 role_arn = aws_iam_role.scheduler_exec.arn

 retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 3600
 }

 dead_letter_config {
arn = aws_sqs_queue.dlq.arn
 }
  }
}
</code>

CLI one-liner (aws scheduler create-schedule)

aws scheduler create-schedule \
  --name "daily-report" \
  --group-name "prod-batch" \
  --schedule-expression "cron(0 9 * * ? *)" \
  --schedule-expression-timezone "Asia/Tokyo" \
  --flexible-time-window '{"Mode":"FLEXIBLE","MaximumWindowInMinutes":15}' \
  --target '{
 "Arn": "<LAMBDA_ARN>",
 "RoleArn": "<ROLE_ARN>",
 "RetryPolicy": {"MaximumRetryAttempts":3,"MaximumEventAgeInSeconds":3600},
 "DeadLetterConfig": {"Arn":"<SQS_ARN>"}
  }' \
  --region ap-northeast-1
</code>

9-3. よくある落とし穴10選

fig06: よくある落とし穴ヒートマップ (検出難易度 × 影響度)

⚠️ よくある落とし穴10選 ― 本番投入前チェックリスト

1. AccessDeniedException: role_arn 欠落または iam:PassRole 不足
target ブロックの role_arn を省略、または実行ロールに iam:PassRole が付与されていないと実行時に即失敗する。trust relationship には必ず scheduler.amazonaws.com を含める。

2. ValidationException: cron 式の構文誤り (6フィールド/年フィールド)
EventBridge Scheduler は CWE より1フィールド多い6フィールド構文を使用する。旧 cron 式をそのままコピーすると ValidationException が発生する。年フィールド (? または *) を忘れずに追加する。

3. タイムゾーン未指定で UTC 解釈 (Asia/Tokyo 忘れ)
schedule_expression_timezone を省略すると UTC 動作となる。JST 09:00 を意図したのに UTC 09:00 = JST 18:00 に実行される典型ミス。必ず "Asia/Tokyo" を明示する。

4. Flexible Time Window と at 式の同時指定 (at 式は FLEXIBLE 不可)
at(yyyy-mm-ddTHH:MM:SS) 形式 (one-time 実行) では mode = "FLEXIBLE" が使えない。この組み合わせは apply 時に ValidationException が発生する。one-time 実行は mode = "OFF" を使用する。

5. Templated target の IAM 自動生成が効かない (Universal と誤設定)
Arn に Lambda ARN を指定しても Input フォーマットを Universal 形式で書くと IAM が自動生成されない。Templated target は所定の Arn + Input 形式で記述する必要がある。

6. Universal target で sts:AssumeRole 漏れ
Universal targets は Scheduler が scheduler.amazonaws.com として AssumeRole するため、ターゲットロールの trust policy に sts:AssumeRole の手動追加が必要。省略すると呼び出しが失敗する。

7. dead_letter_config に SQS Queue URL を使用 (ARN が正解)
dead_letter_config.arn には SQS キューの ARN を指定する。URL (https://sqs...) を誤って入力すると Terraform apply は通るが DLQ への配信が失敗する。ARN 形式 (arn:aws:sqs:...) を必ず使用する。

8. schedule_group 削除時に schedule が残骸として残る (先に schedule 削除)
aws_scheduler_schedule_group を destroy する際、グループ内に schedule が残っていると削除に失敗する。Terraform の depends_on を正しく設定し、先に schedule を削除してからグループを削除する。

9. 料金見積もり漏れ: 1M invocations = $1.00 / 無料枠なし
EventBridge Scheduler に無料枠はない。毎分実行のスケジュールを10本立てると月約 432,000 invocations ≈ $0.43 となる。少額でも設計初期に見積もっておかないと後から予算超過が発生する。

10. RetryPolicy 未設定で失敗イベント無音消失
retry_policy ブロックを省略すると maximum_retry_attempts = 0 扱いとなり、ターゲット呼び出しが1回失敗するとイベントは静かに消える。必ず maximum_retry_attempts ≥ 1 + DLQ をセットで設定する。


9-4. 関連記事

カテゴリ記事ポイント
EventBridge 系EventBridge Pipes 完全ガイドScheduler と Pipes の使い分けを整理
EventBridge 系EventBridge VPC Lattice + Fargate 連携サービス間イベント駆動パターンの実装例
Terraform 系Terraform AWS Provider 5.x 主要変更点aws_scheduler_* リソースの最新仕様を確認
Terraform 系Terraform Remote State と S3 BackendScheduler HCL を含む state 管理ベストプラクティス

9-5. 次回予告

TODO: 次回記事の内容は校閲後に記入。


9-6. この記事が役に立ったら

AWS 公式 EventBridge Scheduler User Guide を読む

Terraform Registry: aws_scheduler_schedule を確認する

EventBridge VPC Lattice 連携の記事を読む