🚫

EC2とRDSを週末に全部止める

2024/10/12に公開

今日のところは動作未確認。

Lambda関数(Python)

import os
import logging
import boto3

excluded_tag_key = os.getenv('EXCLUDED_TAG_KEY')
excluded_tag_value = os.getenv('EXCLUDED_TAG_VALUE')

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ec2_client = boto3.client('ec2')
rds_client = boto3.client('rds')


def stop_ec2_instances():
    response = ec2_client.describe_instances(Filters=[{'Name': 'instance-state-name', 'Values': ['running']}])
    instances = [instance for reservation in response['Reservations'] for instance in reservation['Instances']]

    for instance in instances:
        tags = instance.get('Tags', [])
        excluded = any(tag['Key'] == excluded_tag_key and tag['Value'] == excluded_tag_value for tag in tags)

        if not excluded:
            instance_id = instance['InstanceId']
            ec2_client.stop_instances(InstanceIds=[instance_id])
            logger.info(f'Stopped EC2 instance: {instance_id}')


def stop_rds_db_instances():
    response = rds_client.describe_db_instances()
    db_instances = response['DBInstances']

    for db_instance in db_instances:
        tags = db_instance.get('TagList', [])
        excluded = any(tag['Key'] == excluded_tag_key and tag['Value'] == excluded_tag_value for tag in tags)

        if not excluded:
            db_instance_identifier = db_instance['DBInstanceIdentifier']
            rds_client.stop_db_instance(DBInstanceIdentifier=db_instance_identifier)
            logger.info(f'Stopped DB instance: {db_instance_identifier}')


def lambda_handler(event, context):
    stop_ec2_instances()
    stop_rds_db_instances()

CloudFormationテンプレート

AWSTemplateFormatVersion: 2010-09-09
Description: Create a Lambda function to stop EC2 instances and RDS instances regularly

Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: LambdaExecutionRole
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: StopEC2RDSInstances
                Effect: Allow
                Action:
                  - ec2:DescribeInstances
                  - ec2:StopInstances
                  - rds:DescribeDBInstances
                  - rds:StopDBInstance
                Resource: '*'
              - Sid: PutLogs
                Effect: Allow
                Action: 
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: '*'

  StopInstances:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Runtime: python3.12
      Timeout: 30
      Environment:
        Variables:
          EXCLUDED_TAG_KEY: ExcludeFromAutoStop
          EXCLUDED_TAG_VALUE: True
      Code:
        ZipFile: |
          ##### ここにPythonコードを貼り付ける #####

  LambdaSchedule:
    Type: AWS::Events::Rule
    Properties:
      Description: Invoke StopInstances function at 20:00 on Friday
      ScheduleExpression: cron(0 11 ? * FRI *)
      State: ENABLED
      Targets:
        - Arn: !GetAtt StopInstances.Arn
          Id: LambdaSchedule

  LambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt StopInstances.Arn
      Principal: events.amazonaws.com
      SourceArn: !GetAtt LambdaSchedule.Arn

  LambdaLogGroup:
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${StopInstances}
      RetentionInDays: 30

あとがき

ChatGPT先生に教えてもらいましたが、二重の内包表記([instance for reservation in response['Reservations'] for instance in reservation['Instances']])を初めて知りました。しかしfor ...は前後逆の方が分かりやすかったような……

any(tag['Key'] == excluded_tag_key and tag['Value'] == excluded_tag_value for tag in tags)もChatGPT先生に教えてもらいましたが、自分では思いつかなかった気がします。

Discussion