【AWS】Step Functionsの実行結果をSlackへ通知する


StepFunctionsを用いてBIツール用のAthena集計テーブルを1日1回更新していますが、**実行結果をSlackで知りたい**と思いました。


以下のような形で機能を作ってみたので、構成までの手順を書こうと思います。
![structure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/df400396-0711-4d4f-18e2-7f466f731e01.png)


## 設定

### Slack
まずは[incoming-webhook](https://api.slack.com/messaging/webhooks)アプリを設定します。
設定方法は[こちら](https://lrsupport.zendesk.com/hc/ja/articles/360042304214-Slack%E3%82%84Teams%E3%81%A8%E9%80%A3%E6%90%BA%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AEURL%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)のページを見ていただければと思います。

通知対象としたい「チャンネル名」「チャンネルID」と「Webhook URL」をメモします。

![Incoming Webhook _ Slack App ディレクトリ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/4715babd-42dc-593b-f095-d4d09cb87c60.png)

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/195e0bfb-cf25-33fc-4a45-99caeb9fcf4a.png)

### SNS
AWSコンソールで「Simple Notification Service」を開きます
トピック→トピックの作成でトピックを作ります。

- タイプ
    - スタンダート
- 名前
    - お好きな名前

として、トピックを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/7e82ecf6-62cf-cf60-0702-39d8d2e1eae3.png)


### Lambda

AWSコンソールで「Lambda」を開きます
関数→関数の作成から関数を作ります

- 関数名
    - お好きな名前
- ランタイム
    - Python 3.10

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/b5cc9e2e-4c46-1519-4903-d390ee8d874a.png)


