LineからEC2を操作する

AWS EC2上で自作のアプリケーションを回しているのですが、実行、停止を頻繁に繰り返すことがあり、EC2に入っていちいち操作させるのが結構めんどくさかったりします。
そこで、LineからEC2を操作して、プログラムの実行停止をできるようにしてみます。


## 全体像
以下のフローでLineからEC2を操作します。
1. Line Messaging APIからAPI GatewayへPOSTリクエストを送信
2. API Gatewayをトリガにして、Lambda関数が実行
3. Lambda関数からEC2インスタンスが起動or停止される
4. 起動時には設定したスクリプトが自動実行される
![スクリーンショット 2020-09-28 0.10.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/63dbbeef-efc4-d55c-b2cf-0dc35c0de526.png)



## 作成手順
1. EC2インスタンスの作成
1. IAMポリシーの作成
1. Lambda関数の設定
1. API Gatewayの設定
1. Line Messaging APIの設定
1. EC2起動時実行スクリプトの設定

## 1. EC2インスタンスの作成
プログラムを実行するためのEC2インスタンスを作成しておきます。(作成手順は省略)
後述のLambda関数で利用するため、作成したEC2のインスタンスIDをメモしておきます。

## 2. Lambda用IAMロールの作成
今回はLambdaの関数からEC2を操作するため、LambdaにEC2インスタンスの起動停止を行うための、IAMロールを付与してあげます。
[こちら](https://aws.amazon.com/jp/premiumsupport/knowledge-center/start-stop-lambda-cloudwatch/)のAWS公式マニュアルに従います。

**【IAMポリシーの作成】**
IAM -> ポリシー -> ポリシーの作成 -> JSON
でエディタに下記のJSONを記載します。
ポリシー名はec2StartStopPolicyとします。

<details><summary>ポリシードキュメント</summary><div>

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

</div></details>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/f3b80f87-7665-7168-b06e-ca2a3e1afc59.png" width=60%>



**【IAMロールの作成】**
ロール -> ロールの作成　を選択し、エンティティの種類にAWSサービス、ユースケースにLambdaを選択します。
アクセス権限ポリシーで、上で作成したポリシーを選択し、ロール名をec2StartStopRoleとして、ロールを作成します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/6e48118a-af36-cb55-71c3-8695b5e7dbf8.png" width=70%>


## 3. Lambda関数の作成
EC2を起動、停止するためのLambda関数を作成します。
**【関数の作成】**
Lineから「起動」「停止」というメッセージを受け取って、対応するアクションを行う仕様とします。

Lambda -> 関数の作成 -> 一から作成 -> ランタイムにPython3.8を選択 -> 関数の作成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/4aeb460f-951f-3225-5734-825da59cf09e.png" width=60%>

配置されたLambda関数の関数コード内、lambda_function.pyに下記のコードを記載します。
コード内のregionとinstancesには対象のEC2インスタンスの内容に置き換えてください。


<details><summary>Lambda関数</summary><div>

```python:lambda_function.py
import json
import urllib.request, urllib.parse
import boto3
import os
region = '**リージョン**'
instances = ['**インスタンスID**']
ec2 = boto3.client('ec2', region_name=region)


def start_ec2():
    ec2.start_instances(InstanceIds=instances)

def stop_ec2():
    ec2.stop_instances(InstanceIds=instances)
    

def lambda_handler(event, context):
    # TODO implement
    text = event['events'][0]['message']['text']
    if text == '起動':
        start_ec2()
    elif text == '停止':
        stop_ec2()
    else:
        pass
        
    print("Line Message is {}".format(text))
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

```

</div></details>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/c0323f3b-27e9-59b1-1a1b-1b3a8ad9c65b.png" width=60%>

**【IAMロールを付与】**
作成したIAMロールをLambda関数に付与します。
Lambdaのアクセス権限タブで実行ロールの編集をクリックし、先ほど作成したIAMロールを選択して保存します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/e86249f5-5ff5-916c-22b7-412e1b5a2633.png" width=60%>

## 4. API Gatewayの設定
作成したLambdaのデザイナー上から、トリガーを追加を選択し、API Gatewayをトリガーとして追加します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/80357292-2315-3cee-8f11-3f268eb17b5b.png" width=60%>

作成したAPI GatewayのAPIエンドポイントをメモしておきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/7c14c210-dceb-af52-6f2c-785b29f905d4.png" width=60%>


今回は、LineからのPOSTリクエストに応答して、Lambda関数をトリガするようにします。
API Gateway画面から、アクション -> メソッドを追加 -> POSTを選択 -> 作成したLambda関数を指定して、POSTリクエスト用のメソッドを作成します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/f2ead5d4-dac7-8227-4ac6-9cfbbeee8f43.png" width=60%>

最後に、アクション->APIのデプロイ を選択し、デプロイします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/4fe170f2-68ba-0399-355a-1f6d1749ecaa.png" width=60%>

## 4. Line Messaging APIの設定
API GatewayにPOSTリクエストを送るためのLine Messaging APIを作成します。
Line Developers出ない方は新規登録をしましょう。

**【チャネルの作成】**
[Line Developers](https://developers.line.biz/ja/)にログインし、新規Providerを作成します。
Channelsに「Create a MEssaging API channel」を選択し、Channel nameなど適当に埋めて、Channelを作成します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/07de3255-d04b-009a-4086-92af639f1c07.png" width=60%>


**【Webhook URLの設定】**
Messaging APIの設定で、Webhook URL選択し、作成したAPI GatewayのAPIエンドポイントを記載します。


**【動作確認】**
Messaging API設定画面で表示されるQRコードを読み取り、Lineで友達追加します。
このチャネルで、「起動」または「停止」を送って、実際にEC2インスタンスが起動、停止することを確かめます。
起動、停止を送ると、、、
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/7a77becc-3451-6c58-931e-6c27aa26f7de.png" height=30%>

EC2インスタンスに反映!
![スクリーンショット 2020-09-27 18.40.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501910/c81ff7b0-525b-cb2a-e718-94718fc7bccd.png)

### 5. 起動時設定
最後に、EC2が起動したときに特定のスクリプトを実行できるようにします。
今回は起動・停止時にメッセージを出力するだけの、mytestというスクリプトを登録します。

```sh:mytest
#!/bin/sh
# chkconfig: 2345 99 10
# description: test shell

case "$1" in
 start)
       echo "start!" > /tmp/start.txt
       ;;
 stop)
       echo "stop!" > /tmp/stop.txt
       ;;
  *) break ;;
esac
```

下記のコマンドでchkconfigに登録すると、スクリプトが実行されるようになります。

```
$ chkconfig --add mytest

$ chkconfig mytest on
```


## まとめ
以上で、好きなタイミングでLineからEC2を起動させプログラムを自動起動できるようになりました！外出中でも気軽にEC2を操作できるのが嬉しいですね。