APIGateway＋Lambdaで、コンソール画面を作成し、EC2の起動・停止・スペック変更を操作する①

# はじめに
Ec2の操作は、AWSコンソールから操作しますが、ログインすることが面倒だったりやチームで開発する場合、EC2の起動・停止のためだけにIAMユーザーを作成するのは手間だったりするため、S3に配置したhtmlでEC2を操作できる仕組みを作成しました。
ただし、メンバー以外に操作されないよう、S3にはCloudfront OAI、CloudFrontとApiGatewayにはBasic認証をかけてます。

# 構成図
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/b04e124a-0b85-a4d9-e516-06d85fdbdaf8.png">

S3にhtmlを配置して、CloudFrontで配信し、S3からアクセスできないよう制限します。
そして、html上でEC2を操作できるようLambdaを使用します。

# 完成ページ
EC2の現在のスペックと起動有無が非同期で表示され、ワンクリックでEC2を起動・停止・スペック変更できます。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/89c540fd-603a-70d5-0e8c-b69e3017843a.png">


# 流れ
①Lambda用のIAMロールを作成
②lambdaを4つ作成
③ApiGatewayを設定
④htmlを作成
⑤htmlをS3にアップロード
⑥Cloudfrontからページを読み込むように設定
⑦WebページにBasic認証を有効化する

⑧htmlから起動などのLambda実行後、htmlページにリダイレクトする
*⑧は次回に行います。

# Lambda用のIAMロールを作成

インスタンスの起動・停止・スペック変更をLambdaに許可するため、IAMロールを作成します。

1. ポリシーの作成
・下記のポリシーをコピペ
・名前を入力し、ポリシー作成

2. ロールの作成
・信頼されたエンティティの種類を選択は、AWSサービス
・ユースケースの選択はLambda
・先程作成したポリシーを付与し、ロールを作成

```json:IaRole
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:StartInstances",
                "ec2:StopInstances"
            ],
            "Resource": "arn:aws:ec2:*:*:instance/*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "ec2:ModifyInstanceAttribute",
                "ec2:DescribeInstances",
                "ec2:ReportInstanceStatus"
            ],
            "Resource": "*"
        }
    ]
}
```

Action一覧
起動：ec2:StartInstances
停止：ec2:StopInstances
スペック変更：ec2:ModifyInstanceAttribute
EC2情報を取得：ec2:DescribeInstances
EC2ステータス取得：ec2:ReportInstanceStatus


# lambdaを4つ作成

1. 関数の作成
・一から作成
・関数名を入力（StartEC2,StopEC2,SpecChangeEC2,CheckEC2）
・ランタイムはpython3.8
・アクセス権限は、既存のロールで、先程作成したロールを選択し、lambda作成

2. コード編集
下記のコードをコピペして、デプロイをします
```python:StartEC2
import boto3
import os

def lambda_handler(event, context):
    ec2 = boto3.client('ec2', region_name=os.environ['region'])
    ec2.start_instances(InstanceIds=[event['instance_id']])
    return {
        "Instance Started": event['instance_id']
    }
```

```python:StopEC2
import boto3
import os

def lambda_handler(event, context):
    ec2 = boto3.client('ec2', region_name=os.environ['region'])
    ec2.stop_instances(InstanceIds=[event['instance_id']])
    return {
        "Instance Stopped": event['instance_id']
    }
```

```python:SpecChangeEC2
import boto3
import os

def lambda_handler(event, context):
    NEW_INSTANCE_SIZE = event['instace_spec'] 
    ec2 = boto3.client('ec2', region_name=os.environ['region'])
    
    # stop
    ec2.stop_instances(InstanceIds=[event['instance_id']])
    waiter=ec2.get_waiter('instance_stopped')
    waiter.wait(InstanceIds=[event['instance_id']])
    
    #change
    ec2.modify_instance_attribute(InstanceId=event['instance_id'],
                                  Attribute='instanceType',
                                  Value=NEW_INSTANCE_SIZE)
    
    #start
    ec2.start_instances(InstanceIds=[event['instance_id']])
    
    return {
        "Instance Change": event['instace_spec']
    }
```
SpecChangeEC2は、変更前にインスタンスを停止させる必要があり、停止するまでに数秒かかるため、waitを使用しています。


