LambdaからEventBridge Schedulerを作成してみた

# 背景・目的
EventBridgeをアプリケーションから作成する機会がありましたので、簡単に試してみます。

# 概要
## Amazon EventBridge スケジューラとは
[Amazon EventBridge スケジューラとは](https://docs.aws.amazon.com/ja_jp/scheduler/latest/UserGuide/what-is-scheduler.html)を元に整理します

> Amazon EventBridge スケジューラはサーバーレススケジューラで、一元化されたマネージドサービスからタスクを作成、実行、管理できます。EventBridge スケジューラは拡張性が高く、270 を超える AWS サービスと 6,000 を超える API オペレーションを呼び出すことができる何百万ものタスクをスケジュールできます。EventBridge スケジューラでは、インフラストラクチャをプロビジョニングして管理したり、複数のサービスと統合したりすることなく、スケジュールを大規模に配信してメンテナンスコストを削減できます。

- マネージドサービス
- 270を超えるAWSサービスを呼び出すことができる
- 6,000を超えるAPIオペレーションを呼び出すことができる
- 何百万ものタスクをスケジュールできる

> EventBridge スケジューラは、ダウンストリームのターゲットの空き状況に基づいてスケジュールを調整する組み込みメカニズムにより、タスクを確実に配信します。EventBridge スケジューラでは、繰り返しのパターンに cron やレート式を使ってスケジュールを作成したり、1回限りの呼び出しを設定したりできます。配信の時間枠を柔軟に設定したり、再試行制限を定義したり、失敗したトリガーの最大保持時間を設定したりできます。

- cron、レート式を使ってスケジュールを作成したり、一回限りの呼び出しを設定したりできる

## EventBridge スケジューラの主な機能
> EventBridge スケジューラには、ターゲットの設定やスケジュールの拡張に使用できる以下の主要機能があります。
> - テンプレート化されたターゲット — EventBridge スケジューラは、Amazon SQS、Amazon SNS、Lambda、EventBridge を使用して、一般的な API オペレーションを実行するテンプレート化されたターゲットをサポートしています。定義済みのターゲットを使用すると、EventBridge スケジューラのコンソール、EventBridge スケジューラ SDK、または AWS CLI を使用してスケジュールをすばやく設定できます。
> - ユニバーサルターゲット — EventBridge スケジューラにはユニバーサルターゲットパラメーター (UTP) が用意されています。これを使用して、スケジュール上で 270を超える AWS サービスと 6,000 を超える API オペレーションをターゲットとするカスタマイズされたトリガーを作成できます。UTP では、EventBridge スケジューラのコンソール、EventBridge スケジューラ SDK、または AWS CLI を使用して、カスタマイズされたトリガーを設定できます。
> - 柔軟な時間枠 — EventBridge スケジューラは柔軟な時間枠をサポートしているため、ターゲットの呼び出しを正確にスケジュールする必要のないユースケースでも、スケジュールを分散してトリガーの信頼性を高めることができます。
> - 再試行 — EventBridge スケジューラは、ターゲットに少なくとも 1 回のイベント配信を行います。つまり、ターゲットからの応答で少なくとも 1 つの配信が成功します。EventBridge スケジューラでは、失敗したタスクのスケジュールの再試行回数を設定できます。EventBridge スケジューラは、スケジュールの信頼性を高め、ターゲットが確実に利用できるようにするために、失敗したタスクを遅延させて再試行します。

下記の機能がある
- テンプレート化されたターゲット
    - SQS、SNS、Lambda、EventBridgeを使用して、一般的なAPIを実行するテンプレート化されたターゲットをサポート子ている
- ユニバーサルターゲット
    - ユニバーサルターゲットパラメータ（UTP）が用意されている
        - 270以上のAWSサービス、6000を超えるAPIオペレーションをターゲットとするカスタマイズされたトリガーを作成できる
- 柔軟な時間枠
    - 柔軟な時間枠をサポートしている
    - ターゲットの呼び出しを正確にスケジュールする必要のないユースケースでもスケジュールを分散してトリガーの信頼性を高める
- 再試行
    - ターゲットに少なくても一回のイベント配信を行える
    - ターゲットからの応答で少なくても1つの配信が成功する
    - 失敗したタスクのスケジュールの再試行回数を設定できる

## スケジュールグループの管理
> スケジュールグループとは、スケジュールを整理するために使用する Amazon EventBridge スケジューラのリソースです。

> AWS アカウント には default スケジューラグループが付属しています。新しいスケジュールは、default グループ、または自分で作成して管理するスケジュールグループに関連付けることができます。AWS アカウント には最大 500 個のスケジュールグループを作成できます。EventBridge スケジューラでは、タグを適用することで、個々のスケジュールではなく、スケジュールグループを整理できます。

- スケジュールグループは500まで作成できる
- スケジュールは、タグを適用しスケジュールグループを整理できる

> タグとは、ユーザーが定義する大文字と小文字を区別するキーと値で構成されるラベルです。タグを作成して、目的、所有者、環境などの基準に基づいてスケジュールを分類できます。例えば、次のタグを使用して、スケジュールが属する環境を特定できます

スケジュールグループには、ACTIVE と DELETING という 2 つの状態があります。

> 最初にグループを作成すると、デフォルトでは ACTIVE になっています。スケジュールは ACTIVE グループに追加できます。グループを削除すると、EventBridge スケジューラが関連付けれらたスケジュールの削除を完了するまで状態は DELETING に変わります。EventBridge スケジューラがグループ内のスケジュールを削除すると、そのグループはアカウントで使用できなくなります。

- グループのステータスはACTIVEと、DELETINGという2つの状態がある
- ACTIVEグループに、グループを追加できる
- グループを削除すると、関連付けられたスケジュールの削除までは、DELETINGになる
- すべてのスケジュールを削除すると、グループはアカウントで使用できなくなる



# 実装
下記の構成で試してみます。
- VPC内のLambdaからEventBridgeを作成する

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/a388625a-11ef-23db-16e3-5e6f376d7299.png)


