AWS Lambda(Python)でインスタンス起動停止をSchedule実行してみる

# はじめに

前々から作って見たかったのですが、Javaで作るとたいそうかなと思ったところ、
AWS LambdaがPythonで実行できるということで、書いたことないので、
チャレンジしようと思って、早３ヶ月。

いろんな人が書いてるなか、やってみました。

# 概要

DynamoDBにテーブルを作り、時間やインスタンスに設定したタグ情報、
開始か停止かを入れておく。

Lambda Function(Python)は、１０分ごとに動かして、
DynamoDBに入れてるデータの時間と一致したら、
そのタグ情報が一致するインスタンス全てに対して、
開始か停止かを実行します。

# 準備

DynamoDBにテーブル「EC2」を作ります。
Itemは４つ。「Id」「Time」「Tag」「StartStop」。
　1. Id        : シーケンス番号、順繰りになるようにしてください
　2. Time      : 時間、"%H:%M"形式、例："09:00"
　3. Tag       : インスタンスのタグ開始や停止したいタグを入れてください
　4. StartStop : 開始(start)か停止(stop)かを入れてください。

# Lambdaの設定

1. Runtimeは「Python2.7」
2. コードは後で・・・
3. Handlerは適当に
4. RoleはDynamoDBへの読み書きとEC2への開始/停止と参照があれば、OK
5. メモリは128MBでいけるとおもいます
6. タイムアウトは適当に（今回は10secにしてます）

# Lambda Function(Python)

個人的に苦労した箇所がいくつかあります。
1. "Filters=[{'Name':'tag-key','Values':[tableInfo['Tag']]}]"のとこ　
   "tableInfo['Tag']"の前後に"[]"がいることがわからなかった
2. "table.scan()['Items']"の"['Items']"がないといけない
   これがないとDynamoDBから取得したデータのItemが取れない
3. なんでかわかりませんが、"boto3.client('ec2')"でないといけない
   "boto3.resource('ec2')"だと動かなかった
4. "from boto3.dynamodb.conditions import Key, Attr"が必要みたい

で、コードは以下の通りです。 

```py
import boto3
import datetime
from boto3.dynamodb.conditions import Key, Attr

def lambda_handler(event, context):
    # create EC2 client
    ec2 = boto3.client('ec2')
    # get the time(%H:%M)
    timeInfo = datetime.datetime.now().strftime("%H:%M")
    tableInfoList = getTableInfoList(timeInfo)
    instanceList = getInstanceStartStopList(ec2, tableInfoList)
    executeInstanceStartStop(ec2, instanceList)

# get the list of data (Id, StartEnd, Tag, Time)
#     Id       : Sequence
#     StartEnd : "start" or "stop"
#     Tag      : Tag-key
#     Time     : %H:%M  ex) 09:00
# @param timeInfo
# @return list of data(Id, StartEnd, Tag, Time)
def getTableInfoList(timeInfo):
    dynamodb = boto3.resource('dynamodb')
    #Set TableName
    table    = dynamodb.Table('EC2')
    #Send the Query
    #Query Parameter
    #   @param TimeInfo : [hh:mm]
    #TimeInfo = "09:00"
    tableInfoList = table.scan(
            FilterExpression=Attr('Time').eq(timeInfo)
        )['Items']
    return tableInfoList

# get the list of data (Start or Stop Instance Ids)
# @param ec2
# @param tableInfoList
# @return instanceList Dictionaly('start', 'stop')
def getInstanceStartStopList(ec2, tableInfoList):
    startInstanceIdList = []
    stopInstanceIdList  = []
    for tableInfo in tableInfoList:
        for reservation in ec2.describe_instances(Filters=[{'Name':'tag-key','Values':[tableInfo['Tag']]}])["Reservations"]:
            for instance in reservation["Instances"]:
                if (tableInfo['StartStop'] == 'start'):
                    startInstanceIdList.append(instance["InstanceId"])
                elif (tableInfo['StartStop'] == 'stop'):
                    stopInstanceIdList.append(instance["InstanceId"])
    instanceList = {"start":startInstanceIdList, "stop":stopInstanceIdList}
    return instanceList

# Start of Stop Instances
# @param ec2
# @param instanceList
def executeInstanceStartStop(ec2, instanceList):
    startInstanceIdList = instanceList["start"]
    stopInstanceIdList  = instanceList["stop"]

    if not len(startInstanceIdList) == 0:
        ec2.start_instances(InstanceIds=startInstanceIdList)

    if not len(stopInstanceIdList) == 0:
        ec2.stop_instances(stopInstanceIdList)
```

トライアンドエラーでなんとかできました。

# これから

これからは、これに付随したものを作っていこうと思ってます。

1. DynamoDBのデータを登録/更新/削除/閲覧できるスクリプト
2. S3で静的HTML + JSでDynamoDBのデータを登録/更新/削除/閲覧
3. API Gateway + Lambda で リアルタイム実行

いつになるかはわかりませんが。。。