Updraft ExtensionsPreview
Updraft ScheduleScheduling StableSince v1.0

Updraft::Schedule::CronExpression

Builds a cron expression from a described intent and a named time zone, so a template can say “03:00 in Toronto, weekdays only” instead of a UTC field string that silently drifts by an hour twice a year.

Required properties 2 of 12, plus 6 conditional
Ref returns The expressioncron(0 7 ? * MON-FRI *)
Fn::GetAtt 7 attributes
Replacement risk None no property forces replacement

Minimal template

Every required property, nothing else
Resources:
  NightlyBackup:
    Type: Updraft::Schedule::CronExpression
    Properties:
      Behavior: Daily
      Time: "03:00"
      TimeZone: America/Toronto
Read the result with Fn::GetAtt on Expression. It is already wrapped in cron(...), so it drops straight into an EventBridge ScheduleExpression.

Overview #

Cron expressions describe when badly. They have no time zone, three incompatible dialects across AWS services, and a day-of-week field whose numbering differs between them. The gap between “run at 3am Toronto time on weekdays” and cron(0 7 ? * MON-FRI *) is small enough to cross by hand and large enough to get wrong.

This resource takes the intent and emits the expression, along with a plain-English description you can check in review.

What it generates #

Intent

Resources:
  NightlyBackup:
    Type: Updraft::Schedule::CronExpression
    Properties:
      Behavior: Weekly
      DaysOfWeek: [MON, TUE, WED, THU, FRI]
      Time: "03:00"
      TimeZone: America/Toronto
      DaylightSaving: PinStandard
      Jitter: 10

Result

Expression:  "cron(7 8 ? * MON-FRI *)"
UtcTime:     "08:07"
UtcOffsetMinutes: -300
ObservesDaylightSaving: true
LocalDescription: >-
  Every weekday at 03:07 America/Toronto
  (08:07 UTC, pinned to standard time)
NextOccurrences:
  - "2026-08-10T08:07:00Z"
  - "2026-08-11T08:07:00Z"
  - "2026-08-12T08:07:00Z"

The seven minutes of jitter come from hashing the stack ID. The same stack lands on the same minute every deployment; a different stack lands somewhere else.

Using it with EventBridge #

Expression is already wrapped in cron(...) for the EventBridge dialect, so it drops straight in:

Resources:
  NightlyBackup:
    Type: Updraft::Schedule::CronExpression
    Properties:
      Behavior: Daily
      Time: "03:00"
      TimeZone: UTC

  BackupRule:
    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: !GetAtt NightlyBackup.Expression
      State: ENABLED
      Targets:
        - Id: backup-lambda
          Arn: !GetAtt BackupFunction.Arn

Outputs:
  BackupSchedule:
    Description: Human-readable form of the backup schedule, for review.
    Value: !GetAtt NightlyBackup.LocalDescription

Properties

Expand a row for the full reference; nested types open in place

