0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【連載 第4回】ServiceNow標準機能だけで作る「Pull型」自動化連携 ― Table APIで実行待ちタスクを取得・確保する

0
Posted at

本記事は連載「ServiceNow標準機能だけで作るPull型自動化連携」の第4回です。
第3回では、Catalog Itemから依頼を出すと Open + autoengine1 のSCTASKが生成されるところまで作りました。
今回は、自動化エンジン側からOOTBのTable APIだけで、実行待ちのSCTASKを取得し、確保する部分を実装します。

この回で作るもの

ステップ 使うAPI 状態の変化
実行待ちの取得 Table API(GET) 変化なし
確保 Table API(PATCH) Open + autoengine1 → In Progress + autoengine1

この回で、ServiceNow側に新しく作るものはありません。OOTBのTable APIだけを使います。

1. 準備

1.1 必要なもの

  • 第3回で作成したCatalog Item、Execution Plan、自動化ユーザー
  • APIサービスユーザー svc_autoengine1_api
  • curl と jq が使えるLinux環境

1.2 接続設定

export SN_INSTANCE="https://<instance>.service-now.com"
export SN_USER="svc_autoengine1_api"
export SN_PASSWORD='********'

export TABLE_API_PATH="/api/now/table/sc_task"
export ASSIGNMENT_GROUP="autoengine1group"
export WAITING_ASSIGNEE="autoengine1"
export OPEN_STATE="1"
export IN_PROGRESS_STATE="2"

Stateの内部値(Open=1、In Progress=2)は、インスタンスによって異なる場合があります。第3回の動作確認で調べた値を設定してください。

2. 実行待ちのSCTASKを取得する

2.1 検索条件

実行待ちの条件は、第2回で決めた状態表現そのものです。

条件 Encoded Query
StateがOpen state=1
Assignment groupがautoengine1group assignment_group.name=autoengine1group
Assigned toがautoengine1 assigned_to.user_name=autoengine1
作成日時の古い順 ORDERBYsys_created_on

これらを ^(AND)でつなぎます。

state=1^assignment_group.name=autoengine1group^assigned_to.user_name=autoengine1^ORDERBYsys_created_on

グループとユーザーは、sys_idではなく名前(name、user_name)で指定しています。これにより、インスタンスが変わっても同じスクリプトをそのまま使えます。

2.2 curlで取得する

query="state=${OPEN_STATE}^assignment_group.name=${ASSIGNMENT_GROUP}^assigned_to.user_name=${WAITING_ASSIGNEE}^ORDERBYsys_created_on"

curl --fail-with-body --silent --show-error \
  --user "${SN_USER}:${SN_PASSWORD}" \
  --header "Accept: application/json" \
  --get "${SN_INSTANCE}${TABLE_API_PATH}" \
  --data-urlencode "sysparm_query=${query}" \
  --data-urlencode "sysparm_fields=sys_id,number" \
  --data-urlencode "sysparm_limit=10000" | jq .

sysparm_fields で、必要な sys_id と number だけを返すようにしています。変数などの詳細は、次回作るScripted REST APIで、作業の直前に取得します。

2.3 レスポンス例

{
  "result": [
    { "sys_id": "a1b2c3d4e5f60718293a4b5c6d7e8f90", "number": "SCTASK0010006" },
    { "sys_id": "0f9e8d7c6b5a49382716a5b4c3d2e1f0", "number": "SCTASK0010007" }
  ]
}

実行待ちがない場合は、result が空の配列になります。

3. SCTASKを確保する

3.1 確保とは

取得したSCTASKのStateを In Progress に変えることを、この連載では「確保」と呼びます。

Open + autoengine1  →  In Progress + autoengine1

確保すると、2章の検索条件(State=Open)に当てはまらなくなるので、次回のPullで同じSCTASKを再取得しません。Assigned toはこの時点では変えません。

3.2 curlで確保する

task_sys_id="a1b2c3d4e5f60718293a4b5c6d7e8f90"

curl --fail-with-body --silent --show-error \
  --user "${SN_USER}:${SN_PASSWORD}" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --request PATCH \
  --data "{\"state\":\"${IN_PROGRESS_STATE}\"}" \
  "${SN_INSTANCE}${TABLE_API_PATH}/${task_sys_id}" | jq '.result | {number, state}'

Table APIの更新はsys_idで対象を指定します。ここで使うsys_idは、直前のGETで受け取った値をそのまま渡すだけです。外部に公開するインターフェースやスクリプトの設定値として、sys_idを持つ必要はありません。

3.3 確保できたことを確認する

レスポンスの state が 2 になっていれば、確保できています。

{
  "number": "SCTASK0010006",
  "state": "2"
}

ServiceNowの画面では、SCTASKのStateが In Progress、Assigned toが autoengine1 のままになっていることを確認します。

4. 取得と確保をまとめたスクリプト

#!/usr/bin/env bash
set -Eeuo pipefail

: "${SN_INSTANCE:?set SN_INSTANCE}"
: "${SN_USER:?set SN_USER}"
: "${SN_PASSWORD:?set SN_PASSWORD}"