## 事前準備
### ネットワーク
VPCおよびプライベートサブネットを作成します。

1. VPCに移動します
1. VPCの設定で下記を入力し、「VPCを作成」をクリックします
    - VPC名
    - AZ1つ
    - プライベートサブネット:1つ
    - パブリックサブネット:1つ
    - NATGW
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/bc3d5237-5b13-16f2-2f4b-0f1d78b51118.png)

### EventBridge SchedulerのIAMロール
1. IAMに移動します
1. ナビゲーションペインで「ロール」をクリックします
1. 信頼関係では、下記を設定します
    ```
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Service": "scheduler.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        ]
    }
    ```
1. IMAポリシーに「AWSLambdaRole」をアタッチします


## Lambdaの作成
1. Lambdaに移動します

### 起動されるLambda
1. 「関数の作成」をクリックします
1. 下記を入力します
    - 関数名
    - ランタイム
    - アーキテクチャ
    - 実行ロール
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/321dc520-2ab3-57c7-fe9e-b8d53c4b5418.png)

1. 下記を指定して、「関数の作成」をクリックします
    - VPCを有効化をチェック
    - VPC
    - サブネット
    - セキュリティグループ
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b4385939-aae7-f397-152b-f8b75c15ccaa.png)
1. コードは、デフォルトのままです
    ```
    import json
    
    def lambda_handler(event, context):
        # TODO implement
        return {
            'statusCode': 200,
            'body': json.dumps('Hello from Lambda!')
        }
    ```

1. 動作を確認するため、「テスト」タブをクリックします
1. 「テスト」をクリックすると、直ぐに結果が出ます
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/3eb93bed-bdd5-5547-48b3-b25ec406330b.png)

### EventBridgeを作成するLambda
1. 「関数の作成」をクリックします
1. 下記を入力します
    - 関数名
    - ランタイム
    - アーキテクチャ
    - 実行ロール
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/03864581-88d9-dded-df9c-a77f3efd2ee3.png)


1. 下記を指定して、「関数の作成」をクリックします
    - VPCを有効化をチェック
    - VPC
    - サブネット
    - セキュリティグループ
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b4385939-aae7-f397-152b-f8b75c15ccaa.png)

1. 設定タブ、一般設定を選択します
1. タイムアウトを1分に設定しておきます

#### EventBridgeを作成するコード
Boto3の[ドキュメント](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/scheduler/client/create_schedule.html)を参考に作成します。

