Auth0のログをLambdaで加工してからCloudWatch Logsに転送する

# この記事について

Auth0のログは最も上位のEnterpriseプランにおいても30日間とかなり短いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/cd2b53b8-bbe5-f3a2-4174-c3530a0ebbe9.png)

https://auth0.com/docs/deploy-monitor/logs/log-data-retention

30日間以上長期保存がしたい場合はCloudwatch LogsやS3などに転送する必要があります。

EventBridgeを利用すれば、Auth0のログを簡単にCloudwatch Logsに転送することができます。
さらに、EventBridgeは受け取ったデータをLambdaに渡すことができます。Auth0のログをLambdaで加工してCloudwatch Logsに転送する手順も記載します。

# やること

- [基本] Auth0のログをCloudwatch Logsに転送する
- [応用] Auth0のログを Lambdaで加工してCloudwatch Logsに転送する

# 前提

- Auth0のアカウントを所有していること(プランはFreeで良いです)
- AWSアカウントを所有していること

# [基本] Auth0のログをCloudwatch Logsに転送する


## Auth0側の設定

Auth0にログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/e5f0f8af-6375-7fc8-90dd-8f28d40bc6cf.png)

Monitoring-> Streams -> Create Log Streamに移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/33f6d84e-5f3a-2652-f7a1-3a303b567f42.png)

Amazon EventBridgeを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/d4aeb0e7-e5c6-6997-703a-397be595e89e.png)

名前はなんでも良いです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/76cfd5ba-3d39-9b24-df8c-39c00d1a9096.png)

AWSアカウントID、リージョン、を入力します。それ以外はデフォルトで良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/cdbf54ff-0543-e7ce-1be0-01d78ffa1e43.png)

これでAuth0側の設定は完成です。

## EventBridgeの設定

AWSマネジメントコンソールからEventBridgeの画面を開きます。
パートナーイベントソースを見ると、auth0のイベントソースが表示されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/b2f2ce0e-26fb-e328-c0fb-1a61686e6886.png)

「イベントパスと関連づける」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/f557f5e8-f3a4-e883-5702-46ac1d17906d.png)

チェックはしないで「関連づける」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/0382ccaf-f0d5-9512-0598-32e25117c3ca.png)

カスタムイベントパスが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/014fb82a-0e92-88a3-ef10-f493a65c569e.png)

## Cloudwatch Logsの設定

Cloudwatch Logsの画面に移動して、ロググループを作成します。
(ロググループ名は/aws/eventsという名前からはじまる必要があります)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/64a791c4-bb31-d402-096f-4e3129da6899.png)

EventBridgeの画面から、ルールを作成します。(イベントパスをdefaultから作成したauth0のものに変更します)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/4602f323-a073-9cd3-0ace-eb8d1f7e1aae.png)

名前はなんでも良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/15ff0c23-7466-0056-0de2-2f8d5b059198.png)

特に入力は不要です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/b4618ac7-bb9a-f5ef-5b7f-be0f91fbd036.png)

こちらも入力は不要です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/78903935-d15b-54e9-083c-e1be68132ce2.png)

ターゲットタイプをCloudwatch Logsロググループにして、先ほど作成したロググループを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/da197f42-0c1c-af55-5158-b46a61109cf8.png)

それ以外はデフォルトで良いので、全て入力してルールが作成されたことを確認します。

## 動作確認

イベントパスの「検出を開始する」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/8370d87d-c92c-eaa5-b0bd-47ad34a88c07.png)

そしてAuth0にてUsersからユーザを作成します。((Auth0にてロギングされることがメインなので、それができれば何をしてもいいですいいです。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/8d25860f-2884-a426-45a0-296c02406ef2.png)

Cloudwatch Logsにてログが転送されていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/aeab6de1-9d8c-1723-0684-6b6c96afaa5b.png)

# [応用] Auth0のログを Lambdaで加工してCloudwatch Logsに転送する

Auth0からEventBridgeに転送されるログイベントは以下の形式をもつJSONです。


