mid-server について
MID Server の運用を扱う ServiceNow スキルです。ECC キュー通信、スクリプト実行、プローブとセンサー、リモートホストのオーケストレーション、状態監視を説明します。
主な機能
- 01ECC キュー経由のコマンド送信
- 02MID Server 上でのスクリプト実行
- 03プローブとセンサーの作成
- 04リモート PowerShell と SSH
- 05MID Server の状態監視
活用シーン
- 01MID Server と ECC キューの状態確認
- 02MID スクリプト実行手順のテスト
- 03カスタムディスカバリープローブとセンサーの設定
MCP 対応
snow_query_table による MID および ECC テーブルの照会、snow_artifact_manage によるプローブとセンサーの検索、snow_execute_script によるスクリプトテストを説明しています。
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ serac-labs/ serac --skill mid-serverインストール後、Claude Code で/mid-serverを入力して読み込まれたか確認します。
詳細
ServiceNow 用 MID サーバー
MID サーバーを使うと、ServiceNow からネットワーク内のリソースに通信できます。
MID サーバーのアーキテクチャ
ServiceNow Instance
↓ ECC Queue
MID Server
├── Probes (Outbound)
│ └── Commands sent TO MID
├── Sensors (Inbound)
│ └── Results sent FROM MID
└── Script Execution
主なテーブル
| テーブル | 用途 |
|---|---|
ecc_agent | MID サーバーのレコード |
ecc_queue | 入出力キュー |
discovery_probes | プローブ定義 |
discovery_sensors | センサー定義 |
sys_script_execution | スクリプト実行リクエスト |
ECC キュー通信(ES5)
MID サーバーにコマンドを送信
// Send command via ECC queue (ES5 ONLY!)
function sendMIDCommand(midServerName, command, parameters) {
var mid = getMIDServer(midServerName)
if (!mid) {
gs.error("MID Server not found: " + midServerName)
return null
}
var ecc = new GlideRecord("ecc_queue")
ecc.initialize()
// ECC Queue settings
ecc.setValue("agent", mid.getUniqueValue())
ecc.setValue("topic", "Command")
ecc.setValue("name", command)
ecc.setValue("source", "CustomScript")
// Parameters as payload
ecc.setValue("payload", buildPayload(parameters))
// Direction: output = to MID, input = from MID
ecc.setValue("queue", "output")
// State
ecc.setValue("state", "ready")
return ecc.insert()
}
function getMIDServer(name) {
var mid = new GlideRecord("ecc_agent")
mid.addQuery("name", name)
mid.addQuery("status", "Up")
mid.query()
if (mid.next()) {
return mid
}
return null
}
function buildPayload(params) {
var xml = '<?xml version="1.0" encoding="UTF-8"?>'
xml += "<parameters>"
for (var key in params) {
if (params.hasOwnProperty(key)) {
xml += '<parameter name="' + key + '">'
xml += "<value>" + escapeXML(params[key]) + "</value>"
xml += "</parameter>"
}
}
xml += "</parameters>"
return xml
}
function escapeXML(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
}
ECC の応答を監視
// Wait for ECC queue response (ES5 ONLY!)
function waitForECCResponse(eccSysId, timeoutSeconds) {
var startTime = new Date().getTime()
var timeout = timeoutSeconds * 1000
while (true) {
// Check for response
var response = new GlideRecord("ecc_queue")
response.addQuery("response_to", eccSysId)
response.addQuery("queue", "input")
response.query()
if (response.next()) {
return {
success: response.getValue("state") === "processed",
payload: response.getValue("payload"),
error: response.getValue("error_string"),
}
}
// Check timeout
if (new Date().getTime() - startTime > timeout) {
return {
success: false,
error: "Timeout waiting for MID response",
}
}
// Wait before retry
gs.sleep(1000)
}
}
MID サーバーのスクリプト(ES5)
MID でスクリプトを実行
// Execute JavaScript on MID Server (ES5 ONLY!)
function executeMIDScript(midServerName, script) {
var mid = getMIDServer(midServerName)
if (!mid) {
return { success: false, error: "MID Server not found" }
}
var ecc = new GlideRecord("ecc_queue")
ecc.initialize()
ecc.setValue("agent", mid.getUniqueValue())
ecc.setValue("topic", "JSProbe")
ecc.setValue("name", "Custom Script Execution")
// Script payload
var payload = '<?xml version="1.0" encoding="UTF-8"?>'
payload += "<parameters>"
payload += '<parameter name="script"><value><![CDATA[' + script + "]]></value></parameter>"
payload += "</parameters>"
ecc.setValue("payload", payload)
ecc.setValue("queue", "output")
ecc.setValue("state", "ready")
var eccSysId = ecc.insert()
return {
success: true,
ecc_sys_id: eccSysId,
}
}
// Example: Check disk space on remote server
var diskCheckScript = [
"var output = {};",
"try {",
' var cmd = "df -h /";',
" var result = Packages.com.service_now.mid.probe.tpcon.OperatingSystemCommand.execute(cmd);",
" output.disk_info = result.getOutput();",
" output.success = true;",
"} catch (e) {",
" output.success = false;",
" output.error = e.message;",
"}",
"output;",
].join("\n")
カスタムプローブ(ES5)
プローブを作成
// Create custom probe (ES5 ONLY!)
var probe = new GlideRecord("discovery_probes")
probe.initialize()
probe.setValue("name", "Custom Application Status")
probe.setValue("short_description", "Check custom application status")
probe.setValue("active", true)
// Probe type
probe.setValue("mid_type", "probe")
// Trigger type
probe.setValue("triggered_by", "on_demand")
// Probe script (runs on MID Server - ES5/Rhino!)
probe.setValue(
"script",
"var output = {};\n" +
"\n" +
"// Get parameters\n" +
'var host = probe.getParameter("host");\n' +
'var port = probe.getParameter("port");\n' +
"\n" +
"try {\n" +
" // Execute health check\n" +
' var url = "http://" + host + ":" + port + "/health";\n' +
" var conn = new java.net.URL(url).openConnection();\n" +
' conn.setRequestMethod("GET");\n' +
" conn.setConnectTimeout(5000);\n" +
" conn.setReadTimeout(5000);\n" +
" \n" +
" var responseCode = conn.getResponseCode();\n" +
' output.status = (responseCode === 200) ? "healthy" : "unhealthy";\n' +
" output.response_code = responseCode;\n" +
" output.success = true;\n" +
"} catch (e) {\n" +
' output.status = "unreachable";\n' +
" output.error = e.message;\n" +
" output.success = false;\n" +
"}\n" +
"\n" +
"output;",
)
probe.insert()
センサーを作成
// Create sensor to process probe results (ES5 ONLY!)
var sensor = new GlideRecord("discovery_sensors")
sensor.initialize()
sensor.setValue("name", "Process Application Status")
sensor.setValue("short_description", "Process custom application status results")
sensor.setValue("active", true)
// Link to probe
sensor.setValue("triggered_by", probeSysId)
// Sensor script (runs on ServiceNow instance - ES5!)
sensor.setValue(
"script",
"(function process(result, source) {\n" +
" var output = JSON.parse(result.output);\n" +
" \n" +
" // Find or create application CI\n" +
' var appCI = new GlideRecord("cmdb_ci_appl");\n' +
' appCI.addQuery("name", source.getParameter("app_name"));\n' +
" appCI.query();\n" +
" \n" +
" if (!appCI.next()) {\n" +
" appCI.initialize();\n" +
' appCI.name = source.getParameter("app_name");\n' +
" appCI.insert();\n" +
" }\n" +
" \n" +
" // Update status\n" +
" appCI.u_health_status = output.status;\n" +
" appCI.u_last_health_check = new GlideDateTime();\n" +
" appCI.update();\n" +
" \n" +
" // Create event if unhealthy\n" +
' if (output.status !== "healthy") {\n' +
" createHealthAlert(appCI, output);\n" +
" }\n" +
"})(result, source);",
)
sensor.insert()
オーケストレーション(ES5)
リモート PowerShell
// Execute PowerShell on Windows server (ES5 ONLY!)
function executeRemotePowerShell(midServerName, targetHost, script, credential) {
var payload = [
'<?xml version="1.0" encoding="UTF-8"?>',
"<parameters>",
' <parameter name="host"><value>' + targetHost + "</value></parameter>",
' <parameter name="credential_id"><value>' + credential + "</value></parameter>",
' <parameter name="script"><value><![CDATA[' + script + "]]></value></parameter>",
"</parameters>",
].join("\n")
var mid = getMIDServer(midServerName)
if (!mid) {
return { success: false, error: "MID Server not found" }
}
var ecc = new GlideRecord("ecc_queue")
ecc.initialize()
ecc.setValue("agent", mid.getUniqueValue())
ecc.setValue("topic", "PowerShell")
ecc.setValue("name", "Remote PowerShell Execution")
ecc.setValue("payload", payload)
ecc.setValue("queue", "output")
ecc.setValue("state", "ready")
return {
success: true,
ecc_sys_id: ecc.insert(),
}
}
リモート SSH
// Execute SSH command (ES5 ONLY!)
function executeRemoteSSH(midServerName, targetHost, command, credential) {
var payload = [
'<?xml version="1.0" encoding="UTF-8"?>',
"<parameters>",
' <parameter name="host"><value>' + targetHost + "</value></parameter>",
' <parameter name="credential_id"><value>' + credential + "</value></parameter>",
' <parameter name="command"><value>' + escapeXML(command) + "</value></parameter>",
"</parameters>",
].join("\n")
var mid = getMIDServer(midServerName)
if (!mid) {
return { success: false, error: "MID Server not found" }
}
var ecc = new GlideRecord("ecc_queue")
ecc.initialize()
ecc.setValue("agent", mid.getUniqueValue())
ecc.setValue("topic", "SSHCommand")
ecc.setValue("name", "Remote SSH Execution")
ecc.setValue("payload", payload)
ecc.setValue("queue", "output")
ecc.setValue("state", "ready")
return {
success: true,
ecc_sys_id: ecc.insert(),
}
}
MID サーバーの監視(ES5)
MID のステータスを確認
// Get MID Server status (ES5 ONLY!)
function getMIDServerStatus() {
var servers = []
var mid = new GlideRecord("ecc_agent")
mid.addQuery("status", "!=", "Decommissioned")
mid.query()
while (mid.next()) {
servers.push({
name: mid.getValue("name"),
status: mid.getValue("status"),
ip: mid.getValue("ip_address"),
version: mid.getValue("version"),
validated: mid.getValue("validated"),
last_refreshed: mid.getValue("last_refreshed"),
host_name: mid.getValue("host_name"),
})
}
return servers
}
MCP ツールとの連携
利用可能なツール
| ツール | 用途 |
|---|---|
snow_query_table | MID および ECC テーブルをクエリ |
snow_artifact_manage (action='find') | プローブ/センサーを検索 |
snow_execute_script | MID スクリプトをテスト |
ワークフローの例
// 1. Check MID Server status
await snow_query_table({
table: "ecc_agent",
query: "status=Up",
fields: "name,ip_address,version,last_refreshed",
})
// 2. Query ECC queue
await snow_query_table({
table: "ecc_queue",
query: "queue=output^state=ready",
fields: "agent,topic,name,state,sys_created_on",
})
// 3. Find probes
await snow_query_table({
table: "discovery_probes",
query: "active=true",
fields: "name,short_description,mid_type",
})
ベストプラクティス
- 認証情報 - スクリプトにハードコードしないでください
- エラー処理 - ネットワーク障害に対処してください
- タイムアウト - 適切なタイムアウトを設定してください
- ログ記録 - トラブルシューティング用にログを記録してください
- セキュリティ - MID へのアクセスを制限してください
- 監視 - MID の健全性を追跡してください
- 負荷分散 - HA のために複数の MID を使用してください
- ES5 のみ - 最新の JavaScript 構文は使用しないでください
コメント
Markdown 対応