script-execution について
ServiceNow の管理者や開発者向けのスクリプト実行スキルです。MCP ツールで対応できない操作や複雑なデータ処理に使うサーバーサイド JavaScript の実行方法を、ログやデバッグ、修正スクリプトとともに説明します。
主な機能
- 01sys_trigger によるスクリプト自動実行
- 02読み取り・更新・挿入・削除のパターン
- 03ログ記録と実行結果の確認
- 04バッチ処理と非同期実行
- 05修正スクリプト作成とトラブルシューティング
活用シーン
- 01MCP ツールで未対応の ServiceNow 操作
- 02ServiceNow レコードの一括処理や更新
- 03ログを使ったスクリプトのテストとエラー調査
- 04監査可能な修正スクリプトの作成
MCP 対応
SN-Execute-Background-Script などの MCP ツールでサーバーサイド JavaScript を実行し、ログの照会や修正スクリプトの作成も行います。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ happy-technologies-llc/ happy-platform-skills --skill script-executionインストール後、Claude Code で/script-executionを入力して読み込まれたか確認します。
詳細
スクリプトの実行
概要
このスキルでは、ServiceNow でバックグラウンドスクリプトを安全かつ効果的に実行する方法を説明します。
- sys_trigger を使用したスクリプトの自動実行(画期的な方法)
- 安全なスクリプトのパターンとテンプレート
- 手動実行用の修正スクリプトの生成
- エラー処理とログ記録のベストプラクティス
- デバッグとトラブルシューティングの手法
使用する場面: MCP ツールで必要な操作に対応できない場合、または複雑なデータ操作が必要な場合です。
対象ユーザー: ServiceNow でサーバーサイド JavaScript を実行する必要がある管理者と開発者です。
前提条件
- ロール:
admin(バックグラウンドスクリプトの実行には完全な管理者権限が必要です) - アクセス: sys_trigger、syslog、sys_script_fix テーブル
- 知識: GlideRecord API、ServiceNow のサーバーサイド JavaScript
- 注意: バックグラウンドスクリプトは昇格された権限で実行されるため、慎重にテストしてください
実行方法
方法の比較
| 方法 | 自動化 | 速度 | 使用例 |
|---|---|---|---|
| sys_trigger | 完全 | 約1~2秒 | すべての自動タスクに推奨 |
| UI endpoint | 完全 | 約1~2秒 | trigger が失敗した場合の代替手段 |
| Fix Script | 手動 | 可変 | 監査証跡、スケジュール実行 |
| Scripts - Background | 手動 | 即時 | 1回限りの対話型テスト |
手順
フェーズ 1:自動実行(推奨)
ステップ 1.1:基本的なスクリプト実行
sys_trigger を使用する方法では、すぐに実行され、実行後に自動削除されるスケジュール済みジョブを作成します。
MCP を使用する場合:
Tool: SN-Execute-Background-Script
Parameters:
script: |
gs.info('Hello from automated script execution!');
// Your logic here
var count = 0;
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.addQuery('priority', 1);
gr.setLimit(10);
gr.query();
while (gr.next()) {
count++;
gs.info('Found P1 incident: ' + gr.number);
}
gs.info('Total P1 incidents found: ' + count);
description: Count active P1 incidents
execution_method: trigger
仕組み:
sys_triggerテーブルにスケジュール済みジョブを作成します- next_action を即時実行に設定します
- trigger がスクリプトを実行します
- スクリプトはシステムログに出力を記録します
- 実行後、trigger は自動的に削除されます
ステップ 1.2:実行を確認する
システムログを確認する:
Tool: SN-Query-Table
Parameters:
table_name: syslog
query: message=*automated script*^sys_created_on>javascript:gs.minutesAgo(5)
fields: message,level,sys_created_on,source
limit: 20
ログレベル:
| レベル | メソッド | 目的 |
|---|---|---|
| 0 | gs.info() | 情報メッセージ |
| 1 | gs.warn() | 警告メッセージ |
| 2 | gs.error() | エラーメッセージ |
| 3 | gs.debug() | デバッグメッセージ(有効な場合) |
フェーズ 2:安全なスクリプトのパターン
ステップ 2.1:クエリのパターン(読み取り専用)
データを読み取るための安全なパターンです。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Safe read-only pattern
var results = [];
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.addQuery('priority', 1);
gr.setLimit(100); // Always set a limit
gr.query();
while (gr.next()) {
results.push({
number: gr.number.toString(),
short_description: gr.short_description.toString(),
assigned_to: gr.assigned_to.getDisplayValue(),
created: gr.sys_created_on.toString()
});
}
gs.info('Query Results: ' + JSON.stringify(results, null, 2));
description: Query active P1 incidents (read-only)
ステップ 2.2:更新のパターン(安全確認付き)
安全対策を講じてレコードを更新するためのパターンです。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Safe update pattern with validation
var TABLE = 'incident';
var QUERY = 'active=true^priority=1^assigned_toISEMPTY';
var MAX_UPDATES = 10; // Limit updates per execution
// DRY RUN flag - set to false to actually update
var DRY_RUN = true;
var updated = 0;
var skipped = 0;
var gr = new GlideRecord(TABLE);
gr.addEncodedQuery(QUERY);
gr.setLimit(MAX_UPDATES);
gr.query();
gs.info('Found ' + gr.getRowCount() + ' records to process');
while (gr.next()) {
// Validate before update
if (!gr.canWrite()) {
gs.warn('Cannot write to: ' + gr.number);
skipped++;
continue;
}
if (DRY_RUN) {
gs.info('[DRY RUN] Would update: ' + gr.number);
} else {
gr.work_notes = 'Automated: Escalating unassigned P1';
gr.assignment_group = 'Critical Incidents Team';
gr.update();
gs.info('Updated: ' + gr.number);
updated++;
}
}
gs.info('Summary: Updated=' + updated + ', Skipped=' + skipped + ', DryRun=' + DRY_RUN);
description: Escalate unassigned P1 incidents (with dry run)
ステップ 2.3:挿入のパターン(重複チェック付き)
レコードを安全に作成するためのパターンです。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Safe insert pattern with duplicate check
var TABLE = 'sys_properties';
var PROPERTY_NAME = 'custom.automation.enabled';
var PROPERTY_VALUE = 'true';
// Check for existing
var existing = new GlideRecord(TABLE);
existing.addQuery('name', PROPERTY_NAME);
existing.query();
if (existing.next()) {
gs.warn('Property already exists: ' + PROPERTY_NAME + ' = ' + existing.value);
// Update if different
if (existing.value != PROPERTY_VALUE) {
existing.value = PROPERTY_VALUE;
existing.update();
gs.info('Updated property value to: ' + PROPERTY_VALUE);
}
} else {
// Create new
var gr = new GlideRecord(TABLE);
gr.initialize();
gr.name = PROPERTY_NAME;
gr.value = PROPERTY_VALUE;
gr.description = 'Created by automated script';
var sysId = gr.insert();
gs.info('Created property: ' + PROPERTY_NAME + ' (sys_id: ' + sysId + ')');
}
description: Create or update system property
ステップ 2.4:削除のパターン(確認付き)
最大限の注意を払ってレコードを削除するためのパターンです。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// DANGEROUS: Delete pattern - use with extreme caution
var TABLE = 'sys_user_preference';
var QUERY = 'user.active=false^sys_created_on<javascript:gs.daysAgo(365)';
var MAX_DELETES = 10;
// SAFETY: Always start with dry run
var DRY_RUN = true;
var CONFIRMATION_CODE = 'DELETE_CONFIRMED_2026';
var PROVIDED_CODE = 'INTENTIONALLY_WRONG'; // Must match to delete
if (!DRY_RUN && PROVIDED_CODE !== CONFIRMATION_CODE) {
gs.error('SAFETY: Confirmation code mismatch. Aborting delete.');
return;
}
var gr = new GlideRecord(TABLE);
gr.addEncodedQuery(QUERY);
gr.setLimit(MAX_DELETES);
gr.query();
var count = 0;
var deleted = [];
while (gr.next()) {
count++;
if (DRY_RUN) {
gs.info('[DRY RUN] Would delete: ' + gr.sys_id + ' - ' + gr.name);
} else {
deleted.push(gr.sys_id.toString());
gr.deleteRecord();
}
}
if (DRY_RUN) {
gs.info('[DRY RUN] Would delete ' + count + ' records');
} else {
gs.info('Deleted ' + deleted.length + ' records: ' + JSON.stringify(deleted));
}
description: Delete old user preferences (dry run mode)
フェーズ 3:修正スクリプトの生成
ステップ 3.1:監査証跡用の修正スクリプトを作成する
監査証跡やスケジュール実行が必要な場合に使用します。
MCP を使用する場合:
Tool: SN-Create-Fix-Script
Parameters:
name: FIX_IncidentDataCleanup_20260206
script: |
// Fix Script: Clean up old resolved incidents
// Author: Admin
// Date: 2026-02-06
// JIRA: PROJ-123
var cutoffDays = 90;
var cutoffDate = gs.daysAgo(cutoffDays);
gs.info('=== Starting Incident Cleanup ===');
gs.info('Cutoff date: ' + cutoffDate);
var gr = new GlideRecord('incident');
gr.addQuery('state', 'IN', '6,7,8'); // Resolved, Closed, Cancelled
gr.addQuery('sys_updated_on', '<', cutoffDate);
gr.query();
gs.info('Found ' + gr.getRowCount() + ' incidents to archive');
// Archive logic here
gs.info('=== Cleanup Complete ===');
description: Clean up old resolved incidents for data hygiene
修正スクリプトを手動で作成する:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_fix
data:
name: FIX_IncidentDataCleanup_20260206
script: |
// Script content here
description: Clean up old resolved incidents
active: true
ステップ 3.2:修正スクリプトをクエリする
既存の修正スクリプトを検索する:
Tool: SN-Query-Table
Parameters:
table_name: sys_script_fix
query: active=true
fields: name,description,sys_created_on,run_count
limit: 50
フェーズ 4:エラー処理
ステップ 4.1:try-catch パターン
エラーを処理するため、スクリプトは必ず try-catch で囲みます。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Comprehensive error handling pattern
var scriptName = 'UpdateIncidentPriorities';
var startTime = new GlideDateTime();
try {
gs.info('[' + scriptName + '] Starting execution');
// Main logic
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.setLimit(100);
gr.query();
var processed = 0;
var errors = 0;
while (gr.next()) {
try {
// Process each record
processed++;
// ... logic here
} catch (recordError) {
errors++;
gs.error('[' + scriptName + '] Error processing ' + gr.number + ': ' + recordError.message);
}
}
gs.info('[' + scriptName + '] Completed. Processed=' + processed + ', Errors=' + errors);
} catch (e) {
gs.error('[' + scriptName + '] Fatal error: ' + e.message);
gs.error('[' + scriptName + '] Stack: ' + e.stack);
} finally {
var endTime = new GlideDateTime();
var duration = GlideDateTime.subtract(startTime, endTime).getNumericValue() / 1000;
gs.info('[' + scriptName + '] Duration: ' + duration + ' seconds');
}
description: Update incident priorities with full error handling
ステップ 4.2:トランザクションのロールバックパターン
複雑な更新をまとめて成功または失敗させる場合に使用します。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Transaction-like pattern with rollback capability
var changes = [];
var success = true;
try {
// Step 1: Update incident
var incident = new GlideRecord('incident');
incident.get('[incident_sys_id]');
var oldState = incident.state.toString();
incident.state = 6; // Resolved
incident.update();
changes.push({ table: 'incident', sys_id: incident.sys_id.toString(), field: 'state', old: oldState, new: '6' });
gs.info('Step 1 complete: Incident updated');
// Step 2: Update related task
var task = new GlideRecord('sc_task');
task.get('[task_sys_id]');
var oldTaskState = task.state.toString();
task.state = 3; // Closed Complete
task.update();
changes.push({ table: 'sc_task', sys_id: task.sys_id.toString(), field: 'state', old: oldTaskState, new: '3' });
gs.info('Step 2 complete: Task updated');
// Step 3: Something that might fail
// Simulating failure for demo
// throw new Error('Simulated failure');
gs.info('All steps completed successfully');
} catch (e) {
gs.error('Error occurred: ' + e.message);
gs.info('Rolling back ' + changes.length + ' changes...');
// Rollback in reverse order
for (var i = changes.length - 1; i >= 0; i--) {
var change = changes[i];
var rollback = new GlideRecord(change.table);
rollback.get(change.sys_id);
rollback[change.field] = change.old;
rollback.update();
gs.info('Rolled back: ' + change.table + '.' + change.sys_id);
}
gs.info('Rollback complete');
}
description: Multi-step update with rollback capability
フェーズ 5:ログ記録のベストプラクティス
ステップ 5.1:構造化ログ
一貫したログ形式を使用します。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Structured logging pattern
var Logger = {
scriptName: 'DataMigration',
info: function(message, data) {
var logMessage = '[' + this.scriptName + '] [INFO] ' + message;
if (data) logMessage += ' | Data: ' + JSON.stringify(data);
gs.info(logMessage);
},
warn: function(message, data) {
var logMessage = '[' + this.scriptName + '] [WARN] ' + message;
if (data) logMessage += ' | Data: ' + JSON.stringify(data);
gs.warn(logMessage);
},
error: function(message, error) {
var logMessage = '[' + this.scriptName + '] [ERROR] ' + message;
if (error) logMessage += ' | Error: ' + error.message;
gs.error(logMessage);
},
metric: function(name, value) {
gs.info('[' + this.scriptName + '] [METRIC] ' + name + '=' + value);
}
};
// Usage
Logger.info('Starting migration', { source: 'incident', target: 'x_custom_incident' });
Logger.metric('records_processed', 150);
Logger.warn('Skipped record due to missing field', { number: 'INC0012345' });
Logger.error('Failed to process record', new Error('Invalid reference'));
description: Demonstrate structured logging pattern
ステップ 5.2:スクリプトログをクエリする
スクリプト実行ログを検索する:
Tool: SN-Query-Table
Parameters:
table_name: syslog
query: source=Script execution^sys_created_on>javascript:gs.hoursAgo(1)
fields: message,level,sys_created_on
limit: 100
スクリプト名で絞り込む:
Tool: SN-Query-Table
Parameters:
table_name: syslog
query: messageLIKE[DataMigration]^sys_created_on>javascript:gs.hoursAgo(1)
fields: message,level,sys_created_on
limit: 100
フェーズ 6:高度なパターン
ステップ 6.1:進捗状況を記録するバッチ処理
長時間実行される操作に使用します。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Batch processing with progress tracking
var BATCH_SIZE = 100;
var MAX_BATCHES = 10;
var TABLE = 'incident';
var QUERY = 'active=true';
var totalProcessed = 0;
var totalRecords = 0;
var batchNum = 0;
// Get total count
var countGR = new GlideAggregate(TABLE);
countGR.addEncodedQuery(QUERY);
countGR.addAggregate('COUNT');
countGR.query();
if (countGR.next()) {
totalRecords = parseInt(countGR.getAggregate('COUNT'));
}
gs.info('Total records to process: ' + totalRecords);
// Process in batches
while (batchNum < MAX_BATCHES) {
var gr = new GlideRecord(TABLE);
gr.addEncodedQuery(QUERY);
gr.orderBy('sys_created_on');
gr.chooseWindow(batchNum * BATCH_SIZE, (batchNum + 1) * BATCH_SIZE);
gr.query();
if (!gr.hasNext()) {
gs.info('No more records to process');
break;
}
var batchProcessed = 0;
while (gr.next()) {
// Process record
batchProcessed++;
totalProcessed++;
}
var progress = Math.round((totalProcessed / totalRecords) * 100);
gs.info('Batch ' + (batchNum + 1) + ' complete: ' + batchProcessed + ' records | Progress: ' + progress + '%');
batchNum++;
}
gs.info('Processing complete. Total processed: ' + totalProcessed + '/' + totalRecords);
description: Batch processing with progress tracking
ステップ 6.2:非同期実行
非常に長時間実行されるスクリプトでは、複数の実行を連鎖させます。
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Self-scheduling pattern for long operations
var RECORDS_PER_EXECUTION = 1000;
var DELAY_SECONDS = 10;
// Get or create tracking property
var tracker = gs.getProperty('x_custom.migration.last_processed', '');
var gr = new GlideRecord('incident');
if (tracker) {
gr.addQuery('sys_created_on', '>', tracker);
}
gr.orderBy('sys_created_on');
gr.setLimit(RECORDS_PER_EXECUTION);
gr.query();
var lastProcessed = '';
var count = 0;
while (gr.next()) {
// Process record
count++;
lastProcessed = gr.sys_created_on.toString();
}
if (count > 0) {
// Update tracker
gs.setProperty('x_custom.migration.last_processed', lastProcessed);
gs.info('Processed ' + count + ' records. Last: ' + lastProcessed);
// Schedule next execution
if (count == RECORDS_PER_EXECUTION) {
var trigger = new GlideRecord('sys_trigger');
trigger.initialize();
trigger.name = 'Continue Migration ' + new GlideDateTime().getDisplayValue();
trigger.next_action = new GlideDateTime();
trigger.next_action.addSeconds(DELAY_SECONDS);
trigger.script = 'gs.include("MigrationScript");'; // Call script include
trigger.trigger_type = 0; // Run once
trigger.insert();
gs.info('Scheduled next execution in ' + DELAY_SECONDS + ' seconds');
} else {
gs.info('Migration complete!');
gs.setProperty('x_custom.migration.last_processed', ''); // Reset
}
} else {
gs.info('No more records to process');
}
description: Self-scheduling migration script
スクリプトテンプレート
テンプレート 1:データ監査
// Data Audit Template
var TABLE = 'incident';
var AUDIT_FIELDS = ['state', 'priority', 'assigned_to'];
var audit = {};
AUDIT_FIELDS.forEach(function(field) {
audit[field] = {};
});
var gr = new GlideRecord(TABLE);
gr.addQuery('active', true);
gr.query();
while (gr.next()) {
AUDIT_FIELDS.forEach(function(field) {
var value = gr.getDisplayValue(field) || '(empty)';
audit[field][value] = (audit[field][value] || 0) + 1;
});
}
gs.info('Audit Results:\n' + JSON.stringify(audit, null, 2));
テンプレート 2:参照データの検証
// Reference Validation Template
var TABLE = 'incident';
var REF_FIELD = 'assigned_to';
var REF_TABLE = 'sys_user';
var invalid = [];
var gr = new GlideRecord(TABLE);
gr.addQuery('active', true);
gr.addQuery(REF_FIELD + '.active', false); // Reference to inactive record
gr.query();
while (gr.next()) {
invalid.push({
number: gr.number.toString(),
invalid_ref: gr.getDisplayValue(REF_FIELD)
});
}
gs.info('Found ' + invalid.length + ' records with invalid references');
gs.info(JSON.stringify(invalid, null, 2));
テンプレート 3:フィールドの一括更新
// Bulk Field Update Template
var TABLE = 'incident';
var QUERY = 'active=true^category=inquiry';
var UPDATES = {
subcategory: 'general',
contact_type: 'email'
};
var DRY_RUN = true;
var gr = new GlideRecord(TABLE);
gr.addEncodedQuery(QUERY);
gr.query();
var updated = 0;
while (gr.next()) {
if (DRY_RUN) {
gs.info('[DRY RUN] Would update ' + gr.number);
} else {
for (var field in UPDATES) {
gr[field] = UPDATES[field];
}
gr.update();
updated++;
}
}
gs.info((DRY_RUN ? '[DRY RUN] ' : '') + 'Updated ' + updated + ' records');
ツールの使用方法の概要
| 操作 | MCP ツール | 目的 |
|---|---|---|
| 実行 | SN-Execute-Background-Script | サーバーサイド JavaScript を実行します |
| 修正スクリプト | SN-Create-Fix-Script | 監査可能なスクリプトを作成します |
| ログのクエリ | SN-Query-Table | 実行結果を確認します |
| レコード作成 | SN-Create-Record | 修正スクリプトを手動で作成します |
ベストプラクティス
- まずは必ずドライランを実行する: 実際に実行する前に、DRY_RUN = true でテストします
- 上限を設定する: スクリプトの暴走を防ぐため、必ず setLimit() を使用します
- 詳細にログを記録する: デバッグには構造化ログを使用します
- エラーを処理する: すべてのスクリプトを try-catch で囲みます
- スクリプトを文書化する: コメントに作成者、日付、チケットの参照情報を記載します
- ハードコーディングを避ける: sys_ids とクエリには変数を使用します
- 本番環境以外でテストする: テストしていないスクリプトを本番環境で実行しないでください
- トランザクションを使用する: 複数ステップの操作ではロールバックパターンを検討します
トラブルシューティング
スクリプトが実行されない
症状: システムログに出力がない 原因:
- sys_trigger の権限
- ログが記録される前にスクリプトの構文エラーが発生している 解決策:
Tool: SN-Query-Table
Parameters:
table_name: sys_trigger
query: nameLIKEMCP^sys_created_on>javascript:gs.minutesAgo(5)
fields: name,state,next_action,script
スクリプトがタイムアウトする
症状: 実行が途中で止まり、タイムアウトエラーが発生する 原因:
- 処理するレコードが多すぎる
- クエリの効率が悪い 解決策:
- setLimit() を追加します
- バッチ処理パターンを使用します
- 適切なインデックスを使ってクエリを最適化します
ログが表示されない
症状: スクリプトは実行されるが、gs.info() の出力が表示されない 原因:
- ログレベルによるフィルタリング
- 誤った時間範囲を確認している 解決策:
Tool: SN-Query-Table
Parameters:
table_name: syslog
query: sys_created_on>javascript:gs.minutesAgo(10)
fields: message,level,source,sys_created_on
limit: 50
関連スキル
admin/batch-operations- レコードの一括操作admin/update-set-management- スクリプトの変更を追跡admin/deployment-workflow- インスタンス間でスクリプトをデプロイ