logo NodeSeekbeta

AWS CloudFormation 模板 EC2 定时开关机

为啥,因为停止 EC2 实例后跟终止 EC2 实例一样不计费,但弹性 IP 地址和 EBS 卷还是要计费的

使用方法

注意在与 EC2 实例同区域下,上传模板至 CloudFormation,此处模板文件为 ScheduleEC2StartStop_Temp.yaml,内容在最下

image

修改以下内容

  1. TargetEC2InstanceIds: 填写实例 ID,多个实例 ID 以“,”间隔
  2. StartScheduleCron: 填写启动实例的 cron 表达式,UTC 时间
  3. StopScheduleCron: 填写停止实例的 cron 表达式,UTC 时间

https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html

image

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

image

然后创建

image

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

image

image

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

image

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

image

若想清除所有已创建资源,直接删除堆栈即可

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

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有72033位seeker

🎉欢迎新用户🎉