ForgeVision Engineer Blog

フォージビジョン エンジニア ブログ

Amazon EventBridge の入力トランスフォーマーの使いこなし術

こんにちは、AWS グループの尾谷です。

先週、検証を行なっていて、Amazon EventBridge の「入力トランスフォーマー」 にドハマりしました。 認定試験でも出題されますし、座学では理解していたつもりでしたが、実際に触ってみると全然うまくいかず...

  • 公式ドキュメントには、Security Hub → EventBridge の入力トランスフォーマー設定方法が記載されていない ※1
  • Claude、ChatGPT に質問しても、エラーは通知漏れに遭遇
  • Perplexity の多くは Lambda を使う方法が多く紹介されていて、誘導されている印象
  • Amazon Q CLI は「入力トランスフォーマーがうまくいかないので、一致したイベントを使いましょう」と発言...

※1. ドキュメントに記載はなけれども、応用が効く「神チュートリアル」あり

Security Hub → EventBridge を試して手こずった結果、ここさえ押さえれば設定が通る

Security Hub → EventBridge 検証で、計 35 件セキュリティグループと 4 時間以上格闘した結果、“ここだけ押さえれば動く” といえる設定ポイントを掴めました。

  • 最短の学習方法はドキュメントだと思います!
  • サンドボックスで試せます
  • 事前定義済み変数「aws.events.event.json」を活用してトラシューがオススメ!
  • CloudWatch Alarms を設定してエラーに備えるか、SQS で DLQ を処理できます

EventBridge がイベントを受け渡す方法

一応、前提情報を。ご存知の方は読み飛ばしてください。

Amazon EventBridge がイベントを受け渡す方法を把握しておくと、入力トランスフォーマーの理解がスムーズです。
図を用いて、処理内容を説明します。

EventBridge のイベントパターンは、さまざまなサービスから送られてくる JSON 形式のイベントの中から、特定の条件に一致するものだけを検出して処理を行います。
その処理にあたる部分をターゲットで指定します。

入力トランスフォーマーとは

EventBridge のターゲットとして指定する入力トランスフォーマーは、EventBridge が受信した AWS のサービスが吐いた長い JSON イベントを、人間が読みやすい形に整形して別のサービスへ渡す機能を提供します。

長げーよ。。

不要なフィールドを除外したり、必要なデータだけ取り出して別フォーマットにしたり できる、とても便利な仕組みです。

おっ、分かりやっすい!

入力トランスフォーマーは、各サービスから受け取った JSON データをターゲットに送信する際に指定する機能で、主に「入力パス」と「テンプレート」の 2 部構成となっております。

  • 入力パス:JSON イベントから必要な値だけを取り出します
  • テンプレート:取り出した値を文字列として整形します

公式チュートリアルが一番の近道

それでは、入力トランスフォーマーを触っていきましょう!
操作を覚えるにあたり、真っ先におすすめするのは「公式ドキュメント」です。
このドキュメントには、チュートリアル的に手順が書かれていて、流し読みではなく 一文ずつ漏らさず、手を動かして読みながら操作すると理解できます。

docs.aws.amazon.com

『流し読みした実体験から、ここを強く言いたい!』

実際に触ってみる

「(上記の) ドキュメントを読んだけど、よく分からないよ。」という方、共感できます。流し読みしたり、キーワードを理解せぬまま読むと文章が入ってきません。

本ブログでは、このありがたいドキュメントをなぞる形で、スクリーンショット多めで解説をしていきます。

ステップ 1: 入力トランスフォーマーのありか

皆さま、入力トランスフォーマーがどこにあるか?ご存知でしょうか。
以下手順を進めていただくと見つかります。

  1. EventBridge のコンソールで [ルールを作成] をクリック
  2. ルールの一致条件は何でもOK → [次へ]
  3. サービスもなんでも良いので、とりあえず [Access Analyzer] などで OK → [次へ]
  4. [追加設定] を展開して、[一致したイベント][入力トランスフォーマー] に切り替えて、「入力トランスフォーマーを設定」ボタンをクリック

これで、入力トランスフォーマーが設定できます。

ステップ2: サンドボックス

ちなみに、EventBridge の管理コンソールに入力トランスフォーマーのサンドボックスが用意されています。

ステップ 1 でもステップ 2 でもどちらで操作していただいても大丈夫です。
実際の JSON データを使って、値の抽出と整形(=入力パスとテンプレート) の設定方法をみていきましょう。

