AWS ChaliceでEC2とRDSを自動で起動停止するLambdaを作成

任意のタグが付いたEC2、RDSインスタンスをスケジュールで起動停止するためのLambdaです。

[Chalice](https://github.com/aws/chalice)で作ってあるのでハンドラーを変えればAPIエンドポイントから実行したりもできます。

以下実装です。

「AutoStartStop」タグが付けられたインスタンスを対象にして、平日の10時に起動、平日の19時に停止します。

```python:app.py
from chalice import Chalice
import logging
import os
import boto3

app = Chalice(app_name='schedule-startstop')
app.log.setLevel(logging.DEBUG)

env_dryrun = False if os.environ['DRYRUN'].upper() == 'FALSE' else True


def change_instances_state(event, state='Start', dryrun=True):

    rds = boto3.client('rds')
    ec2 = boto3.client('ec2')

    if state == 'Stop':  # 停止させる条件
        filter_ec2_instance_state = 'running'
        filter_rds_instance_state = 'available'
    else:  # 起動する条件
        filter_ec2_instance_state = filter_rds_instance_state = 'stopped'

    app.log.info("Search Target EC2 Instances.")

    # 対象のEC2インスタンスを取得
    target_ec2_instances = ec2.describe_instances(
        Filters=[
            {
                'Name': 'tag-key',
                'Values': ['AutoStartStop']
            },
            {
                'Name': 'instance-state-name',
                'Values': [filter_ec2_instance_state]
            }
        ]
    ).get(
        'Reservations', []
    )

    # 対象のEC2インスタンスのIDを取得
    target_ec2_instance_ids = []
    for instances in target_ec2_instances:
        for instance in instances['Instances']:
            target_ec2_instance_ids.append(
                instance['InstanceId'])

    if len(target_ec2_instance_ids) > 0:
        try:
            # 対象のEC2インスタンスを起動or停止
            app.log.info("%s EC2 Instances:%s" %
                         (state, ",".join(target_ec2_instance_ids)))
            if state == 'Stop':
                ec2.stop_instances(
                    InstanceIds=target_ec2_instance_ids, DryRun=dryrun)
            else:
                ec2.start_instances(
                    InstanceIds=target_ec2_instance_ids, DryRun=dryrun)

        except Exception as e:
            app.log.error(e)
    else:
        app.log.info("No Target EC2 Instances.")

    app.log.info("Search Target RDS Instances.")

    # 対象のRDSインスタンスを取得
    target_rds_instances = rds.describe_db_instances().get('DBInstances', [])

    for instance in target_rds_instances:

        # タグと状態でフィルタ
        instance_state = instance['DBInstanceStatus']
        instance_tags = rds.list_tags_for_resource(
            ResourceName=instance['DBInstanceArn']).get('TagList', [])
        has_tag = next(iter(filter(
            lambda tag: tag['Key'] == 'AutoStartStop', instance_tags)), None)

        if instance_state == filter_rds_instance_state and has_tag:
            try:
                # 対象のRDSインスタンスを起動
                instance_id = instance['DBInstanceIdentifier']
                app.log.info("%s RDS Instance:%s" % (state, instance_id))

                if state == 'Stop':
                    if not dryrun:
                        rds.stop_db_instance(
                            DBInstanceIdentifier=instance_id)
                    else:
                        app.log.info("Dry run stop_db_instance.")

                else:
                    if not dryrun:
                        rds.start_db_instance(
                            DBInstanceIdentifier=instance_id)
                    else:
                        app.log.info("Dry run start_db_instance.")

            except Exception as e:
                app.log.error(e)


@app.schedule('cron(0 1 ? * MON-FRI *)')  # 平日10時起動（UTC:1時）
def cron_start_handler(event):
    change_instances_state(event, 'Start', env_dryrun)


@app.schedule('cron(0 10 ? * MON-FRI *)')  # 平日19時停止（UTC:10時）
def cron_stop_handler(event):
    change_instances_state(event, 'Stop', env_dryrun)

```

```txt:requirements.txt
boto3
```

```json:.chalice/config.json
{
  "version": "2.0",
  "app_name": "schedule-startstop",
  "stages": {
    "dev": {
      "api_gateway_stage": "api",
      "lambda_timeout": 10,
      "environment_variables": {
        "DRYRUN": "True"
      }
    }
  }
}
```

このコードでは環境変数「DRYRUN」にTrueをセットして、実際に起動停止処理が走らないようにしています。
事前に狙ったインスタンスが処理対象になるか確認するのが目的です。
本当に起動停止処理を走らせる場合は「DRYRUN」にFalseをセットしてデプロイしてください。

後は処理対象にしたいEC2およびRDSのタグのKeyに（Valueは空でいいです）「AutoStartStop」を入れるだけで、時間が来たら自動で起動停止がかかります。

起動停止のタイミングを修正する場合はcron式を修正してください（UTCなので注意）。

また。Auroraを起動・停止する場合は`start/stop_db_instance`ではなく`start/stop_db_cluster`を使う必要があるので注意してください。

---
https://qiita.com/danishi/items/826ed9106a5bed5e2241