为啥,因为停止 EC2 实例后跟终止 EC2 实例一样不计费,但弹性 IP 地址和 EBS 卷还是要计费的
使用方法
注意在与 EC2 实例同区域下,上传模板至 CloudFormation,此处模板文件为 ScheduleEC2StartStop_Temp.yaml,内容在最下

修改以下内容
- TargetEC2InstanceIds: 填写实例 ID,多个实例 ID 以“,”间隔
- StartScheduleCron: 填写启动实例的 cron 表达式,UTC 时间
- StopScheduleCron: 填写停止实例的 cron 表达式,UTC 时间
https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html

下一步选择创建 IAM 相关角色

然后创建

等待 1-2 分钟堆栈完成所有资源的创建


完成后进入 Lambda 相关函数处测试并部署,测试时可以在控制台看到输出结果

CloudWatch 日志组中可以查看到相关的日志事件

若想清除所有已创建资源,直接删除堆栈即可
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template for EC2 Start/Stop Scheduler
Parameters:
TargetEC2InstanceIds:
Type: String
Description: Comma-separated list of EC2 instance IDs to start/stop (e.g., i-1234567890abcdef0,i-0987654321fedcba0)
Default: i-0a33a15f0e969b484
StartScheduleCron:
Type: String
Description: >-
Cron expression for EC2 start schedule (UTC). Format: cron(Minutes Hours Day-of-month Month Day-of-week Year)
Default: cron(0 0 ? * * *)
AllowedPattern: >-
cron\(([0-9*/,-]+)\s([0-9*/,-]+)\s([0-9*/,\-?LW]+)\s([0-9A-Z*/,-]+)\s([0-9A-Z*/,\-?L#]+)\s([0-9*/,-]+|\*)\)
ConstraintDescription: >-
Must be a valid cron expression. Format: cron(Minutes Hours Day-of-month Month Day-of-week Year).
Example: cron(0 9 ? * * *) for every day at 9 AM UTC.
StopScheduleCron:
Type: String
Description: >-
Cron expression for EC2 stop schedule (UTC). Format: cron(Minutes Hours Day-of-month Month Day-of-week Year)
Default: cron(0 17 ? * * *)
AllowedPattern: >-
cron\(([0-9*/,-]+)\s([0-9*/,-]+)\s([0-9*/,\-?LW]+)\s([0-9A-Z*/,-]+)\s([0-9A-Z*/,\-?L#]+)\s([0-9*/,-]+|\*)\)
ConstraintDescription: >-
Must be a valid cron expression. Format: cron(Minutes Hours Day-of-month Month Day-of-week Year).
Example: cron(0 17 ? * * *) for every day at 5 PM UTC.
Resources:
# Lambda 执行角色
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: EC2StartStopPermissions
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:StartInstances
- ec2:StopInstances
- ec2:DescribeInstances
Resource: !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/*'
# EventBridge 调用 Lambda 的角色
EventBridgeInvokeLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: InvokeLambdaFunction
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: !GetAtt ScheduleEC2StartStop.Arn
# Lambda 函数
ScheduleEC2StartStop:
Type: AWS::Lambda::Function
Properties:
FunctionName: ScheduleEC2StartStop
Description: >-
A Lambda function that automatically starts and stops EC2 instances based on a schedule.
Runtime: python3.13
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: |
import boto3
from datetime import datetime
import os
def lambda_handler(event, context):
# 初始化 EC2 客户端
ec2 = boto3.client('ec2')
# 获取目标实例 ID(从环境变量中读取)
instance_ids = os.environ['TARGET_INSTANCES'].split(',')
# 确定操作类型(启动或停止)
action = event.get('action', 'stop')
try:
if action.lower() == 'start':
# 启动实例
response = ec2.start_instances(InstanceIds=instance_ids)
print(f"Starting instances: {instance_ids}")
elif action.lower() == 'stop':
# 停止实例
response = ec2.stop_instances(InstanceIds=instance_ids)
print(f"Stopping instances: {instance_ids}")
return {
'statusCode': 200,
'body': f"Successfully {action}ed instances: {instance_ids}"
}
except Exception as e:
print(f"Error: {str(e)}")
return {
'statusCode': 500,
'body': f"Error performing {action} operation: {str(e)}"
}
Environment:
Variables:
TARGET_INSTANCES: !Ref TargetEC2InstanceIds
MemorySize: 128
Timeout: 30
# EventBridge 规则 - 启动 EC2
StartEC2Rule:
Type: AWS::Events::Rule
Properties:
Name: StartEC2
Description: Start EC2 instances with lambda
ScheduleExpression: !Ref StartScheduleCron
State: ENABLED
Targets:
- Id: StartEC2Target
Arn: !GetAtt ScheduleEC2StartStop.Arn
RoleArn: !GetAtt EventBridgeInvokeLambdaRole.Arn
Input: '{"action": "start"}'
# EventBridge 规则 - 停止 EC2
StopEC2Rule:
Type: AWS::Events::Rule
Properties:
Name: StopEC2
Description: Stop EC2 instances with lambda
ScheduleExpression: !Ref StopScheduleCron
State: ENABLED
Targets:
- Id: StopEC2Target
Arn: !GetAtt ScheduleEC2StartStop.Arn
RoleArn: !GetAtt EventBridgeInvokeLambdaRole.Arn
Input: '{"action": "stop"}'
# Lambda 权限 - 允许 StartEC2Rule 调用
LambdaStartPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref ScheduleEC2StartStop
Principal: events.amazonaws.com
SourceArn: !GetAtt StartEC2Rule.Arn
# Lambda 权限 - 允许 StopEC2Rule 调用
LambdaStopPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref ScheduleEC2StartStop
Principal: events.amazonaws.com
SourceArn: !GetAtt StopEC2Rule.Arn
Outputs:
LambdaFunctionArn:
Description: ARN of the Lambda function
Value: !GetAtt ScheduleEC2StartStop.Arn
LambdaExecutionRoleArn:
Description: ARN of the Lambda execution role
Value: !GetAtt LambdaExecutionRole.Arn
EventBridgeRoleArn:
Description: ARN of the EventBridge role
Value: !GetAtt EventBridgeInvokeLambdaRole.Arn
这个有啥用啊
@PlanktonQAQ #1 你不看第一句話?