ステップ3: サンプルイベント

以下を [サンプルイベント - オプション] に挿入してください。
[独自のサンプルイベントを入力] を選択すると、直接入力できるようになります。

{
  "version": "0",
  "id": "7bf73129-1428-4cd3-a780-95db273d1602",
  "detail-type": "EC2 Instance State-change Notification",
  "source": "aws.ec2",
  "account": "123456789012",
  "time": "2015-11-11T21:29:54Z",
  "region": "us-east-1",
  "resources": [
    "arn:aws:ec2:us-east-1:123456789012:instance/i-abcd1111"
  ],
  "detail": {
    "instance-id": "i-0123456789",
    "state": "RUNNING"
  }
}

ステップ4: 入力パス

入力パス に以下を挿入します。

{
  "timestamp" : "$.time",
  "instance" : "$.detail.instance-id", 
  "state" : "$.detail.state",
  "resource" : "$.resources[0]"
}

JSON データのルートが $ です。小要素は . (ドット) で繋ぎます。
以下のスクリーンショットを用いて説明しますと、

  • version の値を取得するには、$.version
  • resources は配列に入っているので、$.resources[0]
  • statedetail の小要素なので、$.detail.state

これは jq コマンドや、python などでコーディングしているとよく出てくる書き方です。

ステップ5: テンプレート

「テンプレート」とは「何のテンプレートか?」となりそうですが、「入力された情報を出力する際に利用するテンプレート」と補足すると伝わりますでしょうか。

要は、入力パスで取得した情報を「<>」で囲まれたプレースホルダーに載せて送信します。

例えば、以下のように入力して、

"instance <instance> is in <state>"

[出力を生成] ボタンをクリックすると、以下のメッセージが出力されます。

続いて、他サービスから届く JSON データの中身を把握する方法をご紹介します。

【超便利】事前定義済み変数を使って、JSON データの中身を把握!

ドキュメントでは、公式が提供している 5つの事前定義変数 が紹介されています。

  • aws.events.rule-arn
  • aws.events.rule-name
  • aws.events.event.ingestion-time
  • aws.events.event
  • aws.events.event.json

これらは JSON パスを自分で書かなくても使える魔法のキーワードで、特に、event を使うと、実際に受信する JSON データを確認でき、闇雲にマッピングする苦労から開放されます。

以下のスクリーンショットのように、前の手順で操作した入力トランスフォーマーのテンプレートを <aws.events.event.json> に変更してみてください。
イベントが直接出力されるのが確認できます。

入力トランスフォーマーのココがイケテナイ...

いかがでしょうか。
入力パスとテンプレートの構成自体はシンプルに見えるものの、実際に UI で動作させるとエラーになるケースがあり、その落とし穴に注意が必要です。

出力を生成は単なるシミュレーション。ルール更新時に構文エラー頻発!

まず、致命的な欠点として、入力トランスフォーマーの設定画面で、[出力を生成] がありますが、うまく出力できたからといって、設定が完了できるとは限りません。

問題なく出力されているのに!

上記設定を行い、入力トランスフォーマーの設定画面を閉じて、EventBridge ルールを更新しようとすると、初めてエラーが表示されます。
[出力を生成] ボタンは、単なる表示のシミュレーションで、構文チェックが曖昧なようです。

ルールの更新ボタンを押したらエラー。なんのチェックなの?

本エラーの対象法としては、テンプレートに {"originalEvent" : <aws.events.event.json>} と入力すると解決できます。

出力結果に、{"originalEvent":} が含まれてしまうので、除外が必要です。

'

出力されるイベントがファジー

サンプルイベントはたくさん用意されているものの、のサンプル前の手順で操作していただいた [入力トランスフォーマーを設定] ボタンをクリックすると、一番上に [サンプルイベント - オプション] が表示されると思います。

これを展開して、サンプルイベントを確認してください。

サンプルイベントには、実際に届く JSON データのサンプルがたくさん用意されているのですが、どのイベントが届くのか判りづらいです!

ブラックボックス感

例えば、Security Hub で検出するイベントのサンプルは 3 つ用意されています。

Security Hub CSPM でリスクを検出すると、一番下の [Security Hub Findings - Imported] が通知されますが、フレームワークによって若干の相違があります。さらに、リスクが解消されると、フォーマットが変わります。

