EC2インスタンス起動/停止をLambdaで自動化してみた

## はじめに
Lambdaを使ったEC2インスタンスの自動起動/自動停止の環境を構築したので紹介します
きっかけは業務でcrontabファイルを使用しており`Lambdaに移行できるのでは？`と思い実装しました
一回構築してしまえばめんどくさい手作業が自動化できますしヒューマンエラーも防げるのでおすすめです

## 対象読者
* 個人アプリのサーバー費用節約したい人
* EC2は触ったことあるけどLambdaは触ったことがない人
* 自動化に興味のある人

## 前提
* AWSアカウント作成済み
* EC2インスタンス作成済み

## 実装の概要
**実装時間: 30~60分**
1. IAMロールの作成
2. LambdaでEC2インスタンスの起動・停止処理を実装
3. Amazon EventBridgeで Lambdaを実行する時間を指定する  

## IAMロールの作成
AWSにログインして `IAM > ポリシー > ポリシーを作成 > JSONタブ`に進むとポリシー作成画面が表示されます
![スクリーンショット 2021-08-23 9.16.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/f139972a-7cb9-01a8-74ae-a1e9fa0a1a00.png)

そこに今回の実装に必要な権限が書かれた以下JSONテキストを貼り付けてください

```JSON
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:Describe*",
                "ec2:Start*",
                "ec2:Stop*"
            ],
            "Resource": "*"
        }
    ]
}
```

記述したらポリシーの名前を`lambda_for_start_stop_instance`にしてポリシーを作成しましょう。

ポリシーが作成できたら`IAM > ロール > ロールを作成`と進み以下の項目を選択し`次のステップ:アクセス権限`ボタンを押します

**信頼されたエンティティの種類を選択:** AWSサービス
**ユースケースの選択:** Lambda
![スクリーンショット 2021-08-23 9.24.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/ab2106f6-ef24-ea9c-9748-2ceb627cec83.png)

次に`ポリシーのフィルタ`に先ほど作成した`lambda_for_start_stop_instance`を入力して検索したらそれを選択し`次のステップ:タグ`ボタンを押します
![スクリーンショット 2021-08-23 9.27.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/7db80383-5c1f-207f-747b-a0c036c3e7ca.png)

タグは入力せず`次のステップ:確認`ボタンを押します
次にロール名に`start_stop_instance_lambda`を入力し`ロールを作成`ボタンを押してロールを作成します

ロールが作成できたら作成したロール詳細画面で`ポリシーをアタッチする > 作成したポリシーを選択 > ポリシーのアタッチ`の順に進めてロールにポリシーをアタッチします

これでLambda用のロールが完成しました


## Lambda関数の作成
ロールが作成できたらLambda関数を作成していきましょう
`Lambda > 関数 > 関数の作成`の順番で進むとLamba関数の作成画面が表示されます
![スクリーンショット 2021-08-21 0.16.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/da72da91-1178-94a2-771a-e2230ca94722.png)

ここで以下の通り入力/選択しましょう
**オプション:** 一から作成
**関数名:** start_stop_ec2_instance
**ランタイム:** python3.8
**ロール:**　作成したロールを選択

入力できたら`関数の作成`ボタンを押してLambda関数を作成します
![スクリーンショット 2021-08-21 0.22.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/0cad06d4-ebf0-d9c3-9cfb-36fae4462f6f.png)


## 起動・停止処理の実装
それではEC2の起動・停止処理を書いていきます
先ほど作成したLambda関数の`「コード」タブ` > `lambda_function.pyファイル`を選択するとコードの中身が表示されます
![スクリーンショット 2021-08-21 0.41.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/f4d5820d-7cd5-35ff-3cc3-f96d442f6387.png)
ここに以下のコードを貼り付けてください

```python
import boto3
import os
region = 'ap-northeast-1'
instances = [os.environ['INSTANCE_ID']]

def lambda_handler(event, context):
    ec2 = boto3.client('ec2', region_name=region)
    action = event["Action"]
    response = ec2.describe_instances(InstanceIds=instances)
    ec2_status = response['Reservations'][0]['Instances'][0]['State']['Name']
    print(instances[0] + ' instance is ' + ec2_status + ' now.')
    
    if action == "Start":
        ec2.start_instances(InstanceIds=instances)
        print('started your instance: ' + str(instances[0]))
    elif action == "Stop":
        ec2.stop_instances(InstanceIds=instances)
        print('stopped your instance: ' + str(instances[0]))
    else:
        print('Lamdba function could not be executed.')
```