```
{
  "id": "623053cf-8e0f-9203-f464-e789cf18b0e2",
  "detail-type": "Auth0 log",
  "source": "aws.partner/auth0.com/example-tenant-635d694a-8a5a-4f1a-b223-1e0424edd19a/auth0.logs",
  "account": "xxxxxxxxxxxx",
  "time": "2020-01-29T17:26:50Z",
  "region": "us-west-2",
  "resources": [],
  "detail": {
    "log_id": "",
    "data": {
      "date": "2020-01-29T17:26:50.193Z",
      "type": "sapi",
      "description": "Create a log stream",
      "client_id": "",
      "client_name": "",
      "ip": "",
      "user_id": "",
      "log_id": ""
    }
  }
}
```

1階層目のdetail以外のフィールドはEventBridgeに関連するフィールドでAuth0のログに関するものではありません。detail.data内のデータがAuth0のログになります。

ここでは、このdetail.dataだけCloudwatch Logsに転送されるようにログを加工します。

EventBridgeのターゲットをCloudwatch Logsではなく、Lambdaにして、そのLambdaにてログの加工とCloudwatch Logsへの転送を行います。

(ちなみに、EventBridgeの入力トランスフォーマー機能を利用すれば、Lambdaを利用することなくdetail.dataだけを取得することができそうです。ただ今回はあくまでサンプルケースなのでLambdaを使います。)



```plaintext:[基本]Auth0のログをCloudwatchLogsに転送するの構成
Auth0 -> EventBridge -> Cloudwatch Logs
```

```plaintext:[応用]Auth0のログをLambdaで加工してCloudwatchLogsに転送するの構成
Auth0 -> EventBridge -> Lambda -> Cloudwatch Logs
```

## Lambda関数の作成

以下のコードでLambda関数を作成します。

```plaintext:lambda_function.py
import json
import boto3
import time
import datetime

client = boto3.client('logs')

def lambda_handler(event, context):

    now = datetime.datetime.now()
    now_time = now.strftime('%Y%m%d%H%M')
    
    log_group_name = 'auth0/log'
    log_stream_name = now_time

    # ロググループの作成(存在していない場合)
    response = client.describe_log_groups(logGroupNamePrefix=log_group_name)
    if not response['logGroups']:
        client.create_log_group(logGroupName=log_group_name)
    
    # ログストリームの作成(存在していない場合)
    response = client.describe_log_streams(logGroupName=log_group_name, logStreamNamePrefix=log_stream_name)
    if not response['logStreams']:
        client.create_log_stream(logGroupName=log_group_name, logStreamName=log_stream_name)

    # ログイベントからdetail.dataを取得する
    try:
        message = event["detail"]["data"]
    except KeyError: # detail.dataフィールドがない場合はログストリームへの書き込みを行わず終了
        return {
            'statusCode': 400,
        }

    # Cloudwatch Logsログストリームへ書き込む        
    log_events = [{'timestamp': int(round(time.time() * 1000)), 'message': str(message).replace("\'", "\"")}]
    client.put_log_events(logGroupName=log_group_name, logStreamName=log_stream_name, logEvents=log_events)
    
    return {
        'statusCode': 200,
    }
```

Lambdaの実行ロールには`CloudWatchLogsFullAccess`ポリシーをアタッチしておきましょう。(本来ならもう少し厳しいカスタムポリシーを付与すべきですが、あくまで検証ということで緩い管理ポリシーを付与します)


## EventBridgeルールの作成

EventBridgeのルール画面から、イベントバスをauth0のものにして、ルールを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/369e2ff9-66f2-4365-6f49-a7957e1ebe5c.png)

名前は任意です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/8358c22b-0920-3d14-6c96-837705dd9798.png)

イベントパターンをAuth0の全てのイベントにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/d9963571-c6eb-1f0f-d5a2-7b80af5310b3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/41345c44-f8aa-c3f6-a5fd-8c950ddafda4.png)

ターゲットに作成したLambda関数を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/14d0b950-a064-656b-df26-55b3c5bf6de8.png)

これ以外はデフォルトでルールを作成します。

以上で実装は完了です。


## 動作確認

再度Auth0にてユーザを作成します。(Auth0にてロギングされることがメインなので、それができれば何をしてもいいですいいです。)


Cloudwatch LogsにてLambdaにて書き込み先として指定したログストリームを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/cf88aa80-e9aa-40da-6632-3bfb3fb9bc05.png)

ご覧の通り、detail.data配下のフィールドのみに絞られました。

参考：加工前のログ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/3e51be89-7f8c-b085-b9c0-4698d1f573d7.png)