この出力結果がフレームワーク、イベントのステータスによって異なるので、答えが分からないまま、通知されたりされなかったりを闇雲に検証していくは本当に辛いので、正解を受信できると救われます。

JSON データは欠損していても届く

JSON データの欠損は、問題なく送信できるということ判明したのでご紹介します。 以下、検証内容です。

Security Hub CSPM を有効にしているアカウントで、以下のようなインバウンドルールで SSH ポートを Any で全開放するセキュリティグループを作成すると、

数分後に検出結果にリスクが出力されます。

EC2.18EC2.19 は、AWS Foundational Security Best Practices (以降、セキュリティ・ベスプラ) にて検出されたリスクで、4.1 は CIS AWS Foundations Benchmark (以降、CIS ベンチマーク) にて検出されたリスクです。

CIS ベンチマーク4.1 と、セキュリティ・ベスプラ EC2.19 の検出結果をそれぞれ JSON エクスポートしてみます。

JSON データを比較すると、項目に差分があります。
例えば、CIS にある RelatedRequirements が、セキュリティ・ベスプラにはありません。

そのため、入力トランスフォーマーの入力パスに RelatedRequirements を設定をすると、CIS からの JSON データだと出力がされるものの、セキュリティ・ベスプラの JSON データだと [出力を生成] がエラーになってしまいます。

出力生成はエラーになりますが、実運用ではエラーなく通知できました。
セキュリティ・ベスプラの通知では、RelatedRequirements の値が空欄処理されています。

これも「出力生成」と「実際の設定」に乖離があることに起因する課題だと思います。

例外処理は SQS へ

主題からテーマが外れてしまうのですが、EventBridge が通知されない原因として、イベントパターンで検出できなかったケースもあります。

以下のイベントパターンは正しく設定できています。

ただ、以下の入力トランスフォーマーだと通知が届かず、

以下だと届きます。

サンドボックスだとどちらも判定 OK なので、何が間違えているかチェックするのが難しいです。。 (通った設定をテキストで以下に書き出しておきます。)

# 入力パス
{
  "description": "$.detail.findings[0].Description",
  "resource": "$.detail.findings[0].Resources[0].Id",
  "severity": "$.detail.findings[0].Severity.Label",
  "title": "$.detail.findings[0].Title"
}

# テンプレート
{
    "緊急度": "⭐️⭐️⭐️ 緊急対応発生 ⭐️⭐️⭐️",
    "タイトル": "<title>",
    "重要度": "<severity>",
    "説明": "<description>",
    "対象リソース": "<resource>"
}

各種生成 AI サービスが「より柔軟に対応したい場合は、Lambda を使いましょう。」と回答する理由が分かる気がします。

コンテンツの切り分けなどには対応しておらず、エラーになった場合は、EventBridge のモニタリングで FailedInvocations のメトリックが上昇します。この値をトリガーに CloudWatch Alarms を設定できそうです。

公式ドキュメントでは、SQS のデッドレターキューを利用する方法が推奨されています。

docs.aws.amazon.com

デッドレターキューは、以下スクリーンショットのように [入力トランスフォーマー] の下部にあります。
入力トランスフォーマーで処理エラーになり、Amazon SQS キューに送られたキューを SNS に飛ばして、メール通知するといった方法も取れそうです。

SQS に飛ばすと通知が届きました。
ERROR_CODE や、ERROR_MESSAGE から状況を把握して対策ができそうです。

その他のトラブルシューティング

他にもいろいろありそうです。

re:Post を覗いて確認してみたところ、「Invalid InputTemplate」エラーは、各行を二重引用符で囲む、とか、入力パスは 100 個までしか変数が処理できないから注意と言った情報も見つかりました。

repost.aws

まとめ

EventBridge 入力トランスフォーマーは、まだまだ課題が多いように感じます。
それは、サービスが未熟というよりも、連携できるサービスが多すぎて細かい対応が難しくオーケストレーションしきれていない感があります。
「複雑な処理は Lambda にお任せ、入力トランスフォーマーは簡単な処理だけ担当」というスタンスなら、エラーが出ないようにガイドラインを強めにした方が利用者は便利だと感じました。
もしかすると、GUI ではなくコーディングして実装するビルダーの比率が大きいため、UI に対してフィードバックが少ないのかも知れません。

僕はフィードバックしました。
皆さんも試していただき、不具合を見つけたら是非 AWS にフィードバックをご協力お願いします。

最後までお読みくださりありがとうございました。