はじめに
QRadar SOARのサブプレイブックを使用すると、他のプレイブック内で使用する反復可能アクティビティーを定義できます。親プレイブックからの入力データに対してアクションを実行するようにサブプレイブックを設計すると、まるで関数のようにプレイブックの構成要素を呼び出すことができるようになります。
サブプレイブックは入れ子にすることもでき、サブプレイブックの中にサブプレイブックを置くこともできます。
具体的な例を使って、サブプレイブックの作り方を見ていきましょう。
トップに戻る
サブプレイブックの作り方
名前に「プレイブック」が入っていますが、サブプレイブックはプレイブックと同格ではなく、プレイブックに配置する部品のような位置づけです。タスクや関数、スクリプトなどと同じように、Playbook DesignerのGUIから作成します。
そのため、何もないところにいきなりサブプレイブックを作成することはできません。最初に必ず何らかの「プレイブック」を作成し、その中から「サブプレイブック」を作成する必要があります。
しかし、一度作成したサブプレイブックは、作成したプレイブックからだけでなく、どのプレイブックからでも呼び出すことができます。単に作り方の作法として、先に「プレイブック」が必要ということです。
どうせプレイブックを先に作る必要があるならば、「サブプレイブックの単体テスト用のプレイブック」を作成し、その中に目的のサブプレイブックを作成することをおすすめします。つまり、サブプレイブックと、そのサブプレイブックの単体テストを行うためのラッパーとなるプレイブックを、ペアで作成するわけです。
(複数のサブプレイブックに依存関係がある場合は、一対一にこだわらずに、1つのプレイブック内で複数のサブプレイブックを開発してもよいと思います)
「手動」のプレイブックとして単体テスト用のプレイブックを作成しておけば、開発中のテストを行いやすくなります。また、リリース後にサブプレイブックの機能拡張を行う際にも利用できます。
トップに戻る
ラッパー・プレイブックの構成要素
正式なSOAR用語ではありませんが、このブログでは、サブプレイブックの親プレイブック(単体テスト用)を、ラッパー・プレイブックと呼ぶことにします。
ラッパー・プレイブックには、以下の構成要素があると便利です。
- アクティベーション・フォーム
- 手動プレイブックに特有の機能で、呼び出し時にユーザーにフォームを表示し、必要なデータを入力させることができます。
- 単体テストでアドホックなデータを使いたい場合には、この方式が役に立ちます。
- 前処理スクリプト
- サブプレイブックに与える引数を準備するためのスクリプトです。
- アクティベーション・フォームの値をそのまま引数に与えればよいだけならば、省略することも可能です。入力値の形式を変換したり、その値をベースに別のテストデータを生成したりする場合は、スクリプトの併用が必要になります。
- 後処理スクリプト
- サブプレイブックからの戻り値を処理するためのスクリプトです。
- 入力値に対応する戻り値が得られたかどうか、チェックする機能をもたせることができます。
- 戻り値を人間が確認するために、単にテキストを表示するだけで要件を満たす場合もあるでしょう。その場合は、シングルラインの簡単なスクリプトを書くだけです。( incident.addNote(str(戻り値の変数名)) で注記に文字列を表示するなど)
- その他の構成要素
- 通常のプレイブックと同じように、タスクや分岐を配置することもできます。作成中のサブプレイブックの仕様に併せて、必要なテストを実施できるようにデザインしてください。
トップに戻る
サンプル:土日祝日判定サブプレイブック
それでは、サブプレイブックとそのラッパー・プレイブックの概要を、具体的な例を通じて見ていきましょう。
プレイブックの仕様
土日祝日判定サブプレイブックの仕様と実装方法
ラッパー・プレイブックの仕様と実装方法
- インシデント・メニューから呼び出されます。
- 呼び出されると、アクティベーション・フォームのカレンダーUI経由で、ユーザーからの日付入力を受け付けます。
- フォームから入力された日付はエポック秒形式となっており、土日祝日判定サブプレイブックのパラメーターとして渡されます。エポック秒は、SOARの内部で「数値」として扱われ、Pythonのdatetimeライブラリーによって日付として処理されます。
- 土日祝日判定サブプレイブックからの戻り値は、インシデントの注記としてテキストで書き込まれます。
トップに戻る
ラッパー・プレイブック
ラッパー・プレイブックの全体像
前述のように、サブプレイブックはプレイブックの中から作成するため、先にプレイブックが存在する必要があります。そのため、まず「ラッパー・プレイブック」から先に説明します。
ラッパー・プレイブックの全体像は以下のとおりです。
今回の例では、「サブプレイブックの入力」を「スクリプト」形式で記述しているため、独立した「前処理スクリプト」は不要となっています。
トップに戻る
ラッパー・プレイブックのトリガー
ラッパー・プレイブックは、サブプレイブックの単体テストを目的としているため、任意のタイミングで「手動」で呼び出せるほうが便利です。
また、アクティベーション・フォームを使って、テストデータをアドホックに与えることができるようにしています。
- アクティブ化のタイプ:手動
- オブジェクト・タイプ:インシデント
- アクティベーション・フォーム
- フォーム・エレメント・タイプ:日付ピッカー
- 入力フィールドラベル:チェック対象日付
- API名:date_to_check
- 条件:なし
- 自動キャンセル:なし
動画:ラッパー・プレイブックの作成からアクティベーション・フォームの設定まで
トップに戻る
サブプレイブック 「土日祝日判定」の呼び出し
サブプレイブックは、パラメーターとなる「入力」と、戻り値となる「出力」を持ち、関数のように使用することができます。
この例には登場しませんが、ユーザーに指示を出したりフィールドへの値入力を促したりできる「タスク」を配置することで、対話型の処理を行うこともできます。
ラッパー・プレイブックに既存のサブプレイブックを追加するには、以下のステップを実行します。
※全体の流れを理解していただくため、作成済みの「土日祝日判定」サブプレイブックを、ラッパー・プレイブックに配置する部分を先に説明しています。「土日祝日判定」サブプレイブックを新規作成する流れについては、後のセクションで説明します。
- プレイブック・ライブラリーの 「サブプレイブック」 アイコンをクリックします。
- リストを展開して、サブプレイブックを表示します。ライブラリーには、ラッパー・プレイブックのオブジェクト・タイプと互換性のあるオブジェクト・タイプを持つサブプレイブックのみが表示されます。
- サブプレイブックをキャンバスにドラッグするか、プラス (+) アイコンをクリックします。
- キャンバスで、サブプレイブックを選択して構成ウィンドウを開きます。
- 以下の項目を入力します。
- サブプレイブック出力
- サブプレイブックの入力:スクリプト
inputs.date_to_check_epoc = playbook.inputs.date_to_check
動画:ラッパー・プレイブックへのサブプレイブックの配置
トップに戻る
後処理スクリプト 「戻り値のチェック」
サブプレイブック「土日祝日判定」の実行結果を受け取って処理するためのスクリプトです。
この例では、戻り値のチェック処理を記述するのではなく、単に注記に出力しています。
incident.addNote(str(playbook.subplaybooks.results.output))
動画:ラッパー・プレイブックへの後処理スクリプトの配置
トップに戻る
終了点
終了点(エンドポイント)は、プレイブック全体の完了、またはプレイブック内の特定のパスの完了を示します。どのプレイブックにも、1つ以上の終了点を配置する必要があります。
動画:ラッパー・プレイブックへの終了点の配置
トップに戻る
土日祝日判定サブプレイブック
土日祝日判定サブプレイブックの全体像
土日祝日判定サブプレイブックの全体像は、以下のとおりです。
ラッパー・プレイブックに新しいサブプレイブックを追加する(作成する)には、以下のステップを実行します。
- プレイブック・ライブラリーの 「サブプレイブック」 アイコンをクリックします。
- リストの最下部にある 「サブプレイブックの作成 +」 をクリックします。
- 名前とAPI名を入力して、「作成」をクリックします。
トップに戻る
土日祝日判定サブプレイブックの開始点
最初に、開始点を設定します。
「入力」部分で、呼び出し元のプレイブックから受け取る、パラメーターの定義を行います。
- オブジェクト・タイプ:インシデント
- 入力フォーム・レイアウト
- フォーム・エレメント・タイプ:数値 (日付をエポック秒で受け取るため)
- 入力フィールドラベル:チェック対象日付
- API名:date_to_check_epoc
「出力」部分の定義はオプションで、文書化のために使用します。
実際に呼び出し元に返される戻り値は、後述の「終了点」で定義します。
動画:サブプレイブックの作成と引数の定義
トップに戻る
関数呼び出し (REST API)
今回のブログは、REST API呼び出し関数の解説を目的としたものではないため、主要な定義部分のみ抜粋します。
method = "GET"
url = "https://holidays-jp.github.io/api/v1/datetime.json"
header = None
body = None
cookie = None
verify = False
timeout = 60
allowed_status_code = "200"
動画:サブプレイブックからのREST API呼び出し
トップに戻る
後処理スクリプト 「週末判定とREST API結果の祝日判定」
パラメーターとして与えられたエポック秒から土日判定を行うロジックと、直前のREST API呼び出し関数からの戻り値を処理するロジックを記述します。
処理結果の受け渡し方法として、一時変数としてのプレイブック・プロパティーを使用しています。
playbook.addProperty("result", Python
辞書形式
)
の部分です。
プロパティーに格納された値は、サブプレイブックの戻り値を定義する「終了点」のスクリプト内で取り出されます。この部分は後述します。
import datetime
result = playbook.functions.results.output
if not result.success:
playbook.addProperty("result", { "success": False, "reason": result.content.reason })
else:
# JSONデータもPython辞書形式の中で返されるため、JSON->DICTの変換は不要
holidays = result.content.json
if not playbook.inputs.date_to_check_epoc: # フォームから指定しなかった場合は現在日時を使用
target_date = datetime.datetime.now(datetime.timezone.utc)
# サーバーがUTCであることを想定
target_date = target_date.astimezone(datetime.timezone(datetime.timedelta(hours=9)))
target_date = target_date.replace(hour=0, minute=0, second=0)
else:
target_date = datetime.datetime.fromtimestamp(playbook.inputs.date_to_check_epoc / 1000)
# サーバーがUTCであることを想定
target_date = target_date.astimezone(datetime.timezone(datetime.timedelta(hours=9)))
target_date = target_date.replace(hour=0, minute=0, second=0)
# 0:"月", 1:"火", 2:"水", 3:"木", 4:"金", 5:"土", 6:"日"
day_of_week = target_date.weekday()
if day_of_week == 5 or day_of_week == 6:
weekend = True
else:
weekend = False
target_date_epoc = str(int(target_date.timestamp()))
if target_date_epoc in holidays.keys():
playbook.addProperty("result", { "success": True, "target_date": target_date, "holiday": holidays[target_date_epoc], "weekend": weekend })
else:
playbook.addProperty("result", { "success": True, "target_date": target_date, "holiday": False, "weekend": weekend })
動画:サブプレイブックへの後処理スクリプトの配置
トップに戻る
土日祝日判定サブプレイブックの終了点
サブプレイブックの終了点は、呼び出し元の親プレイブックに返す値を定義する、重要な構成要素です。
前のスクリプトによってプロパティーに格納された値を取り出し、戻り値としてセットしています。
if playbook.properties.result.success:
playbook.results = {
"success": True,
"results": playbook.properties.result
}
else:
playbook.results = {
"success": False,
"results": playbook.properties.result
}
動画:サブプレイブックへの終了点の配置と戻り値の設定
トップに戻る
ラッパー・プレイブックの実行確認
サブプレイブックとラッパー・プレイブックの両方が完成したら、実際に単体テストを実行してみます。
既に開発済みの定義内容を入力しているため、このブログの動画では1回でエラーなく実施できているように見えますが、実際の開発では、ケアレスミスなどを含めて多少のエラーは出るものです。
そのような開発時の繰り返し作業において、ラッパー・プレイブックによる単体テストが効果を発揮します。
プレイブックを実行するためには、ベースとなる「ケース」が必要ですので、それを先に作成します。ケースは、シミュレーションでも構いません。
作成したケースのメニューから今回作成したラッパー・プレイブックの名前を選択すると、アクティベーション・フォームが表示されます。
アクティベーション・フォームに入力した日付はサブプレイブックに渡され、その日が祝日かどうか、土日かどうか、判定した結果が返されます。
返された結果は、ケースの「注記」にテキストで出力されますので、想定通りの結果かどうかを確認することができます。
動画:サブプレイブック単体テストの実行
トップに戻る
参考文献