Amazon Bedrock を使ってイベント情報の要約を行い、Slack通知をさせてみた

Amazon Bedrock を使ってイベント情報の要約を行い、Slack通知をさせてみた

Clock Icon2024.07.07

こんにちは、森田です。

皆さん、EventBridge からイベントの通知を行う場合どのように構成していますか?

AWS Lambda を使ったイベント通知や EventBridge から直接APIを実行するような構成が考えられるかと思います。

https://dev.classmethod.jp/articles/eventbridge-teams/

どちらの構成であってもイベント情報から必要な情報のみを取得して通知を行うことになると思います。

この「イベント情報から必要な情報のみを取得」する作業では、JSONのパスミスなどで上手く通知ができない、エラーが発生してしまうことが度々ありましたので、もっと楽に通知構成ができないかを考えてみました。

考えた構成

以下のような EventBridge から Step Functions を実行する構成を考えました。

Untitled(1) (5)

Amazon Bedrock

Amazon Bedrock では、EventBridge から送信されたイベント情報の要約を行います。

今回は、生成AIモデルは、 anthropic.claude-3-5-sonnet-20240620-v1 を利用し、以下のようなプロンプトを与えてみることにしました。

プロンプト
以下の情報は、AWSにおけるイベント情報です。
重要な情報のみに要約を行い、AWSアカウント保有者が必要なアクションがあれば、記述してください。

---
source: ${source}
resources: ${resources}
detail: ${detail}

やってみる

リソースの作成

以下のCloudFormationでリソースの作成を行いました。

CloudFormationテンプレート
AWSTemplateFormatVersion: "2010-09-09"
Description: Notify Step Functions 

Parameters:
  Prompt:
    Type: String
  ChannelId:
    Type: String
  ApiKeyValue:
    Type: String
    NoEcho: true

Resources:
  SlackConnection:
    Type: AWS::Events::Connection
    Properties:
      AuthorizationType: API_KEY
      Description: Slack Connection
      AuthParameters:
        ApiKeyAuthParameters:
          ApiKeyName: "Authorization"
          ApiKeyValue: !Sub "Bearer ${ApiKeyValue}"

  StateMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: StepFunctionsAssumeRolePolicy
            Effect: Allow
            Principal:
              Service: "states.amazonaws.com"
            Action: sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: "EventBridgeRetrieveConnectionCredentialsScopedAccessPolicy"
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - events:RetrieveConnectionCredentials
                Resource: !Sub "${SlackConnection.Arn}"
              - Effect: Allow
                Action:
                  - secretsmanager:GetSecretValue
                  - secretsmanager:DescribeSecret
                Resource: !Sub "${SlackConnection.SecretArn}"
        - PolicyName: "StepFunctionsInvokeHttpEndpointScopedAccessPolicy"
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - states:InvokeHTTPEndpoint
                Resource: !Sub "arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:*"
                Condition:
                  StringEquals:
                    "states:HTTPEndpoint":
                      - "https://slack.com/api/chat.postMessage"
                    "states:HTTPMethod":
                      - "POST"
        - PolicyName: "XRayAccessPolicy"
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - xray:PutTraceSegments
                  - xray:PutTelemetryRecords
                  - xray:GetSamplingRules
                  - xray:GetSamplingTargets
                Resource: "*"
        - PolicyName: "BedrockInvokeModel"
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - bedrock:InvokeModel
                Resource: !Sub "arn:aws:bedrock:${AWS::Region}::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0"

  NotifyStateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn:
        Fn::GetAtt: [ StateMachineRole, Arn ]
      DefinitionString: !Sub 
        - |-
          {
            "Comment": "A description of my state machine",
            "StartAt": "claude-3-5-sonnet-20240620",
            "States": {
              "claude-3-5-sonnet-20240620": {
                "Type": "Task",
                "Resource": "arn:aws:states:::bedrock:invokeModel",
                "Parameters": {
                  "ModelId": "arn:aws:bedrock:${Region}::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0",
                  "Body": {
                    "anthropic_version": "bedrock-2023-05-31",
                    "max_tokens": 4096,
                    "messages": [
                      {
                        "role": "user",
                        "content": [
                          {
                            "type": "text",
                            "text.$": "States.Format('${Prompt}---detail-type: {}\nsource: {}\nresources: {}\ndetail: {}', $.detail-type, $.source, $.resources, $.detail)"
                          }
                        ]
                      }
                    ]
                  }
                },
                "Next": "Call third-party API"
              },
              "Call third-party API": {
                "Type": "Task",
                "Resource": "arn:aws:states:::http:invoke",
                "Parameters": {
                  "ApiEndpoint": "https://slack.com/api/chat.postMessage",
                  "Method": "POST",
                  "Authentication": {
                    "ConnectionArn": "${ConnectionArn}"
                  },
                  "RequestBody": {
                    "channel": "${ChannelId}",
                    "blocks": [
                      {
                        "type": "section",
                        "text": {
                          "type": "mrkdwn",
                          "text.$": "$.Body.content[0].text"
                        }
                      }
                    ]
                  }
                },
                "Retry": [
                  {
                    "ErrorEquals": [
                      "States.ALL"
                    ],
                    "BackoffRate": 2,
                    "IntervalSeconds": 1,
                    "MaxAttempts": 3,
                    "JitterStrategy": "FULL"
                  }
                ],
                "End": true
              }
            }
          }
        - Region: !Sub "${AWS::Region}"
          Prompt: !Ref Prompt
          ConnectionArn: !Sub "${SlackConnection.Arn}"
          ChannelId: !Ref ChannelId

動作確認

では、実際にEventBridgeルールを作成し、イベント情報のサマリが行われるかを確認してみます。

今回は、以下のような Security Hub Findingsのイベントに対して通知を行ってみます。

イベントパターン
{
  "source": ["aws.securityhub"],
  "detail-type": ["Security Hub Findings - Imported"],
  "detail": {
    "findings": {
      "ProductFields": {
        "StandardsArn": ["arn:aws:securityhub:::standards/aws-foundational-security-best-practices/v/1.0.0"]
      },
      "Compliance": {
        "Status": ["FAILED"]
      }
    }
  }
}

ターゲットには、CloudFormationで作成したステートマシンを選択して、ルールの作成を完了させます。

スクリーンショット 2024-07-07 23.31.54

あとは、イベントが発生するように、AWSリソースの変更を行います。

イベント発生後、Step Functionsが実行され、以下のような通知が行われました。

スクリーンショット_2024-07-07_23_25_35

いい感じにサマリされていますね。

この構成で通知させる時の注意点

この構成であれば、Amazon Bedrock でいい感じにサマリを行ってくれますが、重要な情報が抜けてしまう可能性はゼロではありません。

また、通知頻度が多くなることが想定される場合は、この構成は向いていません。
通知のたびに、Amazon Bedrock の呼び出しが行われ、コスト増加につながる恐れがあります。

さいごに

Step Functions を利用することでサーバレスでイベント情報の要約を行い、Slack通知ができました。

この構成の使い所としては、検証環境など手っ取り早く通知構成を構築したい場合に有効かと思いますのでぜひ使ってみてください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.