コードを貼り付けたら3行目の`region = 'ap-northeast-1'`の部分を起動/停止したいインスタンスがあるリージョンの名前に変更してください
筆者は東京リージョンEC2インスタンスがあるので`ap-northeast-1`にしています
変更できたらタブの横にある`Deploy`ボタンを押して変更を保存します
**※「Deploy」ボタンを押さないとコードの変更が反映されないので注意**

### コード概要
特徴は２つです
1つ目は**Lambda関数に送られてくるリクエストによって起動と停止の処理を条件分岐させています**
理由はCloudWatchでLambdaを監視する場合、Lambda関数の数は少ない方が費用を削減できます（CloudWatchでの監視は別途紹介します）

2つ目は**EC2インスタンスのidを記述する箇所は環境変数にして汎用性の高いコードにしてあります**
そうしておくと他のインスタンス自動化したい！となった場合Lambda関数を複製して環境変数の部分だけ書き換えれば簡単に実装できます
ちなみに私はこのLambda関数を10個ぐらい複製しました

### 環境変数の設定

`「設定」タブ` > `環境変数` > `編集` > `環境変数の追加`と進み環境変数の編集画面に進みます
![スクリーンショット 2021-08-21 1.14.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/4ba73a1d-1d8a-9572-9046-e4d8cb178912.png)

キーと値には次の値を入力しましょう
**キー：** INSTANCE_ID
**値:** i-xxxxxxxx （起動・停止したいEC2インスタンスのid）

入力できたら保存ボタンを押して環境変数を追加しましょう

これでLambda関数の設定は完了です


## Lambdaのテスト方法
完成したLambda関数でインスタンスが起動・停止できるかテストしてみましょう

まずはあらかじめEC2インスタンスを起動させておきます

そのあとLambda関数画面で`「テスト」タブ` を押すとLambda関数に投げるリクエストを作成できます
停止のリクエストを投げたいので以下の項目を選択/入力します

テンプレート: hello_world
**名前:** stop_request
**JSON:**  {"Action": "Stop"} 

入力したら`変更を保存 > テスト`ボタンを押しましょう
すると`実行結果:成功`の表示がされるはずです
![スクリーンショット 2021-08-23 18.04.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/43db702b-0f87-c026-bf10-5cf2de78bc28.png)

インスタンスの状態を確認すると`起動中`から`停止済み`に変更されているはずです

## トリガーの設定
それでは完成したLambda関数を`いつ起動・停止するのか`設定していきましょう
`Amazon EventBridge` > `イベント` > `ルール` > `ルールを作成`を押します
![スクリーンショット 2021-08-21 1.36.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603209/2ac84736-691f-4829-2b26-b2e280404f57.png)

今回はEC２インスタンスを**毎日9：00に起動**、**毎日21:00に停止** したいとして実装していきます

次の値を入力します
**名前:** start__ec2_instance_at_0900
**説明:** 毎日9：00に{"Action": "Start"}リクエストを投げるルール
**イベントパターン：**　スケジュール
**cron式：** cron(0 0 ? * * *) 

**ターゲット:** Lambda関数
**機能:** start_stop_ec2_instance(作成したLambda関数を選択)
**入力の設定:** 定数 (JSON テキスト) を選択
**JSON テキスト:**

入力が終わったら作成ボタンを押します
作成できたら今度は次の値を入力して作成ボタンを押します。
**名前:** stop__ec2_instance_at_0900
**説明:** 毎日21：00に{"Action": "Stop"}リクエストを投げるルール
**イベントパターン：**　スケジュール
**cron式：** cron(0 12 ? * * *) 

**ターゲット:** Lambda関数
**機能:** start_stop_ec2_instance(作成したLambda関数を選択)
**入力の設定:** 定数 (JSON テキスト) を選択
**JSON テキスト:**


これで決まった時間にEC2インスタンスが自動起動/停止するようになりました

## 最後に
最後まで本記事を読んでくださりありがとうございました
後日、本記事の内容をTerraformで構築する方法やCloundWatchで監視してSlackに通知する方法も投稿します
ご興味ありましたらお読みください