「関数の概要」からトリガーを追加します。
SNSトピックは[SNS](#SNS)で作成したものを選択します。

![トリガーの追加 - Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/7a5e00fc-542e-2a41-b481-9a79322e1310.png)

`lambda_function.py`には以下のコードを記載します。
Webhook URLとSlackチャンネルは[Slack](#Slack)設定のものを用いてください。

```py:lambda_functions.py
import urllib3
import json

http = urllib3.PoolManager()


def lambda_handler(event, context):
    url = "SlackのWebHookURL"
    rec = event["Records"][0]["Sns"]["Message"]
    if type(rec) == str:
        rec = json.loads(rec)
    message_blocks = [
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": f':information_source: *<https://{rec["region"]}.console.aws.amazon.com/states/home?region={rec["region"]}#/v2/executions/details/{rec["detail"]["executionArn"]}| {rec["detail-type"]} | {rec["region"]} | Account: {rec["account"]}>*',
            },
        },
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": f'*State Machine Arn*\n\
```\n\
{rec["detail"]["stateMachineArn"]}\n\
```\n',
            },
        },
    ]

    msg = {
        "channel": "#Slackチャンネル名",
        "username" : "AWS-StepFunctions-Result",
        "blocks" : message_blocks,
    }

    encoded_msg = json.dumps(msg).encode("utf-8")
    resp = http.request("POST", url, body=encoded_msg)
    print(
        {
            "message": event["Records"][0]["Sns"]["Message"],
            "status_code": resp.status,
            "response": resp.data,
        }
    )
```

ここのコードですが、後述する[入力ステートメント](https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-transform-target-input.html)の設定によって`event`の中身を変えることができます。

Lambda関数のテストを行う場合は、それ用のイベントを設定する必要があるので、以下のjsonを用いてください。


```json
{
  "Records": [
    {
      "EventSource": "aws:sns",
      "EventVersion": "1.0",
      "EventSubscriptionArn": "arn:aws:sns:us-east-1:{{{accountId}}}:ExampleTopic",
      "Sns": {
        "Type": "Notification",
        "MessageId": "MessageId",
        "TopicArn": "arn:aws:sns:ap-northeast-1:{{{accountId}}}:ExampleTopic",
        "Subject": "example subject",
        "Message": {
          "version": "0",
          "id": "id",
          "detail": {
            "executionArn": "arn:aws:states:ap-northeast-1:{{{accountId}}}:execution:TestEvent",
            "stateMachineArn": "arn:aws:states:ap-northeast-1:{{{accountId}}}:stateMachine:TestEvent",
            "name": "xxxxxxxxxx",
            "status": "SUCCEEDED",
            "startDate": 1682167551859,
            "stopDate": 1682167551935,
            "input": "{\n    \"Comment\": \"Insert your JSON here\"\n}",
            "output": "{\"source\":[\"aws.states\"],\"detail-type\":[\"Step Functions Execution Status Change\"],\"detail\":{\"status\":[\"SUCCEEDED\",\"FAILED\"]}}",
            "inputDetails": {
              "included": true
            },
            "outputDetails": {
              "included": true
            },
            "error": null,
            "cause": null
          },
          "detail-type": "[SUCCEEDED] Step Functions Execution Status Change",
          "source": "aws.states",
          "account": "{{{accountId}}}",
          "time": "2023-04-22T12:45:52Z",
          "region": "ap-northeast-1",
          "resources": [
            "https://ap-northeast-1.console.aws.amazon.com/states/home?region=ap-northeast-1#/executions/details/arn:aws:states:ap-northeast-1:{{{accountId}}}:execution:TestEvent",
            "{\n    \"Comment\": \"Insert your JSON here\"\n}"
          ]
        },
        "Timestamp": "1970-01-01T00:00:00.000Z",
        "SignatureVersion": "1",
        "Signature": "EXAMPLE",
        "SigningCertUrl": "EXAMPLE",
        "UnsubscribeUrl": "EXAMPLE",
        "MessageAttributes": {
          "Test": {
            "Type": "String",
            "Value": "TestString"
          },
          "TestBinary": {
            "Type": "Binary",
            "Value": "TestBinary"
          }
        }
      }
    }
  ]
}

```

この状態でテストを実行すると、以下のような投稿がSlackに流れると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/bb360671-d47f-b08b-21bc-f36231c33083.png)



### StepFunctions

StepFunctionsは実行結果を知りたい対象なので、簡単にHello Worldのステートマシンを使います。
実際は、定期実行するステートマシンを対象としてください。
AWSコンソールで「Step Functions」を開きます。
ステートマシン→ステートマシンの作成→コードでワークフローを記述

で、定義を以下として「次へ」を押します。

```json:HelloWorld定義
{
  "Comment": "A Hello World example of the Amazon States Language using Pass states",
  "StartAt": "Hello",
  "States": {
    "Hello": {
      "Type": "Pass",
      "Result": "Hello",
      "Next": "World"
    },
    "World": {
      "Type": "Pass",
      "Result": {
        "source": [
          "aws.states"
        ],
        "detail-type": [
          "Step Functions Execution Status Change"
        ],
        "detail": {
          "status": [
            "SUCCEEDED",
            "FAILED"
          ]
        }
      },
      "End": true
    }
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/edcd9cb3-e66b-d391-239c-e59cf42be25a.png)

### EventBridge
AWSコンソールで「Amazon EventBridge」を開きます。

ルール→ルールを作成

- 名前
    - お好きな名前
- ルールタイプ
    - イベントパターンを持つルール
- イベントソース
    - AWSイベントまたはEventBridgeパートナーイベント
- 作成のメソッド
    - パターンフォームを使用する

イベントパターンは以下のように設定します。
Step Functionsの`SUCCEEDED,FAILED`をキャッチするようにします。
そして、対象のステートマシンArnを記載します。

![SnapCrab_NoName_2023-4-29_7-49-29_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/352cbe14-af92-d0e7-8231-2bb78016b6be.png)


```json:イベントパターン
{
  "source": ["aws.states"],
  "detail-type": ["Step Functions Execution Status Change"],
  "detail": {
    "status": ["SUCCEEDED", "FAILED"],
    "stateMachineArn": ["arn:aws:states:ap-northeast-1:xxxxxxxxx:ステートマシンの名前"]
  }
}
```

- ターゲット
    - ターゲットタイプを以下のように設定します。トピックは[SNS設定](#SNS設定)で作成したものを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/17ff9760-950e-5acc-2a0e-32642280ca0e.png)


- 追加設定
    - ターゲット入力を設定
        - 入力トランスフォーマー

ターゲット入力トランスフォーマーには以下のコードを設定します。

```json:ターゲット入力トランスフォーマー
{
    "detail-executionArn": "$.detail.executionArn",
    "source": "$.source",
    "detail-name": "$.detail.name",
    "version": "$.version",
    "detail-input": "$.detail.input",
    "detail-type": "$.detail-type",
    "detail": "$.detail",
    "id": "$.id",
    "time": "$.time",
    "region": "$.region",
    "account": "$.account",
    "detail-status": "$.detail.status"
}
```

テンプレートには以下のコードを設定します。

```json:テンプレート
{
    "version": <version>,
    "id": <id>,
    "detail":<detail>,
    "detail-type": "[<detail-status>] <detail-type>",
    "source": <source>,
    "account": <account>,
    "time": <time>,
    "region": <region>,
    "resources": [
        "https://<region>.console.aws.amazon.com/states/home?region=<region>#/executions/details/<detail-executionArn>",<detail-input>
    ]
}
```

## 実行

[StepFunctions](#StepFunctions)で作成したステートマシンを実行します！
（本来はEventBridge Schedulerで定期実行します）

![SnapCrab_NoName_2023-4-29_21-13-38_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/9b2a9fbd-f4e4-00ba-14f6-7ba04d7b847c.png)

以下のようにSlackに通知が送られます

![SnapCrab_NoName_2023-4-29_21-15-23_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/82fce8dc-1be5-3d45-8b82-28a81f90029c.png)

### 失敗を試す

HelloWorldのStepFunctionsの定義を以下のように書きかえてわざと失敗させ、失敗の通知についてもSlackへ送られるのかチェックします。

```json:
{
  "Comment": "A Hello World example of the Amazon States Language using Fail states",
  "StartAt": "Hello",
  "States": {
    "Hello": {
      "Type": "Pass",
      "Result": "Hello",
      "Next": "FailState"
    },
    "FailState": {
      "Type": "Fail",
      "Error": "Failure",
      "Cause": "This state machine is designed to fail"
    }
  }
}
```



![SnapCrab_NoName_2023-4-29_21-21-18_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635079/d5abe1e0-53db-c8fe-96e3-38ad254bc2d7.png)

失敗の通知も受け取ることが出来ました！ヤッタゾ

## 参考
何か不足点やアドバイス等いただけますと嬉しいです！


- [Incoming Webhookを使用してAWS Step Functionsの実行結果をSlackに通知する](https://dev.classmethod.jp/articles/slack-notify-incoming-webhook-using-lambda/)
- [ウェブフックを使用して Amazon SNS メッセージを Amazon Chime、Slack、または Microsoft Teams に発行するにはどうすればよいですか?](https://repost.aws/ja/knowledge-center/sns-lambda-webhooks-chime-slack-teams)
- [Step Functions の結果を EventBridge 経由で Slack に通知する](https://int128.hatenablog.com/entry/2022/01/30/222849)
- [[Organizations]マルチアカウントのイベントを集約してChatbotもLambdaも使わずにEventBridgeの入力トランスフォーマーでSlackに通知する](https://dev.classmethod.jp/articles/organizations-aggregate-events-and-format-them-with-eventbridges-input-transformer/)