TABLE_API_PATH="${TABLE_API_PATH:-/api/now/table/sc_task}"
ASSIGNMENT_GROUP="${ASSIGNMENT_GROUP:-autoengine1group}"
WAITING_ASSIGNEE="${WAITING_ASSIGNEE:-autoengine1}"
OPEN_STATE="${OPEN_STATE:-1}"
IN_PROGRESS_STATE="${IN_PROGRESS_STATE:-2}"

api() {
    curl --fail-with-body --silent --show-error \
        --user "${SN_USER}:${SN_PASSWORD}" \
        --header "Accept: application/json" "$@"
}

get_open_tasks() {
    local query
    query="state=${OPEN_STATE}^assignment_group.name=${ASSIGNMENT_GROUP}^assigned_to.user_name=${WAITING_ASSIGNEE}^ORDERBYsys_created_on"
    api --get "${SN_INSTANCE}${TABLE_API_PATH}" \
        --data-urlencode "sysparm_query=${query}" \
        --data-urlencode "sysparm_fields=sys_id,number" \
        --data-urlencode "sysparm_limit=10000"
}

claim_task() {
    local task_sys_id="$1"
    api --header "Content-Type: application/json" \
        --request PATCH \
        --data "$(jq -n --arg state "$IN_PROGRESS_STATE" '{state:$state}')" \
        "${SN_INSTANCE}${TABLE_API_PATH}/${task_sys_id}"
}

main() {
    local tasks_json total index task_sys_id task_number claim_response

    tasks_json="$(get_open_tasks)"
    total="$(jq '.result | length' <<<"$tasks_json")"

    if [[ "$total" -eq 0 ]]; then
        echo "No Open + ${WAITING_ASSIGNEE} SCTASK found."
        exit 0
    fi

    for ((index=0; index<total; index++)); do
        task_sys_id="$(jq -r ".result[${index}].sys_id" <<<"$tasks_json")"
        task_number="$(jq -r ".result[${index}].number" <<<"$tasks_json")"

        echo "Claiming ${task_number}: state ${OPEN_STATE} -> ${IN_PROGRESS_STATE}"
        claim_response="$(claim_task "$task_sys_id")"

        if [[ "$(jq -r '.result.state // empty' <<<"$claim_response")" != "$IN_PROGRESS_STATE" ]]; then
            echo "ERROR: claim failed for ${task_number}." >&2
            jq . <<<"$claim_response" >&2
            exit 1
        fi

        echo "Claimed ${task_number}"
        # 次回: Scripted REST APIで詳細を取得し、実行開始(autoengine1 -> autoengine1running)
    done
}

main "$@"

実運用では、このスクリプトをcronや自動化エンジンのスケジュール機能で定期実行します。AAPを使う場合は、同じ処理をPlaybookの ansible.builtin.uri モジュールで書くこともできます。

5. なぜTable APIの単純な更新で確保するのか

確保の方法には、Scripted REST APIで「現在のStateがOpenの場合だけ更新する」条件付き更新を作る選択肢もあります。
本連載では、次の理由からTable APIの単純な更新を採用しました。

  • ServiceNow側の開発を増やさない: 確保のためだけにAPIを作ると、保守の対象が増えます。
  • ポーラーは1つの前提: ディスパッチャーが1つなら、同じSCTASKを2つの処理が同時に確保することはありません。
  • 二重実行は次のステップで防ぐ: 次回作る実行開始APIは、Assigned toが autoengine1 の場合だけ autoengine1running へ更新する条件付き更新(Compare-and-set)です。仮に同じSCTASKを2回確保しても、実際に作業を始められるのは1回だけです。
  • ポーラー(ディスパッチャー)を複数動かす場合は、確保の段階での排他制御を追加で設計してください。
  • 取得してから確保するまでの間に自動化エンジンが停止すると、同じSCTASKが次回も取得されます。自動化処理は、できるだけ冪等にしておくと安全です。
  • 確保したまま長時間 In Progress + autoengine1 に残ったSCTASKは、ServiceNowの標準機能(SLAやエスカレーション)で検知する想定です。

6. 動作確認

  • 実行待ちのSCTASKがある状態でGETすると、そのSCTASKの sys_id と number が返る
  • 他のグループや他の担当者のSCTASKは返らない
  • 実行待ちがない場合、result が空の配列になる
  • PATCH後、レスポンスの state が 2 になる
  • 画面上で、SCTASKが In Progress + autoengine1 になっている
  • 確保後に再度GETすると、確保したSCTASKは返らない

第7回で紹介するBusiness Ruleを設定すると、確保した時点でRITMのActivityに進捗が表示されるようになります。

次回予告

次回は、ServiceNow側にScripted REST APIを作成し、SCTASKの詳細(変数を含む)を取得して、実行開始を宣言する部分を実装します。expected_current_assigned_to を使った条件付き更新と、400・409のエラー設計を解説します。

連載記事


※ 本記事の内容は個人の見解であり、所属する組織の公式な見解ではありません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?