1. 下記をアップロードします

    ```
    import json
    import boto3
    
    def lambda_handler(event, context):
    
    
        client = boto3.client('scheduler')
        response = create_schedule(client)
        
    
        return {
            'statusCode': 200,
            'body': json.dumps('Hello from Lambda!')
        }
    
    
    def create_schedule(client):
        response = client.create_schedule(
            ActionAfterCompletion='NONE',
            ClientToken='string',
            Description='test-scheduler',
            FlexibleTimeWindow={
                'MaximumWindowInMinutes': 5,
                'Mode': 'FLEXIBLE'
            },
            Name='test-scheduler',
            ScheduleExpression='rate(5 minutes)',
            State='ENABLED',
            Target={
                'Arn': 'arn:aws:lambda:{リージョン}:XXXXXXX:function:hello_world',
                'Input': '{"key1":"test"}',
                # 作成したEventBridgeのIAMロールのARNを指定します。
                'RoleArn': 'arn:aws:iam::XXXXXXX:role/EventBridgeScheduleLambdaInvoke',
            }
        )
        return response
    
    ```

#### IAMポリシーの作成
[EventBridge スケジューラのアクセス許可](https://docs.aws.amazon.com/ja_jp/scheduler/latest/UserGuide/security_iam_id-based-policy-examples.html#security_iam_id-based-policies-permissions)に記載があるポリシーをLambdaを作成したときのIAMロールにアタッチします。

1. IAMに移動します
1. ナビゲーションウィンドウで「ポリシー」をクリックします
1. 「ポリシーの作成」をクリックします
1. ポリシエディタで、EventBridge Schedulerを選択し、下記のポリシーを作ります
    ```
    {
    	"Version": "2012-10-17",
    	"Statement": [
    		{
    			"Sid": "VisualEditor0",
    			"Effect": "Allow",
    			"Action": [
    				"scheduler:ListTagsForResource",
    				"scheduler:GetSchedule",
    				"scheduler:UpdateSchedule",
    				"scheduler:TagResource",
    				"scheduler:CreateSchedule",
    				"scheduler:ListSchedules",
    				"scheduler:GetScheduleGroup",
    				"scheduler:UntagResource",
    				"scheduler:ListScheduleGroups",
    				"scheduler:DeleteScheduleGroup",
    				"scheduler:CreateScheduleGroup",
    				"scheduler:DeleteSchedule"
    			],
    			"Resource": "*"
    		},
            {
                "Effect": "Allow",
                "Action": "iam:PassRole",
                "Resource": "arn:aws:iam::*:role/*",
                "Condition": {
                    "StringLike": {
                        "iam:PassedToService": "scheduler.amazonaws.com"
                    }
                }
            }
    	]
    }

    ```
1. ポリシー名を入力し、「ポリシーの作成」をクリックします
1. 作成したIAMロールで「ポリシーをアタッチ」をクリックします
1. 作成したIAMポリシーをアタッチし、「許可を追加」をクリックします

## コードの実行
### Lambdaの実行
1. Lambdaに移動します

### EventBridge Schedulerの確認

1. EventBridgeを作成するLambdaで、「テスト」をクリックします
1. EventBridge Scheduleに移動します
1. できていました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6e8ab18a-61d9-bbe2-2c16-ef7110dcf1b8.png)

1. 「スケジュール」タブをクリックします。rate(5 minute)が設定されていました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/c16597ad-ed04-40b7-5317-279c804ca4d8.png)

1. 「ターゲット」タブをクリックします。パラメータ、ターゲット、IAMロールなど想定通りの値が設定されていました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/a83efe30-c612-0973-791e-b3aa73f7f0c6.png)

### 起動されるLambdaの確認
1. Lambdaに移動します
1. 起動されるLambda関数「hello_world」を表示します
1. 「モニタリング」タブをクリックします
1. 実行されていることがわかります
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b8002510-86e6-50d9-fac2-03c662f43d80.png)

### CloudWatch Logsの確認
CloudWatch Logs（CWL）でも起動確認します。

1. CWLに移動します
1. /aws/lambda/hello_worldのロググループに移動します
1. 最新のログストリームをクリックします
1. 想定通り、5分毎に起動されていました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5156762e-0782-367b-a6d5-3845f046d0f9.png)


# 考察
今回、EventBridge ScheduleをVPC内のLambdaから作成しました。
対応する過程で、VPCeがなく、NATGWやIGWなど用意する必要があるなど、若干ハマりました。
今後は、各種スケジュールの設定や、デッドレターキューの設定などいくつか試してみたいと思います。

# 参考

https://docs.aws.amazon.com/ja_jp/scheduler/latest/UserGuide/what-is-scheduler.html

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/scheduler.html

https://docs.aws.amazon.com/ja_jp/general/latest/gr/eventbridgescheduler.html

https://docs.aws.amazon.com/ja_jp/scheduler/latest/UserGuide/security_iam_id-based-policy-examples.html