1import type { Input } from "../../Input.ts";
2import * as Scheduler from "../Scheduler/index.ts";
3import type { Cluster } from "./Cluster.ts";
4
5export interface ScheduleProps {
6 /**
7 * ECS cluster that the scheduled task should run on.
8 */
9 cluster: Cluster;
10
11 /**
12 * ECS task definition and roles to invoke on each schedule tick.
13 *
14 * This is typically an `AWS.ECS.Task` resource (whose attributes are
15 * `Output`s — the fields are `Input`-typed because `every` is a helper
16 * function, not resource Props).
17 */
18 task: {
19 /**
20 * Task definition ARN to run.
21 */
22 taskDefinitionArn: Input<string>;
23 /**
24 * Task role ARN passed through EventBridge `iam:PassRole`.
25 */
26 taskRoleArn: Input<string>;
27 /**
28 * Execution role ARN passed through EventBridge `iam:PassRole`.
29 */
30 executionRoleArn: Input<string>;
31 };
32
33 /**
34 * Subnets used for the Fargate task network configuration.
35 */
36 subnets: Input<string[]>;
37
38 /**
39 * Security groups attached to the scheduled task ENIs.
40 */
41 securityGroups?: Input<string[]>;
42
43 /**
44 * Whether the scheduled task should receive a public IP.
45 * @default false
46 */
47 assignPublicIp?: boolean;
48
49 /**
50 * Number of task copies to start on each invocation.
51 * @default 1
52 */
53 taskCount?: number;
54
55 /**
56 * Static JSON payload forwarded to the EventBridge ECS target.
57 */
58 input?: string;
59}
60
61const toScheduleExpression = (value: string) =>
62 value.startsWith("rate(") || value.startsWith("cron(")
63 ? value
64 : `rate(${value})`;
65
66/**
67 * Creates a scheduled EventBridge rule that runs an ECS Fargate task.
68 *
69 * `every` is the high-level ECS scheduling helper for phase 1. It provisions
70 * the EventBridge rule plus the invoke role required to call `ecs:RunTask` and
71 * `iam:PassRole` for the target task's execution roles.
72 *
73 * Plain English durations like `"1 hour"` are normalized to `rate(...)`
74 * expressions automatically. Full `rate(...)` and `cron(...)` expressions are
75 * also accepted as-is.
76 * **Example:** Run a task every hour
77 * ```typescript
78 * yield* AWS.ECS.every("HourlyJob", "1 hour", {
79 * cluster,
80 * task: jobTask,
81 * subnets: [privateSubnet1.subnetId, privateSubnet2.subnetId],
82 * securityGroups: [jobSecurityGroup.groupId],
83 * });
84 * ```
85 *
86 * **Example:** Use an explicit cron expression
87 * ```typescript
88 * yield* AWS.ECS.every("NightlyJob", "cron(0 3 * * ? *)", {
89 * cluster,
90 * task: nightlyTask,
91 * subnets: [privateSubnet1.subnetId, privateSubnet2.subnetId],
92 * securityGroups: [jobSecurityGroup.groupId],
93 * });
94 * ```
95 *
96 * **Example:** Run multiple copies with static input
97 * ```typescript
98 * yield* AWS.ECS.every("BatchJob", "30 minutes", {
99 * cluster,
100 * task: batchTask,
101 * subnets: [privateSubnet1.subnetId, privateSubnet2.subnetId],
102 * securityGroups: [jobSecurityGroup.groupId],
103 * taskCount: 3,
104 * input: JSON.stringify({ source: "scheduler" }),
105 * });
106 * ```
107 *
108 * @binding
109 */
110export const every = (id: string, schedule: string, props: ScheduleProps) =>
111 Scheduler.every(toScheduleExpression(schedule)).named(id).toEcsTask({
112 cluster: props.cluster,
113 task: props.task,
114 subnets: props.subnets,
115 securityGroups: props.securityGroups,
116 assignPublicIp: props.assignPublicIp,
117 taskCount: props.taskCount,
118 input: props.input,
119 });