```python:CheckEC2
import boto3
import os

def lambda_handler(event, context):
    list = []
    ec2 = boto3.client('ec2', region_name=os.environ['region'])

    ec2_data = ec2.describe_instances()
    for reservation in ec2_data['Reservations']:
        for instance in reservation['Instances']:
            ec2_tags = dict([(tag['Key'], tag['Value']) for tag in instance['Tags']])
            ec2_name = ec2_tags.get('Name', 'unknown')
            if ec2_name == "":
                ec2_name = "unknown"

            data = {"id": instance['InstanceId'], "spec": instance['InstanceType'], "status": instance['State']['Name'], "name": ec2_name} 
            list.append(data)

    return list
```
`list.append(data)`で、リージョン内のすべてのEC2をjson形式で取得できます。

<br>
3. 設定から環境変数を変更

環境変数は、対象のインスタンスのリージョンを設定する。今回は、東京リージョンを設定します。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/07af50d6-9ea5-9d79-dfa2-fabe1c004b52.png">
4. テスト
一般設定のタイムアウトをデフォルト値3秒であり、タイムアウトでエラーになるのを防ぐため、6秒に変更します。
SpecChangeEC2は、時間がかかるため、60秒に設定します。
instance_idの値は、操作したいEC2のうち一つです。
以下入力し、テストし、成功することを確認します。

```json
{
  "instace_spec": "t2.micro",
  "instance_id": "i-XXXXXXXX"
}
```
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/135e39da-2663-ff45-94bf-addf04df1e06.png">
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/27897985-5fdc-1708-da37-58d95bb0ebd5.png">
<br>
また、CheckEC2は、json形式で取得できていることがわかります。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/5e547d80-deb7-0b2d-78a3-f3fac48d03b5.png">



# ApiGatewayを設定
API Gatewayは,Web APIを提供するサービスであり、APIの入り口といえます。APIがリクエストを受けた後の処理は、先程作成したlambda関数を実行します。
例えば、`~/prod/start`というURLをリクエストすると、StartEC2のLambda関数が実行されます。
ステージは prod とします (後述)

|Lambda関数  |API リソース  |API メソッド  |URL  |
|---|---|---|---|
|StartEC2  |start  |GET  |~/prod/start  |
|StopEC2  |stop  |GET  |~/prod/stop  |
|SpecChangeEC2 |specchange  |GET  |~/prod/specchange  |
|CheckEC2  |check  |GET  |~/prod/check  |

①REST APIの構築をクリックします
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/9e06e840-8a63-56ac-8813-9285a6c92271.png">
②以下の設定で作成します
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/10626c2d-6444-8891-451e-5cc6fa412929.png">
③リソースを作成します
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/c733dac5-dd24-6c5d-44bc-89f9d146c347.png">
④リソース名を入力し、`API Gateway CORS を有効にする`にチェックし、リソースを作成をクリックします
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/54b60852-2069-6a8e-67bc-ab64be131419.png">
⑤GETメソッドを作成します
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/a81d60ad-a54e-0060-ff5c-ea5adfbbdac3.png">
先程作成したLambda関数を紐付けて作成します。optionメソッドは削除してください
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/e219effd-22c0-f8e7-ccd9-7f09564f4e7f.png">
⑥GETを選択し、統合リクエストをクリックします
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/dceb9d11-d4da-7be5-4ce1-d9760e6a5f98.png">
⑦マッピングテンプレートで、application/jsonという名前で追加し、以下の通りにテンプレートを入力し、保存します。
パススルー動作の変更のポップアップが表示されるので、統合を保護をクリックします
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/08c67f46-1642-70f7-df84-8e7ca1df4096.png">

