下記を参考にして、CloudWatch+Lambdaの組み合わせを用いて、タグ指定でインスタンスの開始、停止を実装してみた。
できるだけシンプルな仕組みで簡単にEC2の自動起動・停止を実現したい!
AWS Lambda+Python3で複数のRDSを起動停止
開発環境など業務時間内のみインスタンスを稼働させ、コストを最小化させたいときに役に立つ。
EC2インスタンスの場合
Lambda関数に設定する実行ロール
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "*"
}
]
}
Lambda関数(ec2.describe_instances()の戻りデータがそれなりに大きいので、タイムアウトをデフォルトの3秒より少し長めにしておかないとタイムアウトしてしまう)
import boto3
"""
Request 4Parameters
Region
Action(start or stop)
TagName
TagValue
"""
def lambda_handler(event, context):
region = event['Region']
action = event['Action']
tagname = event['TagName']
tagvalue = event['TagValue']
ec2 = boto3.client('ec2', region_name=region)
try:
reservations = ec2.describe_instances()
except Exception as e:
print(e)
return { 'status': -1 }
if action == 'start':
for instances in reservations["Reservations"]:
for instance in instances['Instances']:
tags = instance['Tags']
tag = next(iter(filter(lambda tag: tag['Key'] == tagname and tag['Value'] == tagvalue, tags)), None)
if tag:
if instance['State']['Name'] == 'stopped':
response = ec2.start_instances(InstanceIds=[instance["InstanceId"]])
print(instance['InstanceId'] + " is starting!")
else:
print(instance['InstanceId'] + " is already started!")
elif action == 'stop':
for instances in reservations["Reservations"]:
for instance in instances['Instances']:
tags = instance['Tags']
tag = next(iter(filter(lambda tag: tag['Key'] == tagname and tag['Value'] == tagvalue, tags)), None)
if tag:
if instance['State']['Name'] == 'running':
response = ec2.stop_instances(InstanceIds=[instance["InstanceId"]])
print(instance['InstanceId'] + " is stopping!")
else:
print(instance['InstanceId'] + " is already stopped!")
return { 'status': 0 }
CloudWatchEventで設定するターゲットの入力値(定数 (JSON テキスト))
{
"Action": "start",
"Region": "ap-northeast-1",
"TagName": "AutoStart",
"TagValue": "true"
}
上記を設定すると、東京リージョン(ap-northeast-1)にあるAutoStart=Trueとタグ設定されたEC2インスタンスが起動するようになります。Actionをstopとすると停止になります。
RDSインスタンスの場合
Lambda関数に設定する実行ロール
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"rds:DescribeDBInstances",
"rds:StopDBInstance",
"rds:StartDBInstance",
"rds:ListTagsForResource"
],
"Resource": "*"
}
]
}
Lambda関数
import boto3
"""
Request 4Parameters
Region
Action(start or stop)
TagName
TagValue
"""
def lambda_handler(event, context):
region = event['Region']
action = event['Action']
tagname = event['TagName']
tagvalue = event['TagValue']
rds = boto3.client('rds', region_name=region)
try:
dbs = rds.describe_db_instances()
except Exception as e:
print(e)
return { 'status': -1 }
if action == 'start':
for db in dbs['DBInstances']:
instance_arn = db['DBInstanceArn']
instance_tags = rds.list_tags_for_resource(ResourceName=instance_arn)
tags = instance_tags['TagList']
tag = next(iter(filter(lambda tag: tag['Key'] == tagname and tag['Value'] == tagvalue, tags)), None)
if tag:
if db['DBInstanceStatus'] == 'stopped':
response = rds.start_db_instance(DBInstanceIdentifier=db["DBInstanceIdentifier"])
print(db['DBInstanceIdentifier'] + " is starting!")
else:
print(db['DBInstanceIdentifier'] + " is already started!")
elif action == 'stop':
for db in dbs['DBInstances']:
instance_arn = db['DBInstanceArn']
instance_tags = rds.list_tags_for_resource(ResourceName=instance_arn)
tags = instance_tags['TagList']
tag = next(iter(filter(lambda tag: tag['Key'] == tagname and tag['Value'] == tagvalue, tags)), None)
if tag:
if db['DBInstanceStatus'] == 'available':
response = rds.stop_db_instance(DBInstanceIdentifier=db["DBInstanceIdentifier"])
print(db['DBInstanceIdentifier'] + " is stopping!")
else:
print(db['DBInstanceIdentifier'] + " is already stopped!")
return { 'status': 0 }
参考)EC2とRDSのAPIに差異がある点に注意が必要。
例:EC2のstart_instancesは、リストで渡せる(Type: Array of strings)一方で、RDSのstart-db-instanceは、単一のIDしか渡せない(Type: String/リストで渡せない)。