ec2-instance-running-scheduler 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,46 +1,59 @@
1
- # EC2 Instance Running Scheduler (AWS CDK v2)
1
+ # EC2 Instance Running Scheduler (CDK v2)
2
2
 
3
- [![GitHub](https://img.shields.io/github/license/gammarers-aws-cdk-constructs/ec2-instance-running-scheduler?style=flat-square)](https://github.com/gammarers-aws-cdk-constructs/ec2-instance-running-scheduler/blob/main/LICENSE)
4
- [![npm](https://img.shields.io/npm/v/ec2-instance-running-scheduler?style=flat-square)](https://www.npmjs.com/package/ec2-instance-running-scheduler)
5
- [![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/gammarers-aws-cdk-constructs/ec2-instance-running-scheduler/release.yml?branch=main&label=release&style=flat-square)](https://github.com/gammarers-aws-cdk-constructs/ec2-instance-running-scheduler/actions/workflows/release.yml)
6
- [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/gammarers-aws-cdk-constructs/ec2-instance-running-scheduler?sort=semver&style=flat-square)](https://github.com/gammarers-aws-cdk-constructs/ec2-instance-running-scheduler/releases)
3
+ [![npm version](https://img.shields.io/npm/v/ec2-instance-running-scheduler?style=flat-square)](https://www.npmjs.com/package/ec2-instance-running-scheduler)
4
+ [![license](https://img.shields.io/npm/l/ec2-instance-running-scheduler?style=flat-square)](https://www.npmjs.com/package/ec2-instance-running-scheduler)
5
+ [![Node.js](https://img.shields.io/node/v/ec2-instance-running-scheduler?style=flat-square)](https://www.npmjs.com/package/ec2-instance-running-scheduler)
6
+ [![build](https://img.shields.io/github/actions/workflow/status/gammarers-aws-cdk-constructs/ec2-instance-running-scheduler/build.yml?label=build&style=flat-square)](https://github.com/gammarers-aws-cdk-constructs/ec2-instance-running-scheduler/actions/workflows/build.yml)
7
7
 
8
8
  [![View on Construct Hub](https://constructs.dev/badge?package=ec2-instance-running-scheduler)](https://constructs.dev/packages/ec2-instance-running-scheduler)
9
9
 
10
- AWS CDK construct library that starts and stops EC2 instances on a cron schedule using **EventBridge Scheduler** and a **Durable Execution Lambda**. The handler discovers instances with the **Resource Groups Tagging API**, issues start/stop, **waits until each instance reaches a stable target state** (durable `step` / `wait`), processes **multiple instances in parallel** (bounded concurrency), and posts **Slack** summary and per-instance thread messages using a secret from **Secrets Manager**. The Lambda emits **structured application logs** alongside JSON platform logs.
10
+ AWS CDK construct library that starts and stops EC2 instances on a cron schedule using EventBridge Scheduler and a Durable Execution Lambda. Tagged instances are discovered account-wide, started or stopped in parallel, waited until stable, and reported to Slack via Secrets Manager.
11
11
 
12
12
  ## Features
13
13
 
14
14
  - **Tag-based targeting** – Select EC2 instances by tag key and values (e.g. `Schedule` / `YES`) via `tag:GetResources`.
15
15
  - **EventBridge Scheduler** – Separate cron rules for start and stop, with per-rule timezone (`aws-cdk-lib` `TimeZone`).
16
16
  - **Durable Lambda** – One Lambda with AWS Lambda Durable Execution (`step`, `wait`, `map`, child contexts per instance) for long-running workflows without Step Functions.
17
- - **Stable-state waiting** – After start/stop, the function waits (`resourceWait.statusChangeWaitSeconds`, default **20** seconds between attempts) and re-describes instances until `running` (start mode) or `stopped` (stop mode).
18
- - **Configurable wait limits** – Per-instance **max loop count**, **max elapsed time**, and **status-change wait interval** via `resourceWait` (default: 90 loops / 1800 seconds / 20 seconds). Failures use explicit `ResourceWaitFailed:*` messages instead of running until the Durable execution timeout (construct default: 2 hours; override with `durable.executionTimeout`).
19
- - **Configurable Lambda runtime** – Memory, invoke timeout, and bounded instance concurrency via `runtime` (default: **512 MB** / **15 minutes** / **maxConcurrency 10**). Durable execution timeout and history retention via `durable` (default: **2 hours** / **1 day**).
20
- - **Configurable logs** – Log group retention and removal policy via `logGroup` (default: **3 months**, `RemovalPolicy.DESTROY`).
21
- - **Validated environment variables** – The bundled handler parses env vars with **strict-env-resolver** (`StrictEnvResolver`). `SLACK_SECRET_NAME` is required; wait limits and `maxConcurrency` must be **positive integers** (`>= 1`).
22
- - **Slack notifications** – Parent message plus threaded updates per instance; credentials from Secrets Manager JSON (`token`, `channel`). The construct sets **`SLACK_SECRET_NAME`** on the function.
23
- - **Structured logging** – Durable execution **`ctx.logger`** for traceable JSON application logs (invocation, describe/start/stop/wait loops, wait limit errors, Slack steps, completion).
24
- - **Optional failure detection** – CloudWatch alarms and log-based metrics for Lambda errors, instance wait failures (`ResourceWaitFailed`), Slack post failures, and other handler `ERROR` logs. Optional SNS notifications via a caller-supplied topic (`failureDetection.alarmTopic`).
17
+ - **Stable-state waiting** – After start/stop, waits and re-describes until `running` or `stopped`, with configurable wait limits (`resourceWait`).
18
+ - **Configurable Lambda runtime** – Memory, invoke timeout, and bounded concurrency via `runtime`; Durable timeout and retention via `durable`.
19
+ - **Slack notifications** – Parent message plus threaded updates per instance; credentials from Secrets Manager JSON (`token`, `channel`).
20
+ - **Optional failure detection** – Opt-in CloudWatch alarms via `failureDetection` (`enabled` / optional `alarmTopic`). Uses `LambdaFailureDetector` for platform Lambda `Errors` plus scheduler-specific log filters (`ResourceWaitFailed`, Slack post failures, other handler `ERROR` logs). The construct never creates an SNS topic.
25
21
  - **Scheduling toggle** – Enable or disable both schedules without removing the stack (`enableScheduling`).
26
- - **Configurable schedules** – Optional cron overrides for start and stop (`minute`, `hour`, `week`, `timezone`); sensible defaults if omitted.
27
- - **IAM and observability** – Start/stop is limited to EC2 instances in the stack account/region whose tags match `targetResource`. Slack secret read grant, **Parameters and Secrets Lambda Extension**, JSON logging, and a dedicated log group (override retention and removal policy via `logGroup`).
22
+
23
+ ## How it works
24
+
25
+ 1. Tag EC2 instances in the same account and region with the `targetResource` tag key and one of the tag values **before** enabling schedules.
26
+ 2. EventBridge Scheduler invokes the Lambda alias on the start/stop cron with `Params.TagKey`, `Params.TagValues`, and `Params.Mode` (`Start` or `Stop`).
27
+ 3. The Durable handler discovers matching instances via the Resource Groups Tagging API, then starts or stops them in parallel (bounded by `runtime.maxConcurrency`).
28
+ 4. For each instance it waits (`resourceWait`) until the desired stable state, or fails with a `ResourceWaitFailed:*` error.
29
+ 5. Slack receives a summary and per-instance thread updates when the secret is configured.
30
+ 6. When `failureDetection.enabled` is `true`, CloudWatch alarms cover Lambda platform errors and the scheduler log filters above. Pass `failureDetection.alarmTopic` to attach SNS actions to an existing topic.
31
+
32
+ **Tag an instance (AWS CLI)**
33
+
34
+ ```bash
35
+ aws ec2 create-tags \
36
+ --resources i-0123456789abcdef0 \
37
+ --tags Key=Schedule,Value=YES
38
+ ```
39
+
40
+ Start/stop IAM is limited to instance ARNs in the stack account and region whose `aws:ResourceTag/<tagKey>` matches `tagValues`. `tag:GetResources` and `ec2:DescribeInstances` still use `Resource: *` because those APIs do not support resource-level permissions or resource-tag conditions.
28
41
 
29
42
  ## Installation
30
43
 
31
- **npm**
44
+ ### npm
32
45
 
33
46
  ```bash
34
47
  npm install ec2-instance-running-scheduler
35
48
  ```
36
49
 
37
- **yarn**
50
+ ### yarn
38
51
 
39
52
  ```bash
40
53
  yarn add ec2-instance-running-scheduler
41
54
  ```
42
55
 
43
- **pnpm**
56
+ ### pnpm
44
57
 
45
58
  ```bash
46
59
  pnpm add ec2-instance-running-scheduler
@@ -108,7 +121,7 @@ new EC2InstanceRunningScheduler(stack, 'EC2InstanceRunningScheduler', {
108
121
  });
109
122
  ```
110
123
 
111
- Use the **stack** `EC2InstanceRunningScheduleStack` when deploying the scheduler as its own stack. It accepts the same **targeting, schedules, secrets, enable flag, and failure detection** as the construct (plus standard `StackProps` such as `env`). For **`resourceWait`**, **`runtime`**, **`durable`**, and **`logGroup`**, use the construct directly or extend the stack in your app.
124
+ Use the **stack** `EC2InstanceRunningScheduleStack` when deploying the scheduler as its own stack. It accepts the same targeting, schedules, secrets, enable flag, and failure detection as the construct (plus standard `StackProps`). For `resourceWait`, `runtime`, `durable`, and `logGroup`, use the construct directly.
112
125
 
113
126
  ```typescript
114
127
  import * as cdk from 'aws-cdk-lib';
@@ -152,39 +165,6 @@ new EC2InstanceRunningScheduleStack(app, 'EC2InstanceRunningScheduleStack', {
152
165
  });
153
166
  ```
154
167
 
155
- ## Tag instances before scheduling
156
-
157
- The scheduler only starts and stops EC2 instances that **already** have the tag key and one of the tag values in `targetResource`. Tag instances in the same account and region as the stack **before** enabling schedules.
158
-
159
- **AWS CLI**
160
-
161
- ```bash
162
- aws ec2 create-tags \
163
- --resources i-0123456789abcdef0 \
164
- --tags Key=Schedule,Value=YES
165
- ```
166
-
167
- **Console** – EC2 → Instances → select the instance → Tags → Add `Schedule` = `YES` (or your `tagKey` / `tagValues`).
168
-
169
- IAM for `ec2:StartInstances` and `ec2:StopInstances` is limited to:
170
-
171
- - instance ARNs in the stack **account** and **region** (`arn:...:ec2:<region>:<account>:instance/*`)
172
- - instances whose `aws:ResourceTag/<tagKey>` matches one of `tagValues`
173
-
174
- An instance without the tag is not discovered by `tag:GetResources`, and start/stop is denied even if an instance ID is known. `tag:GetResources` and `ec2:DescribeInstances` still use `Resource: *` because those APIs do not support resource-level permissions or resource-tag conditions.
175
-
176
- EventBridge Scheduler invokes the Lambda with `Params.TagKey`, `Params.TagValues`, and `Params.Mode` (`Start` or `Stop`); the construct wires this for you. The function environment includes:
177
-
178
- | Variable | Source | Purpose |
179
- |----------|--------|---------|
180
- | `SLACK_SECRET_NAME` | `secrets.slackSecretName` | Secrets Manager secret for Slack (required) |
181
- | `PROCESS_RESOURCE_MAX_LOOP_COUNT` | `resourceWait.maxLoopCount` (default `90`) | Max describe/wait iterations per instance |
182
- | `PROCESS_RESOURCE_MAX_ELAPSED_SECONDS` | `resourceWait.maxElapsedSeconds` (default `1800`) | Max wall-clock seconds waiting for one instance |
183
- | `PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS` | `resourceWait.statusChangeWaitSeconds` (default `20`) | Seconds between describe/wait iterations |
184
- | `PROCESS_RESOURCES_MAX_CONCURRENCY` | `runtime.maxConcurrency` (default `10`) | Max instances processed in parallel |
185
-
186
- When you set wait limits via `resourceWait` or concurrency via `runtime.maxConcurrency`, the construct writes them as decimal integer strings. At invocation the handler parses them with **strict-env-resolver**; each value must be a **positive integer** (`>= 1`). Missing `SLACK_SECRET_NAME` or invalid env values cause `StrictEnvValidationError` at the start of an invocation.
187
-
188
168
  ## Options
189
169
 
190
170
  ### EC2InstanceRunningScheduler
@@ -200,7 +180,7 @@ When you set wait limits via `resourceWait` or concurrency via `runtime.maxConcu
200
180
  | `runtime` | `RunningSchedulerRuntimeProps` | No | Lambda memory, invoke timeout, and map concurrency. |
201
181
  | `durable` | `RunningSchedulerDurableProps` | No | Durable execution timeout and history retention. |
202
182
  | `logGroup` | `RunningSchedulerLogGroupProps` | No | Function log group retention and removal policy. |
203
- | `failureDetection` | `FailureDetectionAlarms` | No | Optional CloudWatch alarms and log-based metrics (see below). |
183
+ | `failureDetection` | `RunningSchedulerFailureDetectionProps` | No | Optional CloudWatch alarms and log-based metrics (see below). |
204
184
 
205
185
  ### EC2InstanceRunningScheduleStack
206
186
 
@@ -211,8 +191,6 @@ Includes `targetResource`, `secrets`, `startSchedule`, `stopSchedule`, `enableSc
211
191
  - `tagKey` – Tag key used to select instances (e.g. `Schedule`). Required on each target instance before schedules run.
212
192
  - `tagValues` – Tag values that must match (e.g. `['YES']`). At least one value is required.
213
193
 
214
- See [Tag instances before scheduling](#tag-instances-before-scheduling).
215
-
216
194
  ### Schedule
217
195
 
218
196
  - `timezone` – `TimeZone` from `aws-cdk-lib` (e.g. `TimeZone.ASIA_TOKYO`, `TimeZone.ETC_UTC`).
@@ -252,30 +230,34 @@ Lambda invoke settings. Written `maxConcurrency` to `PROCESS_RESOURCES_MAX_CONCU
252
230
  - `retention` – CloudWatch Logs retention (default: **`RetentionDays.THREE_MONTHS`**).
253
231
  - `removalPolicy` – Log group removal policy (default: **`RemovalPolicy.DESTROY`**).
254
232
 
255
- ### FailureDetectionAlarms
233
+ ### RunningSchedulerFailureDetectionProps
256
234
 
257
- Optional operational failure detection. Alarms are created only when `enabled` is `true`.
235
+ Extends `LambdaFailureDetection` (`enabled` / `alarmTopic`). Alarms are created only when `enabled` is `true`. Internally uses `LambdaFailureDetector` with scheduler-specific log filters. The construct does **not** create an SNS topic.
258
236
 
259
237
  - `enabled` – When `true`, creates four CloudWatch alarms and three log metric filters (default: disabled when omitted).
260
- - `alarmTopic` – Optional `sns.ITopic` for alarm actions. The construct does **not** create an SNS topic; pass an existing or imported topic. When omitted, alarms are created without SNS actions.
238
+ - `alarmTopic` – Optional `sns.ITopic` for alarm actions. When omitted, alarms are created without SNS actions.
261
239
 
262
- When enabled, the construct creates:
240
+ When enabled:
263
241
 
264
242
  | Alarm | Trigger |
265
243
  |-------|---------|
266
- | Lambda errors | `AWS/Lambda` `Errors` metric |
244
+ | Lambda errors | `AWS/Lambda` `Errors` metric (platform) |
267
245
  | Instance status failure | Log filter: `ResourceWaitFailed` |
268
246
  | Slack post failure | Log filter: `running-scheduler: Slack post failed` |
269
247
  | Durable execution failure | Other handler `ERROR` logs (excluding the above) |
270
248
 
271
- Custom metrics are published under the `EC2InstanceRunningScheduler` namespace. Access the created alarms via `EC2InstanceRunningScheduler.failureDetection` when enabled.
249
+ Custom metrics use the `EC2InstanceRunningScheduler` namespace. Access created alarms via `EC2InstanceRunningScheduler.failureDetection` when enabled.
250
+
251
+ ## API
252
+
253
+ See [API.md](./API.md).
272
254
 
273
255
  ## Requirements
274
256
 
275
257
  - **Node.js** ≥ 20.0.0 (for developing or synthesizing CDK apps that depend on this package).
276
258
  - **aws-cdk-lib** ^2.232.0 and **constructs** ^10.5.1 (peer dependencies).
277
- - **AWS** – EventBridge Scheduler; Lambda with **Durable Execution** (Node.js **24.x** runtime in the construct; Durable Execution requires a supported Node.js runtime in your region), a **live alias**, **Parameters and Secrets Lambda Extension**; EC2 (`DescribeInstances`, `StartInstances`, `StopInstances`); Resource Groups Tagging API (`tag:GetResources`); Secrets Manager. The deployed function uses **arm64**, Durable Execution IAM policies, a 2-hour Durable execution timeout (construct default), and a bundled handler that loads secrets via **aws-lambda-secret-fetcher** (^0.7) and parses env vars via **strict-env-resolver** (^0.6). Secret fetch runs only inside Lambda (requires runtime `AWS_SESSION_TOKEN` and the extension layer); the library retries transient extension errors including cold-start "not ready" responses.
259
+ - **AWS** – EventBridge Scheduler; Lambda with Durable Execution (Node.js **24.x** runtime in the construct); Parameters and Secrets Lambda Extension; EC2 (`DescribeInstances`, `StartInstances`, `StopInstances`); Resource Groups Tagging API (`tag:GetResources`); Secrets Manager.
278
260
 
279
261
  ## License
280
262
 
281
- This project is licensed under the Apache-2.0 License.
263
+ This project is licensed under the (Apache-2.0) License.