Migrate Off Instance Scheduler on AWS Without Double Stops
Inventory what the solution is doing, run one scheduler at a time, and tear the stacks down in the right order.
Instance Scheduler on AWS is a CloudFormation-deployed solution that starts and stops EC2 and RDS resources according to a tag. Moving to another scheduler is mostly an inventory exercise, plus one rule: never let two schedulers own the same resource at once, because the solution’s enforced setting will fight any other tool.
1. Find every tagged resource
The solution reads a tag key that defaults to Schedule; the tag value is the name of a schedule. The key is case sensitive and can be changed at deploy time, so check the hub stack’s Schedule tag key parameter in the CloudFormation console before searching.
aws resourcegroupstaggingapi get-resources \ --tag-filters Key=Schedule \ --resource-type-filters ec2:instance rds:db rds:cluster \ --query "ResourceTagMappingList[].{arn:ResourceARN,tags:Tags}" \ --output json > instance-scheduler-inventory.jsonRun it in every account and Region the hub stack or its remote stacks cover.
2. Export the schedules and periods
Install the Scheduler CLI that ships with the solution (it needs Python 3.8 or later and boto3), then dump the configuration. --stack is required on every command.
scheduler-cli describe-schedules --stack instance-scheduler-hub > schedules.jsonscheduler-cli describe-periods --stack instance-scheduler-hub > periods.jsonA schedule is a group of one or more periods; each period is a running window with a start and stop time. When a schedule has several periods, the solution starts the instance if at least one period is active.
3. Map the fields that change behaviour
Most of the work is in the schedule flags, not the times:
| Instance Scheduler field | What it does | What to set in the new scheduler |
|---|---|---|
| time zone | Per schedule; falls back to the stack’s default, which is UTC | The same IANA zone, explicitly |
stop_new_instances | Default true: stops a running instance the first time it is tagged outside a period | Decide whether first attachment should stop immediately |
enforced | Stops an instance started manually outside a period, and restarts one stopped manually inside a period | Replace with a documented override process |
retain_running | Leaves an instance running at period end if it was started manually before the period began | Usually an override with an expiry |
hibernate | Hibernates EC2 instead of stopping, if the AMI supports it | Check the new tool’s stop verb |
ssm-maintenance-window | Adds an SSM maintenance window as a running period | Add the window as an extra start and stop, or the instance will be off during it |
4. Suspend, then cut over
Do not delete anything yet. In the CloudFormation console, update the hub stack and set Enable scheduling to No. That suspends all scheduling operations while leaving tags, tables and schedules intact, so you can roll back by setting it to Yes.
Then enable the replacement schedules. For the first week, compare the replacement’s actions with CloudTrail:
aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=StopInstances \ --start-time 2026-09-24T00:00:00ZThe Username and user identity on each event show which principal made the call: every stop should come from the new scheduler, none from the solution.
5. Remove the tags and the stacks
Once a full week has run cleanly, remove the tag so nothing re-enrols by accident:
aws resourcegroupstaggingapi untag-resources \ --resource-arn-list arn:aws:ec2:eu-west-1:123456789012:instance/i-0abc1234def567890 \ --tag-keys ScheduleAWS’s uninstall guidance is to delete all custom schedule stacks before the solution itself, and to delete the hub stack together with every remote stack in secondary accounts.
aws cloudformation delete-stack --stack-name instance-scheduler-hubIf DynamoDB table protection (the Retain data and logs parameter at deploy time) is enabled on the hub stack, CloudFormation keeps the solution’s DynamoDB tables and KMS key. Disable it before deleting the stack, or delete those resources by hand afterwards.
Rebuilding the schedules in ZopNight
Each Instance Scheduler schedule becomes a ZopNight schedule with a name, a description, an IANA timezone and cron start and stop expressions; a schedule can hold several crons, which covers multi-period schedules. Resources attach individually or through a resource group, where members can be ordered with a per-resource delay. Overrides force a resource on or off until an expiry, which replaces most uses of enforced and retain_running, and each start, stop or failure can notify Slack, Teams, Google Chat or a webhook. EC2 and RDS are both on the start and stop list; the EC2 scheduling page and the cron scheduling guide help with the translation.