```JSON
{
  "instance_id" : "$input.params('instance_id')",
  "instace_spec" : "$input.params('instace_spec')"
}
```

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/d92321d2-82fb-9b63-02c4-7907e882f797.png">

⑧テストをクリックします。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/dceb9d11-d4da-7be5-4ce1-d9760e6a5f98.png">

⑨`start、stop`メソッドの場合、{start｝のクエリ文字に`instance_id=i-XXXXXXXXXX`を入力し、テスト実行すると、対象のEC2が起動・停止します。
`check`メソッドの場合は、nullを入力します。
`specchange`メソッドの場合は、`instance_id=i-xxxxxx&instace_spec=t2.micro`を入力します
specchangeメソッドのテストは、`{"message": "Endpoint request timed out"}`と表示され、タイムオーバーになる可能性があります。
理由は、ApiGatewayのタイムアウト仕様は最大29秒（デフォルト）までの制限があるため、停止→スペック変更→起動までに29秒を超えるとエラーが出るというわけです。
ただし、タイムオーバーと表示されてもスペック変更はされているため、このままにしておきます。
エラーを表示したくない場合、EC2を停止してから、スペック変更テストを行うとタイムアウトになりません。

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/692e571f-80cb-37dc-ec33-7824b14c8204.png">

⑩テストに問題がなければ、リソースを③〜⑨手順に従い`start`,`stop`,`specchange`,`check`の4つ作成します
`check`リソースのみCORS有効化をクリックし、`CORSを有効にして既存のCORSヘッダーを置換`をクリックします
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/1660fca5-9cf6-a50d-a078-3fdbc78b27e0.png">
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/0421b2e3-4154-752b-eea6-7b4da34ac9e8.png">



⑪APIをデプロイすると（ステージ名は`prod`）、URLの呼び出しが表示されます。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/f185c999-776f-9d91-f772-9d3dc5076ed4.png">
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/9964945a-5c37-2432-5e97-e264a1e513c9.png">

呼び出しURLの`prod`の後ろに、`start?instance_id=インスタンスID`を加え、URLに遷移すると対象のEC2が起動します
例↓
https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/start?instance_id=i-xxxxxx

`aaaaaa`は次のhtml作成のときに使用します。

# htmlを作成

htmlを以下のように作成します
`aaaaaa`と`xxxxxx`は、自身のものを埋め込むこと。
aaaaaaは、先程の呼び出しURLの独自のもの、xxxxxxはインスタンスIDになります。
今回は、EC2を2つ操作します。

```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>EC2Control</title>
</head>
  <body>
    <h1 id="ec2-name-1">読込中（name）</h1>
    <h2 id="ec2-id-1">読込中（id）</h2>
    <p>ステータス</p>
    <ul>
        <li id="status1-EC2-1">起動・停止・スペック変更中</li>
        <li id="status2-EC2-1">読込中（スペック）</li>
    </ul>
    <p>アクション</p>
    <ul>
        <li id="action-start-EC2-1" onclick="return clickEventStart()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/start?instance_id=i-xxxxxx">起動する</a></li>
        <li id="action-stop-EC2-1" onclick="return clickEventStop()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/stop?instance_id=i-xxxxxx">停止する</a></li>
        <li id="action-spec-large-EC2-1" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.large&instance_id=i-xxxxxx">t2.largeに変更して再起動</a></li>
        <li id="action-spec-medium-EC2-1" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.medium&instance_id=i-xxxxxx">t2.mediumに変更して再起動</a></li>
        <li id="action-spec-small-EC2-1" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.small&instance_id=i-xxxxxx">t2.smallに変更して再起動</a></li>
        <li id="action-spec-micro-EC2-1" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.micro&instance_id=i-xxxxxx">t2.microに変更して再起動</a></li>
        <li id="action-spec-nano-EC2-1" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.nano&instance_id=i-xxxxxx">t2.nanoに変更して再起動</a></li>
    </ul>

    <h1>EC2-2</h1>
    <h1 id="ec2-name-2">読込中（name）</h1>
    <h2 id="ec2-id-2">読込中（id）</h2>
    <p>ステータス</p>
    <ul>
        <li id="status1-EC2-2">起動・停止・スペック変更中</li>
        <li id="status2-EC2-2">読込中（スペック）</li>
    </ul>
    <p>アクション</p>
    <ul>
        <li id="action-start-EC2-2" onclick="return clickEventStart()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/start?instance_id=i-xxxxxx">起動する</a></li>
        <li id="action-stop-EC2-2" onclick="return clickEventStop()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/stop?instance_id=i-xxxxxx">停止する</a></li>
        <li id="action-spec-large-EC2-2" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.large&instance_id=i-xxxxxx">t2.largeに変更して再起動</a></li>
        <li id="action-spec-medium-EC2-2" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.medium&instance_id=i-xxxxxx">t2.mediumに変更して再起動</a></li>
        <li id="action-spec-small-EC2-2" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.small&instance_id=i-xxxxxx">t2.smallに変更して再起動</a></li>
        <li id="action-spec-micro-EC2-2" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.micro&instance_id=i-xxxxxx">t2.microに変更して再起動</a></li>
        <li id="action-spec-nano-EC2-2" onclick="return clickEventChange()"><a href="https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/specchange?instace_spec=t2.nano&instance_id=i-xxxxxx">t2.nanoに変更して再起動</a></li>
    </ul>
    <script>
      fetch('https://aaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/check') // (1) リクエスト送信
        .then(response => response.json()) // (2) レスポンスデータを取得
        .then(json => { // (3)レスポンスデータを処理

          //id
          document.getElementById('ec2-name-1').innerHTML = json[0].name;
          document.getElementById('ec2-id-1').innerHTML = json[0].id;
          //status
          if (json[0].status == "running"){
            document.getElementById('status1-EC2-1').innerHTML = "起動中";
            document.getElementById("action-start-EC2-1").style.display ="none";
          }else if(json[0].status == "stopped"){
            document.getElementById('status1-EC2-1').innerHTML = "停止";
            document.getElementById("action-stop-EC2-1").style.display ="none";
          }else{
            document.getElementById('status2-EC2-1').innerHTML = "エラーです"
          };
          //spec
          if (json[0].spec == "t2.large"){
            document.getElementById('status2-EC2-1').innerHTML = json[0].spec + " (現在のスペック)";
            document.getElementById("action-spec-large-EC2-1").style.display ="none";
          }else if(json[0].spec == "t2.medium"){
            document.getElementById('status2-EC2-1').innerHTML = json[0].spec + " (現在のスペック)";
            document.getElementById("action-spec-medium-EC2-1").style.display ="none";
          }else if(json[0].spec == "t2.small"){
            document.getElementById('status2-EC2-1').innerHTML = json[0].spec + " (現在のスペック)";
            document.getElementById("action-spec-small-EC2-1").style.display ="none";
          }else if(json[0].spec == "t2.micro"){
            document.getElementById('status2-EC2-1').innerHTML = json[0].spec + " (現在のスペック)";
            document.getElementById("action-spec-micro-EC2-1").style.display ="none";
          }else if(json[0].spec == "t2.nano"){
            document.getElementById('status2-EC2-1').innerHTML = json[0].spec + " (現在のスペック)";
            document.getElementById("action-spec-nano-EC2-1").style.display ="none";
          }else{
            document.getElementById('status2-EC2-1').innerHTML = json[0].spec + " (現在のスペック)";
          };
          //20秒ごとにリロード
          document.addEventListener('DOMContentLoaded', function() {
            document.getElementById("ec2-name-1").addEventListener("click", function(){
            window.location.reload();
              })
            });
            setTimeout("location.reload()",20000);
          //id
          document.getElementById('ec2-name-2').innerHTML = json[1].name;
          document.getElementById('ec2-id-2').innerHTML = json[1].id;
          //status
          if (json[1].status == "running"){
            document.getElementById('status1-EC2-2').innerHTML = "起動中";
            document.getElementById("action-start-EC2-2").style.display ="none";
          }else if(json[1].status == "stopped"){
            document.getElementById('status1-EC2-2').innerHTML = "停止";
            document.getElementById("action-stop-EC2-2").style.display ="none";
          }else{
            document.getElementById('status2-EC2-2').innerHTML = "エラーです"
          };
          //spec
          if (json[1].spec == "t2.large"){
            document.getElementById('status2-EC2-2').innerHTML = json[1].spec + " (現在のスペック)";
            document.getElementById("action-spec-large-EC2-2").style.display ="none";
          }else if(json[1].spec == "t2.medium"){
            document.getElementById('status2-EC2-2').innerHTML = json[1].spec + " (現在のスペック)";
            document.getElementById("action-spec-medium-EC2-2").style.display ="none";
          }else if(json[1].spec == "t2.small"){
            document.getElementById('status2-EC2-2').innerHTML = json[1].spec + " (現在のスペック)";
            document.getElementById("action-spec-small-EC2-2").style.display ="none";
          }else if(json[1].spec == "t2.micro"){
            document.getElementById('status2-EC2-2').innerHTML = json[1].spec + " (現在のスペック)";
            document.getElementById("action-spec-micro-EC2-2").style.display ="none";
          }else if(json[1].spec == "t2.nano"){
            document.getElementById('status2-EC2-2').innerHTML = json[1].spec + " (現在のスペック)";
            document.getElementById("action-spec-nano-EC2-2").style.display ="none";
          }else{
            document.getElementById('status2-EC2-2').innerHTML = json[1].spec + " (現在のスペック)";
          };
        });

      function clickEventStart() {
        var checked = confirm("起動します");
        if (checked == true) {
            return true;
        } else {
            return false;
        }
      }
      function clickEventStop() {
        var checked = confirm("停止します");
        if (checked == true) {
          return true;
        } else {
          return false;
        }
      }
      function clickEventChange() {
        var checked = confirm("スペック変更します");
        if (checked == true) {
          return true;
        } else {
          return false;
        }
      }
    </script>
  </body>
</html>
```
javascriptのfetchを使用することで、非同期でインスタンスIDと現在のスペックと起動の有無を確認できます。
インスタンスごとに同じようなコードが繰り返していますので、もっとスマートに書けると思いますが、とりあえずこれでいきます。

`onclick="return clickEventStart()"`としていますが、onClickにreturnをつけないと、キャンセル時も起動してしまいます。
[JSのconfirmでダイアログのボタン押下時の処理の分岐](https://qiita.com/Rys8/items/aad482a4bc3bf823c188)

こちらが実際のhtmlページです↓
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/d99910ed-1cc9-d6f1-cbba-b3b65de0040d.png">

# htmlをS3にアップロード

S3のバケット作成
・バケット名入力
・パブリックアクセスをすべてブロックのチェックを外します
・バケット作成をクリック
・先程作成したhtmlをアップロード(パブリック読み取りアクセス権の付与を選択すること)
・プロパティ→静的ウェブサイトホスティングを編集し、静的ウェブサイトホスティングを有効、インデックスドキュメントを入力し保存します
・アクション→公開をクリックし,バケットウェブサイトエンドポイントからhtmlページに遷移できます

詳しくはこちら参照
https://qiita.com/yuichi1992_west/items/ab3b7e91c34f822cb12b

# Cloudfrontからページを読み込むように設定
CloudFront OAIを作成して、ユーザーは S3 バケットへ直接アクセスを不可とし、CloudFront 経由でのみアクセスできるようにします。
S3の静的ウェブサイトホスティングは`無効`に設定します。
有効だと、CloudFront OAIによるアクセス制限はできません。

s3の設定
・静的ウェブサイトホスティング:無効

Cloudfrontの設定
・Origin domain：`ec2contol.s3.ap-northeast-1.amazonaws.com`
・Name:`ec2contol.s3.ap-northeast-1.amazonaws.com`
・S3 bucket access：`YES`
→Create new OAI
・Bucket policy：`YES`

Origin domainに必ずリージョン名も含めてください。`ec2contol.s3.ap-northeast-1.amazonaws.com`ではなく`ec2contol.s3.amazonaws.com`とすると、CloudFrontのURLにアクセスしてもS3のURLにリダイレクトされることがあるようです。
こちら参考になります。[CloudFrontのURLにアクセスしてもS3のURLにリダイレクトされる](https://dev.classmethod.jp/articles/s3-cloudfront-redirect/)


CloudFrontは、ディストリビューションのURLへアクセスする際に、"index.html"の指定を省略できる設定があります。
ルート URLのアクセス時に"index.html" をオブジェクトとして返す設定を行います。

settingsの`Edit`から
・Default root object:`index.html`と入力
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/d8efb5d2-f179-cb60-5140-5f2389534856.png">

ディストリビューションのURLへアクセスすると、ページが表示されました！
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/89c540fd-603a-70d5-0e8c-b69e3017843a.png">



また、S3からのアクセスもできないことを確認しておきましょう!

# WebページにBasic認証を有効化する
ApiGatewayとCloudFrontから直接のアクセスされる可能性がありますので、それぞれにBasic認証をかけます。

### ApiGatewayは、こちらの手順通りに行いました。
[「API GatewayにLambda(Node.js)でBASIC認証かける」](https://dev.classmethod.jp/articles/apigateway-lambda-basicauth/)
ただし、checkのGETメソッドのみBasic認証は外しています。非同期でfetchが働かなかったためです。checkは操作されることはないので、このような対応にしました。
API Gatewayの方は設定後、デプロイを忘れずにしましょう！



### CloudFrontは、こちらの手順通りに行いました。
以下の2通りの方法があります
- [Lambda@EdgeでBasic認証設定]
(https://qiita.com/r-wakamatsu/items/43fa0f3c4b2e7c9528cf)
- [CloudFront FunctionsでBasic認証](https://dev.classmethod.jp/articles/apply-basic-authentication-password-with-cloudfront-functions/)

### 追加作業
以下ののEdit behaviorコンソールの記載しておきます。
#### CloudfrontのEdit behavior

##### Origin and origin groups
S3bucketのURLを指定します

##### Cache key and origin requests
・Legacy cache settings：Headers
→Include the following headers
→Access-Control-Request-Headers」Access-Control-Request-Method、Origin3つを加える

・Legacy cache settings：Object caching
→Object caching
→Customize
→TTL3つすべて0にする

保存する
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/461fae70-af69-c69e-019e-5a7d5cafcdc2.png">

簡単にBasic認証をかけることができました。

⑧htmlから起動などのLambda実行後、htmlページにリダイレクトする
htmlから起動をクリックするとページが遷移され、戻るボタンを押す必要があります。
煩わしいので、遷移せずhtmlにリダイレクトするよう設定していきます。
次回に繰り越します。
[API Gateway でLambda実行後、特定のページにリダイレクトする ②](https://qiita.com/holdout0521/items/9ed3d861db67fa561c15)


# 参考文献
[boto3でEC2インスタンスサイズ変更](https://qiita.com/s-katsumata/items/e1c6c39d1da58a104401)
[AWS Lambda 関数で EC2 インスタンスを起動・終了する](https://mseeeen.msen.jp/start-ec2-instance-by-aws-lambda-with-python-36#i-5)
[CloudFront + S3 構築のポイント](https://oji-cloud.net/2021/01/02/post-5754/)
[API Gateway + LambdaでREST API開発を体験しよう [10分で完成編]](https://qiita.com/tamura_CD/items/46ba8a2f3bfd5484843f)
[javascriptでAPI Gatwayにリクエスト送ったらエラーが出た！ ＞ それ、CORSを忘れていない？](https://qiita.com/g_ogawa/items/b10999954788d202e0ff)