EC2とRDSを自動停止!AWSコスト削減のためのスクリプトを紹介
AWSのEC2やRDS、検証環境でついつい止め忘れて、予想外の請求額にヒヤッとした経験はありませんか?
そんな悩みを解決するために、 EC2とRDSを自動で停止するスクリプト を作成しました!
このスクリプトを使えば、毎晩決まった時間にEC2とRDSを自動で停止してくれるので、コスト削減と運用効率の向上に繋がります。
スクリプトの特徴
- 簡単設定: Cloudshellで数コマンドを実行するだけで、全リージョンまたは特定リージョンにスクリプトをデプロイできます。
- 停止設定: 日本時間の午後9時から午前8時まで、1時間おきに停止スクリプトを実行し、起動中のEC2とRDSを確実に停止します。
- 停止除外設定: 特定のEC2やRDSを常時稼働させたい場合は、タグを設定するだけで停止対象から除外できます。
スクリプトの使い方
- ファイルをダウンロード: 以下のコマンドをCloudshellで実行して、スクリプトをダウンロードします。
wget https://iret.media/wp-content/uploads/2025/02/Automatic-Stop-System.zip - ファイルを解凍:
unzip Automatic-Stop-System.zip - ディレクトリに移動:
cd Automatic-Stop-System - スクリプトを実行:
- 全リージョンにデプロイする場合:
python Deploy_All_Region.py - 特定のリージョンにデプロイする場合:
python Deploy_Individual_Region.py - デプロイしたスタックを削除する場合:
python Delete_All_Region.py
- 全リージョンにデプロイする場合:
- 特定のEC2・RDSを停止から除外:
停止したくないEC2・RDSインスタンス・RDSクラスターに、以下のタグを追加します。- キー:
Always_Running_Reason - 値: 常時起動する理由を英語で記載 (例:
For monitoring)
- キー:
構成要素
このスクリプトは、以下の要素で構成されています。
- デプロイ用Pythonスクリプト: CloudFormationスタックの作成・更新、Lambda関数のデプロイを自動化します。
- CloudFormationテンプレート: Lambda関数、IAMロール、CloudWatch EventsルールなどのAWSリソースを定義します。
- Lambda関数: EC2インスタンス、RDSクラスタ、RDSインスタンスを停止する処理を実行します。
●デプロイ用Pythonスクリプトの説明
デプロイ用Pythonスクリプトのフローチャートは以下となります。S3からLambda関数をデプロイする場合、LambdaとS3は同一リージョンに存在する必要があるため、デプロイするリージョンごとにS3バケットを作成しています。
作成されたS3バケットのバケット名とキー情報はCfnのパラメータとしてデプロイ時に参照されます。

●CFnテンプレート及びLambda関数の説明
CFnテンプレートでは、各リージョンに「EventBridge Scheduler」と「Lambda関数」をデプロイするシンプルな構成としています。
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Automatic-Stop-System-Deploy'
Parameters:
TempS3BucketName:
Type: String
TempS3KeyName:
Type: String
Resources:
LambdaFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
Policies:
- PolicyName: !Sub '${AWS::Region}-Automatic-Stop-System-Lambda-Policy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:DescribeInstances
- ec2:StopInstances
- rds:DescribeDBClusters
- rds:StopDBCluster
- rds:DescribeDBInstances
- rds:StopDBInstance
Resource: '*'
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.lambda_handler
Code:
S3Bucket: !Sub '${TempS3BucketName}'
S3Key: !Sub '${TempS3KeyName}'
Role: !GetAtt LambdaFunctionRole.Arn
Runtime: python3.11
MemorySize: 128
Timeout: 600
LogGroupFirehosLambda:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/lambda/${LambdaFunction}'
RetentionInDays: 7
LambdaFunctionPermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt
- LambdaFunction
- Arn
Principal: "events.amazonaws.com"
SourceArn: !GetAtt
- StopScheduleEvent
- Arn
StopScheduleEvent:
Type: AWS::Events::Rule
Properties:
Description: ’Stop schedule event for lambda’
ScheduleExpression: 'cron(0 12-23 * * ? *)'
State: ENABLED
Targets:
- Arn: !GetAtt LambdaFunction.Arn
Id: ScheduleEvent1Target
DependsOn:
- LambdaFunction
Lambda関数では対象リソースに設定されたタグ情報を読み込み、除外対象のリソースの場合は処理をスキップさせます。
import boto3, json
from botocore.exceptions import ClientError
client_EC2 = boto3.client('ec2')
client_RDS = boto3.client('rds')
def Stop_EC2_instances():
try:
response = client_EC2.describe_instances(Filters=[{'Name': 'instance-state-name','Values': ['running']}])
except ClientError as e:
print('boto3 client error: %s' % e)
return
for reservation in response['Reservations']:
flag = False
for tag in reservation['Instances'][0]['Tags']:
if(tag['Key'] == 'Always_Running_Reason' and tag['Value']):
flag = True
if flag:
continue
try:
response = client_EC2.stop_instances(InstanceIds=[reservation['Instances'][0]['InstanceId']])
except ClientError as e:
print('boto3 client error: %s' % e)
continue
def Stop_DB_Cluster():
try:
response = client_RDS.describe_db_clusters()
except ClientError as e:
print('boto3 client error: %s' % e)
return
for DBCluster in response['DBClusters']:
if not DBCluster['Status'] == "available":
continue
if not "aurora" in DBCluster['Engine']:
continue
flag = False
for tag in DBCluster['TagList']:
if(tag['Key'] == 'Always_Running_Reason' and tag['Value']):
flag = True
if flag:
continue
try:
response = client_RDS.stop_db_cluster(DBClusterIdentifier=DBCluster['DBClusterIdentifier'])
except ClientError as e:
print('boto3 client error: %s' % e)
continue
def Stop_DB_Instance():
try:
response = client_RDS.describe_db_instances()
except ClientError as e:
print('boto3 client error: %s' % e)
return
for DBInstance in response['DBInstances']:
if not DBInstance['DBInstanceStatus'] == "available":
continue
if "aurora" in DBInstance['Engine']:
continue
flag = False
for tag in DBInstance['TagList']:
if(tag['Key'] == 'Always_Running_Reason' and tag['Value']):
flag = True
if flag:
continue
try:
response = client_RDS.stop_db_instance(DBInstanceIdentifier=DBInstance['DBInstanceIdentifier'])
except ClientError as e:
print('boto3 client error: %s' % e)
continue
def lambda_handler(event, content):
Stop_EC2_instances()
Stop_DB_Cluster()
Stop_DB_Instance()
return { 'statusCode': 200,'body': json.dumps('Excuted!')}
このスクリプトで、AWSコストの削減と運用効率の向上を実現しましょう!