【EC2】Lambdaから時刻指定でEC2を自動起動・停止を時刻実現する

# EC2の自動起動・停止を実現したい！

https://qiita.com/YujiHamada3/items/3caccdaded527548432b

上記記事の改良版です。上記記事では自動起動・停止をhour単位でしか指定できず、一日に一度しか指定できませんでした。
それに加えて今回は
- 時刻まで指定できる（10分ごと）
- 複数時刻を指定できる
- 平日指定できる

の機能を追加しました。
今回はLambdaを何度も呼び出したくないので10分ごとにしましたが、Lambdaを毎分呼び出せば、分指定までできるようになります。

# EC2のタグの設定
![スクリーンショット 2023-01-30 16.56.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290902/8f66b619-198e-9555-a157-2849073dde15.png)

タグはこの様に設定します。
これだと
平日16時30分に起動して16時40分に停止する。
平日16時50分に起動して17時00分に停止する。
という流れになります。
時刻はカンマ区切りで記入して、0埋めで記載します `05:10` のような形です。

# Lambda

```python
import json
import boto3
from datetime import datetime, timedelta, timezone

# 10分単位で時間を丸める
# 例 12時8分 → 12時10分   5時58分 → 6時00分
def hour_rounder(t):
    return (t.replace(second=0, microsecond=0, minute=0, hour=t.hour) + timedelta(minutes=round(t.minute, -1)//10) * 10)

# 平日ならTrueを返し、週末（土日）ならFalseを返す
# 祝日は現在考慮していない
def is_weekday(t):
    weekday = t.weekday()
    if weekday == 5 or weekday == 6:
        return False
    return True
    
def lambda_handler(event, context):
    ec2 = boto3.client('ec2', region_name='ap-northeast-1')
    JST = timezone(timedelta(hours=+9), 'JST')
    now = datetime.now(JST)
    rounded_time = hour_rounder(now)
    is_weekday_flag = is_weekday(rounded_time)
    time = rounded_time.strftime("%H:%M")
    # 停止するインスタンスの処理
    custom_filter = [{
        'Name':'tag:auto_stop_at', 
        'Values': ["*" + time + "*"]
    }]
    	
    response = ec2.describe_instances(Filters=custom_filter)
    stop_instance_ids = []
    for instance_dic in response['Reservations']:
        stop_instance_ids.append(instance_dic['Instances'][0]['InstanceId'])
    
    if stop_instance_ids:
        ec2.stop_instances(InstanceIds=stop_instance_ids)
    
    # 開始するインスタンスの処理
    custom_filter = [{
        'Name':'tag:auto_start_at', 
        'Values': ["*" + time + "*"]
    }]
    custom_filter.append({
        'Name':'tag:auto_start_only_weekly', 
        'Values': ['true']
    })
    response = ec2.describe_instances(Filters=custom_filter)
    start_instance_ids = []
    for instance_dic in response['Reservations']:
        for tag in instance_dic['Instances'][0]['Tags']:
            # 平日の場合、平日設定のインスタンスのみ起動する
            if is_weekday_flag and tag['Key'] == 'auto_start_only_weekly' and tag['Value'] == 'true':
                start_instance_ids.append(instance_dic['Instances'][0]['InstanceId'])        
                
    if start_instance_ids:
        ec2.start_instances(InstanceIds=start_instance_ids)
    return {
        'statusCode': 200,
        'start_instance_ids': start_instance_ids,
        'stop_instance_ids': stop_instance_ids
    }
```

かんたんにコードを説明すると
`hour_rounder` という関数で、時間の10分を丸めています。
12時8分 → 12時10分   5時58分 → 6時00分
という形です。
これは考慮いらない気もしますが、毎度ピッタリの時刻にLambdaが呼ばれない可能性を考慮しています。

```py
for instance_dic in response['Reservations']:
        for tag in instance_dic['Instances'][0]['Tags']:
            # 平日の場合、平日設定のインスタンスのみ起動する
            if is_weekday_flag and tag['Key'] == 'auto_start_only_weekly' and tag['Value'] == 'true':
                start_instance_ids.append(instance_dic['Instances'][0]['InstanceId'])
```

次に上記部分です。自動起動を平日のみ設定のものの考慮です。
時間からインスタンスIDをフィルタリングしたあとに、平日のみ設定だったら曜日判定するという流れです。
このあたり、もう少し簡略化できるかもしれませんが、一旦これで問題ないのでこうしています。

## 問題点
現状のコードだと`05:00`のように0埋めで時間を入力しておく必要があります。カンマ区切りで入力できるようにした関係で、インスタンスIDを取る際に、`*5:00*`というワイルドカードを使っている関係で、0がないと`5:00`とすると15時にも動いてしまいます。
このあたりなんとかしたいのですが、一旦運用でカバーしています。