AWS EventBridge Scheduler: Starting and Stopping EC2 on a Schedule
Starting and stopping EC2 instances on a cron schedule with EventBridge Scheduler calling the EC2 API directly, no Lambda involved.
EventBridge Scheduler can start and stop EC2 instances on a cron schedule without any Lambda function in between.
Building
Start an EC2 instance for the test.
aws ec2 run-instances \ --image-id ami-0f9fe1d9214628296 \ --count 1 \ --instance-type t2.micro \ --security-group-ids <SECURITY_GROUP_IDS> \ --subnet-id <SUBNET_ID>The ScheduleStartExpression and ScheduleStopExpression parameters on lines 11-16 define the schedules as cron expressions.
AWSTemplateFormatVersion: '2010-09-09'Description: EC2 scheduler
Parameters: Prefix: Type: String Default: ec2-scheduler InstanceIds: Type: String Description: '"i-1234567890abcdefg", "..."' ScheduleStartExpression: Type: String Default: 'cron(0 7 * * ? *)' ScheduleStopExpression: Type: String Default: 'cron(0 22 * * ? *)' ScheduleTimezone: Type: String Description: IANA timezone identifier. For the full list, refer to https://en.wikipedia.org/wiki/List_of_tz_database_time_zones Default: UTC AllowedValues: - UTC - Japan
Resources: ScheduleStart: Type: AWS::Scheduler::Schedule Properties: Name: !Sub ${Prefix}-schedule-start ScheduleExpression: !Ref ScheduleStartExpression ScheduleExpressionTimezone: !Ref ScheduleTimezone FlexibleTimeWindow: Mode: 'OFF' State: ENABLED Target: Arn: arn:aws:scheduler:::aws-sdk:ec2:startInstances Input: !Sub |- { "InstanceIds": [${InstanceIds}] } RoleArn: !GetAtt Role.Arn
ScheduleStop: Type: AWS::Scheduler::Schedule Properties: Name: !Sub ${Prefix}-schedule-stop ScheduleExpression: !Ref ScheduleStopExpression ScheduleExpressionTimezone: !Ref ScheduleTimezone FlexibleTimeWindow: Mode: 'OFF' State: ENABLED Target: Arn: arn:aws:scheduler:::aws-sdk:ec2:stopInstances Input: !Sub |- { "InstanceIds": [${InstanceIds}] } RoleArn: !GetAtt Role.Arn
Role: Type: AWS::IAM::Role Properties: RoleName: !Sub ${Prefix}-role AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - scheduler.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: ec2 PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - ec2:StartInstances - ec2:StopInstances Resource: !Sub arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/i-*Deploy the CloudFormation stack with the following command:
aws cloudformation deploy \ --template-file ./template.yaml \ --stack-name ec2-scheduler \ --parameter-overrides InstanceIds='"<INSTANCE_ID1>", "<INSTANCE_ID2>"' \ --capabilities CAPABILITY_NAMED_IAMTesting
With the default UTC timezone in this template, EventBridge Scheduler starts the EC2 instance at 07:00 UTC and stops it at 22:00 UTC.
Cleaning Up
Clean up all the AWS resources provisioned during this example with the following command:
aws ec2 terminate-instances --instance-ids <INSTANCE_IDS>aws cloudformation delete-stack --stack-name ec2-schedulerConclusion
Two EventBridge Scheduler schedules start the EC2 instance at 07:00 and stop it at 22:00 in the timezone selected by ScheduleExpressionTimezone.
Targeting the EC2 API directly through arn:aws:scheduler:::aws-sdk:ec2:startInstances is what keeps this setup free of any Lambda function — EventBridge Scheduler can call the AWS SDK directly, so the schedule and the IAM role granting ec2:StartInstances/ec2:StopInstances are the entire solution.
The one setting worth double-checking before relying on this for cost savings is ScheduleExpressionTimezone: since it defaults to UTC in the template, a schedule meant to align with business hours in another timezone needs that parameter set explicitly, or instances will start and stop several hours off from what’s expected.
Related posts
Connecting to EC2 with Session Manager (No SSH Required)
Opening a shell on an EC2 instance through Systems Manager's Session Manager, with no SSH keys, bastion host, or open port 22 required.
Debugging PHP Remotely on AWS EC2 with PhpStorm and Xdebug
Remote debugging a PHP application on EC2 with PhpStorm and Xdebug, from the server-side ini settings to the IDE's path mapping.
Securely Accessing EC2 Windows Instances via SSH Port Forwarding
Reaching an EC2 Windows instance in a private subnet through an SSH bastion host, keeping RDP off the public internet entirely.
Running Proxy.py as a Lightweight HTTP Proxy on EC2
Running Proxy.py on an EC2 instance and reaching it safely through an SSH tunnel, since the proxy has no authentication of its own.
Migrating from aws-vault to Granted
I migrated to Granted after aws-vault development ended. Here is how role switching and console access differ, and what to check when carrying over credentials and configuration.
