AWSのEC2を自動停止するLambdaを作る記録
実行権限を作成します。
- 「IAMのポリシーを作成する」を参考にポリシーを作成します。
- [JSON]タブで設定するポリシーは以下になります。
- 「IAMのロールを作成する」を参考にロールを作成します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "logs:CreateLogStream", "ec2:DescribeInstances", "ec2:StopInstances", "logs:CreateLogGroup", "logs:PutLogEvents" ], "Resource": "*" } ] }
自動停止の対象と停止時間を設定できるようにするためにEC2インスタンスにAutoStopタグを追加します。
「自動停止の対象と停止時間を設定できるようにするためにRDSにAutoStopタグを追加します。」を参考にEC2インスタンスにAutoStopタグを追加します。
Lambda関数を作成します。
- AWSのコンソールにある[Lambda] > [関数の作成]ボタンで画面を開きます。
- 必要な項目を入力後に[関数の作成]ボタンで関数を作成します。
- オプション : [一から作成]
- 関数名 : 任意の関数名
- ランタイム : Python3.8
- 実行ロール : 既存のロールを使用する
- 作成したロールを選択します。
- 「Lambda関数を実行するトリガーを作成します。」を参考にトリガーを作成します。
関数を実装します。
- [関数コード] > [lambda_function.py]に以下のコードを張り付けて[保存]ボタンで保存します。
- 定数の[REGION_NAME]には停止対象のEC2インスタンスがあるリージョンを指定してください。
- 「関数を動かしてみます。」を参考にエラーが無くなるまで関数を動かして修正を繰り返します。
# -*- coding: utf-8 -*- from __future__ import print_function import sys import json import boto3 from datetime import datetime, timedelta, timezone, date import re # 停止対象のインスタンスがあるリージョンを設定する REGION_NAME = 'ap-northeast-1' DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ' print("Loading function") def get_instance_list(ec2): """ 起動中かつ[AutoStop]タグが設定されたインスタンスを取得する処理. Parameters ---------- ec2 """ instances = ec2.describe_instances( Filters=[ {"Name": "instance-state-name", "Values": ["running"]}, {'Name': 'tag-key', 'Values': ['AutoStop']} ] )['Reservations'] new_instances = [] for instance in instances: new_instances.append(instance['Instances'][0]) return new_instances def get_auto_stop_tag(instance): """ AutoStopタグに設定された時間文字列を取得する. """ result = '' val = '' tag_list = instance['Tags'] tag = next(iter(filter(lambda tag: tag['Key'] == 'AutoStop' and (tag['Value'] is not None and len(tag['Value']) != 0), tag_list)), None) if tag: val = tag["Value"] if not val: print('タグに時間が指定されていません。') elif not re.fullmatch('\d{1,2}:\d{1,2}', val): print('タグに設定されている文字列が時間(hh:mm)ではありません。' + val) else: print('タグに設定された時間は、' + val + 'です。') result = val return result def get_utc_time(jst_time_string): """ HH:MM形式のJST時間文字列をUTC時間にして取得する. """ # 時間文字列を{時間, 分}の配列にする jst_time = jst_time_string.split(':'); today = datetime.now() # 設定時刻が8:59以前である場合、UTC変換時に前日にならないよう日付を1日進めておく if int(jst_time[0]) < 9: today = today + timedelta(day=1) jst_time = datetime(today.year, today.month, today.day, int(jst_time[0]), int(jst_time[1])) utc_time = jst_time + timedelta(hours=-9) return utc_time def is_stop(event_time, auto_stop_time): """ インスタンスを停止するかを判定する """ # 設定時間の5分前 from_time = auto_stop_time + timedelta(minutes=-5) # 設定時間の5分後 to_time = auto_stop_time + timedelta(minutes=5) print("イベント時間(" + event_time.strftime(DATETIME_FORMAT) + ")が、" + \ from_time.strftime(DATETIME_FORMAT) + "から" + to_time.strftime(DATETIME_FORMAT) + "の間であればインスタンスを停止します。") # AutoStopタグに指定された時刻の前後5分以内であればインスタンス停止 is_stop = from_time <= event_time and event_time <= to_time return is_stop def lambda_handler(event, context): print("Received event: " + json.dumps(event, indent=2)) ec2 = boto3.client('ec2', REGION_NAME) instances = get_instance_list(ec2) if len(instances) == 0: print('処理対象のインスタンスはありません。') return 0 # インスタンス終了処理 for instance in instances: print(instance['InstanceId'] + 'は起動中です。') # [AutoStop]タグに設定された時間文字列を取得する tag_time_string = get_auto_stop_tag(instance) if tag_time_string: # [AutoStop]タグに設定された時間文字列をUTC時間にする auto_stop_time = get_utc_time(tag_time_string) print('タグに設定された時間(UTC)は、' + auto_stop_time.strftime(DATETIME_FORMAT)) # 引数のイベント時間を取得 event_time = datetime.strptime(event["time"], DATETIME_FORMAT) if is_stop(event_time, auto_stop_time): ec2.stop_instances(InstanceIds=[instance['InstanceId']]) print(instance['InstanceId'] + "を停止しました。") return 0
使用した関数のドキュメント
- describe_instances : 辞書形式でインスタンスを取得する
- stop_instances : 指定したインスタンスを停止する
失敗したこと
An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
- 原因 : describe_instances関数を実行する権限がなかった。
- 対応 : ポリシーの[Action]に「ec2:DescribeInstances」を追加しました。
Response: { "errorMessage": "An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.", "errorType": "ClientError", "stackTrace": [ " File \"/var/task/lambda_function.py\", line 34, in lambda_handler\n instance_list = get_instance_list()\n", " File \"/var/task/lambda_function.py\", line 19, in get_instance_list\n instances = ec2.describe_instances(\n",