12 top-level properties

  • Behavior StringThe shape of the schedule, which determines which other properties apply. Required No interruption — updates in place

    Each behavior consumes a different subset of the remaining properties. A property that does not apply to the chosen behavior is rejected rather than ignored, so a misplaced DaysOfWeek fails the stack instead of quietly doing nothing.

    BehaviorUses
    DailyTime
    WeeklyTime, DaysOfWeek
    MonthlyTime, DayOfMonth
    EveryNHoursInterval, Time (as the offset within the hour)
    EveryNMinutesInterval
    BusinessHoursTime, EndTime, DaysOfWeek, Interval
    CustomExpression
    Type
    String
    Required
    Yes
    Update behaviour
    No interruption

    Allowed values

    • Daily
    • Weekly
    • Monthly
    • EveryNHours
    • EveryNMinutes
    • BusinessHours
    • Custom

    Example values

    • Daily
  • TimeZone StringIANA time zone name that Time is expressed in. Required No interruption — updates in place
    The zone is resolved against the IANA database at stack-operation time and converted to a fixed UTC expression.
    Type
    String
    Required
    Yes
    Update behaviour
    No interruption
    Pattern
    ^([A-Za-z_]+/[A-Za-z_+\-0-9/]+|UTC)$ IANA zone name. Fixed offsets such as `-05:00` are deliberately not accepted.

    Example values

    • America/Toronto
    • Europe/London
    • UTC
  • DayOfMonth StringDay of the month, or L for the last day. Conditional No interruption — updates in place
    Days 29 to 31 simply do not fire in months that lack them; EventBridge does not clamp. Use L for “end of month” rather than 31.
    Type
    String
    Required
    ConditionalRequired for Monthly.
    Update behaviour
    No interruption
    Pattern
    ^(L|[1-9]|[12]\d|3[01])$

    Example values

    • 1
    • 15
    • L
  • DaysOfWeek Array of StringDays the schedule fires on. Conditional No interruption — updates in place

    Days the schedule fires on.

    Type
    Array of String
    Required
    ConditionalRequired for Weekly. Optional for BusinessHours, where it defaults to Monday–Friday.
    Update behaviour
    No interruption
    Items
    1 – 7 must be unique

    Allowed values

    • MON
    • TUE
    • WED
    • THU
    • FRI
    • SAT
    • SUN

    Example values

    • ["MON","TUE","WED","THU","FRI"]
  • EndTime StringLocal end of the window for BusinessHours, exclusive. Conditional No interruption — updates in place

    Local end of the window for BusinessHours, exclusive.

    Type
    String
    Required
    ConditionalRequired for BusinessHours.
    Update behaviour
    No interruption
    Pattern
    ^([01]\d|2[0-3]):([0-5]\d)$

    Example values

    • 17:00
  • Expression StringA raw six-field cron expression, validated and passed through. Conditional No interruption — updates in place
    Escape hatch for schedules the other behaviors cannot express. The expression is still parsed and validated, and NextOccurrences is still computed, so Custom does not mean unchecked.
    Type
    String
    Required
    ConditionalRequired for Custom.
    Update behaviour
    No interruption

    Example values

    • 0 7 ? * MON-FRI *
  • Interval IntegerRepeat interval, in the unit implied by Behavior. Conditional No interruption — updates in place
    For EveryNHours the interval must divide 24 evenly, otherwise the schedule skips at midnight. 5 is rejected for that reason; 6 and 8 are fine.
    Type
    Integer
    Required
    ConditionalRequired for EveryNHours, EveryNMinutes, and BusinessHours.
    Update behaviour
    No interruption
    Range
    1 – 23

    Example values

    • 6
  • Time StringLocal time of day in 24-hour HH:MM. Conditional No interruption — updates in place
    For EveryNHours this is interpreted as the offset within each hour, so Time: "00:20" with Interval: 6 fires at 00:20, 06:20, 12:20 and 18:20.
    Type
    String
    Required
    ConditionalRequired for Daily, Weekly, Monthly, and BusinessHours.
    Update behaviour
    No interruption
    Pattern
    ^([01]\d|2[0-3]):([0-5]\d)$ 24-hour clock, zero-padded.

    Example values

    • 03:00
    • 23:30
  • DaylightSaving StringHow to handle a zone whose UTC offset changes during the year. No interruption — updates in place
    • Reject — fail the stack operation if the zone observes DST. Forces the decision to be made explicitly, in the template, by whoever can judge it.
    • PinStandard — always use the zone’s standard-time offset. The schedule is an hour late during summer time, and never moves.
    • PinCurrent — use whichever offset is in effect on the deployment date. The schedule is correct today and wrong in six months.
    • SplitExpressions — emit two expressions in ExpressionSet, one per offset, each with a month range. Correct all year, but requires the consumer to accept a list of schedules.
    Type
    String
    Required
    No
    Update behaviour
    No interruption
    Default
    Reject

    Allowed values

    • Reject
    • PinStandard
    • PinCurrent
    • SplitExpressions
  • Jitter IntegerDeterministic spread, in minutes, applied to the computed time. No interruption — updates in place
    The offset is derived by hashing JitterSeed, so it is stable across deployments — the same stack always lands on the same minute. This spreads a fleet of identically-scheduled stacks without making any single stack’s schedule unpredictable.
    Type
    Integer
    Required
    No
    Update behaviour
    No interruption
    Default
    0
    Range
    −∞ – 59
  • JitterSeed StringSeed for the jitter hash. Defaults to the stack ID. No interruption — updates in place
    Give two stacks the same seed and they land on the same minute — occasionally useful when two schedules must stay aligned.
    Type
    String
    Required
    No
    Update behaviour
    No interruption

    Example values

    • ${AWS::StackId}
  • Target StringWhich service will consume the expression, which changes its syntax. No interruption — updates in place
    EventBridge cron takes six fields and wraps them in cron(...). Auto Scaling takes five fields, unwrapped, and treats day-of-week 0 as Sunday. Emitting the wrong dialect is one of the more tedious ways to lose an afternoon, so the resource emits the right one.
    Type
    String
    Required
    No
    Update behaviour
    No interruption
    Default
    EventBridge

    Allowed values

    • EventBridge
    • AutoScaling
    • Backup
    • SSM
updates in place some interruption replacement create-only read-only write-only

Return values

What other resources can read from this one
Ref
Returns the generated expression — the same value as Fn::GetAtt [ Schedule, Expression ], so it can be dropped straight into a ScheduleExpression property.

!Ref MyResource → cron(0 7 ? * MON-FRI *)

Fn::GetAtt attributes

AttributeTypeDescriptionExample value
ExpressionStringThe generated expression, in the dialect selected by Target.cron(0 7 ? * MON-FRI *)
ExpressionSetArrayAll generated expressions. One entry unless DaylightSaving is SplitExpressions.["cron(0 8 ? * MON-FRI *)","cron(0 7 ? * MON-FRI *)"]
LocalDescriptionStringA plain-English rendering of the schedule. Put it in a stack output — it is the fastest way to catch a wrong schedule during review.Every weekday at 03:00 America/Toronto (07:00 UTC during standard time)
NextOccurrencesArrayThe next five fire times in UTC, computed at deploy time.["2026-08-10T07:00:00Z","2026-08-11T07:00:00Z"]
ObservesDaylightSavingBooleanWhether the named zone changes offset during the year.true
UtcOffsetMinutesIntegerThe zone offset applied, in minutes east of UTC.-300
UtcTimeStringThe computed UTC time of day, for eyeballing against the local time you asked for.07:00

Required permissions

For the principal running the stack operation

This type makes no AWS API calls. It is computed entirely from its own properties during the stack operation, so it needs no permissions beyond those CloudFormation already holds to manage the stack.

Examples

1 worked scenario
Region-staggered maintenance One template, deployed to eight regions, that gives each region a different maintenance window in a deliberate order — so a bad change reaches the regions you watch first. AdvancedUpdraft::Schedule::CronExpressionUpdraft::Region::StaggerUpdraft::Region::MetadataDiagram