DataSyncの転送状況をSlackに通知させる方法

# 概要
[前回](https://qiita.com/afadhli/items/812958a1ad45b6638cb9 "LambdaでDataSyncのタスクの実行を自動化させる")ではAWS DataSync (以下`DataSync`)のタスク実行を自動化させる方法を説明してきました。

タスク実行の自動化は便利ですが、データ転送の状況はAWSコンソールかAWS CLIまたはAWS SDKから確認することになります。どれも手動操作です。知りたい`DataSync`タスク実行ステータスをSlackなどに通知を飛ばせたらすぐに気づけますし便利ですよね。実際に仕事でもこの仕組みを実装しましたのでその方法を記事にしたいと思います。

`DataSync`の転送先Locationは前回と同じくAmazon EFS (以下`EFS`）、通知先はSlackを例に挙げて解説していきます。

# 注意事項
- AWSサービスに知見のある方を対象にしていますが、初心者の方にもトライして頂けますと嬉しいです。
- 東京リージョン(ap-northeast-1)でリソースを構築します。

# AWSサービス
* [AWS IAM](https://aws.amazon.com/jp/datasync/) (以下`IAM`)
* [AWS Lambda](https://aws.amazon.com/jp/lambda/)　（以下`Lambda`)
* [Amazon EventBridge](https://aws.amazon.com/jp/eventbridge/) (以下`EventBridge`)

# 手順
## ■ IAM
`Lambda`で利用する`IAMロール`を作成しておきます。

作成したら下記インラインポリシーを追加します。ポリシー名は任意のもので構いません。

- AWSLambdaBasicExecutionRole-policy

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:ap-northeast-1:account-id:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:ap-northeast-1:account-id:log-group:/aws/lambda/*:*"
            ]
        }
    ]
}
```
- notify-datasync-status-policy

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "datasync:DescribeTaskExecution",
            "Resource": "arn:aws:datasync:ap-northeast-1:account-id:task/*/execution/*"
        }
    ]
}
```

:::note warn
**account-id**はご自身のAWSアカウントに置き換えて下さい。
:::

## ■ Lambda
`EventBridge`のトリガーとなる`Lambda`を作成していきます。`Lambda`は`EventBridge`からのイベントを処理し、Slackに通知させる役割を担います。

コードには次の考慮がされています。

1.　Slackアイコンは`DataSync`のタスク実行ステータスによって分けています。
**ERROR**の場合 : 雨マーク
**ERROR以外** : 晴れマーク

2.　どのファイルがどんなステータスなのかSlackのメッセージに記載します。実現方法は`DataSync`の*describe_task_execution*メソッドを使います。

| 関数名 | ランタイム | 実行ロール |
|:-----------:|:------------:|:------------:|
| 任意の名前 | Python 3.9 (記事執筆時)| 上記で作成したIAMロール|

- コード

```python:lambda_function.py
import urllib3
import json
import logging
import boto3

# Logging settings
log = logging.getLogger()
log.setLevel(logging.INFO)

# Boto3 settings
DataSync = boto3.client('datasync')

# Slack channel info
channel = "#your-slack-channel"
webhook_url = "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ"

def lambda_handler(event, context):
    try:
        # Retrieve info that we need from EventBridge event
        log.info('Event:: ' + json.dumps(event))
        resource = event["resources"][0]
        state = event["detail"]['State']
        
        # Retrieve task execution info
        tasks = DataSync.describe_task_execution(TaskExecutionArn=resource)
        file_name = tasks['Includes'][0]['Value']
        
        if state in 'ERROR':
            slack_icon = ":umbrella:"

        else:
            slack_icon = ":sunny:"
        
        # Send notify to Slack channel
        text = slack_icon + " " + file_name +  ' ｜ ' + 'Task execution status： ' + '*' + state + '*'
        url = webhook_url
        msg = {
            "channel": channel,
            "text": text
        }
        encoded_msg = json.dumps(msg).encode('utf-8')
        http = urllib3.PoolManager()
        resp = http.request('POST',url, body=encoded_msg)
        log.info({
            'message:: ' + str(text),
            'status_code:: ' + str(resp.status),
            'response:: ' + str(resp.data)
        })

    except Exception as e:
      log.error('Error occurred:: ' + str(e))
```
:::note warn
***channel***と***webhook_url***はご自身のSlack情報に置き換えて下さい。
:::

## ■ EventBridge
| ルール名 | タイプ  |ターゲット|
|:-----------:|:------------:|:------------:|
|任意の名前 | スタンダード | 上記で作成したLambda関数 |

下記例では全てのリソースかつ全てのステータスを対象にします。

::: note info
各タスク実行ステータスの説明については[AWSのドキュメント](https://docs.aws.amazon.com/ja_jp/datasync/latest/userguide/understand-task-statuses.html)をご参照下さい。
:::

* イベントパターン

```json
{
  "source": ["aws.datasync"],
  "detail-type": ["DataSync Task Execution State Change"],
  "detail": {
    "State": ["QUEUED", "LAUNCHING", "PREPARING", "TRANSFERRING", "VERIFYING", "SUCCESS", "ERROR"]
  }
}
```

特定のリソースかつ特定のステータスのみフィルターして通知させたい場合は、下記イベントパターンで実現可能です。


```json
{
  "source": ["aws.datasync"],
  "detail-type": ["DataSync Task Execution State Change"],
  "resources": [{
    "prefix": "arn:aws:datasync:ap-northeast-1:account-id:task/task-id/execution/exec-"
  }, {
    "prefix": "arn:aws:datasync:ap-northeast-1:account-id:task/task-id/execution/exec-"
  }],
  "detail": {
    "State": ["SUCCESS", "ERROR"]
  }
}
```
:::note warn
**account-id**と**task-id**はご自身のAWSアカウントに置き換えて下さい。
:::
# 動作確認
`DataSync`タスク実行が行われた時に、そのステータスがSlackに通知されるか見ていきましょう。

※手順1,2,4のAWSリソースについては[前回](https://qiita.com/afadhli/items/812958a1ad45b6638cb9 "LambdaでDataSyncのタスクの実行を自動化させる")の記事に記載してあります。

#### 1. 任意のファイルを`S3`にアップロードします。 
例ではcaminandes_llamigos_480p.mp4というMP4形式のファイルを使います。

![スクリーンショット 2023-03-09 23.49.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429912/c2b655f9-e852-2e1c-20af-741928b43237.png)


#### 2. `DataSync`タスクが起動されたことを確認

![スクリーンショット 2023-03-09 22.28.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429912/de118920-b59f-eeb5-30fb-0a73ce51f8d9.png)

#### 3. Slackチャンネルに通知がされたことを確認
`EventBridge`のイベントを`Lambda`が受け取って、その処理が正常に終われば、指定したSlackチャンネルに通知が届きます。

下記内容を例に挙げると、
**LAUNCHING** - 実行タスクが開始された
**TRANSFERRING** - データ転送が実行中
**SUCCESS** - データ転送が成功した
という`DataSync`タスクの実行ステータスが通知されたことが分かりました。

![スクリーンショット 2023-03-10 1.20.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429912/caef61ef-8c17-c84f-a598-4a38bad8e42e.png)

**ERROR**ステータスは中々出ないため、`Lambda`のテストイベントを使って再現してみました。データ転送が失敗した場合のSlackへの通知は次の通りです。雨マークで一目瞭然ですよね。万が一**ERROR**ステータスが出た場合`、CloudWatch Logs`に詳細なエラー内容が記載されます。

![スクリーンショット 2023-03-10 0.16.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429912/22e527af-2c94-53bf-3e69-fae911f72f35.png)

:::note warn
Slackチャンネルの情報が正しいにも関わらず、Slackへの通知が届かない場合は`Lambda`の`CloudWatch Logs`から手掛かりや原因が見つかるかもしれません。
:::
### 4. EFS上にデータを確認
念の為、実際のデータはどうなっているのかを見るために、`EC2`インスタンスにマウントされた対象の`EFS`の中身を確認します。ファイルが作成された日時と**SUCCESS**が通知された日時が同じであることから、データ転送が正常に行われたことが見て取れます。

```
$ ls -la /mnt/efs/
合計 47332
drwxr-xr-x 3 nfsnobody nfsnobody     6144  3月  9 22:47 .
drwxr-xr-x 4 root      root            31  1月 19 18:33 ..
drwx------ 2 root      root          6144  3月  9 22:47 .aws-datasync
-rwxr-xr-x 1 nfsnobody nfsnobody 48458210  3月  9 22:47 caminandes_llamigos_480p.mp4
```

# まとめ
`DataSync`の転送状況はコンソールからでも見ることができますが、転送が失敗した時にいち早く気づいて対応したい場面もあるかと思います。今回は通知先はSlackチャンネルにしましたが、`Amazon SNS`と連携すれば、EメールやSMSへの送信も可能です。そして`DataSync`だけではなく、AWSサービスの多くは`EventBridge`が受け取るイベントを生成しますので、他のサービスでも微修正を施すだけで上記`Lambda`の流用をすることができます。使い方次第で業務の効率が上がるかもしれません。興味があれば是非活用してみて下さい。少し短いですが最後までお読みいただき、ありがとうございました！

# 参考
https://aws.amazon.com/jp/datasync/
https://aws.amazon.com/jp/lambda/
https://aws.amazon.com/jp/eventbridge/
https://docs.aws.amazon.com/ja_jp/datasync/latest/userguide/understand-task-statuses.html