Scheduled Jobs and Background Scripts
Introduction
Not every automation in ServiceNow should fire in response to a record change. Some operations are inherently time-based: nightly data cleanup, weekly SLA summary emails, daily synchronization with external systems. Scheduled Jobs handle these recurring tasks. Background Scripts handle one-off administrative operations that you need to run directly against the server.
Scheduled Jobs
Scheduled Jobs (sysauto_script) run server-side JavaScript on a defined schedule.
System Definition > Scheduled Jobs > New
Key Fields
| Field | Description |
|---|---|
| Name | Descriptive job name |
| Run | Frequency (Daily, Weekly, Periodically, On Demand, Once) |
| Time | When to run (for daily/weekly) |
| Run as | User context for the job |
| Script | Server-side JavaScript to execute |
| Active | Enable/disable the job |
Cron-Style Scheduling (Periodically)
For custom frequencies, use the "Periodically" option with a repeat interval, or use a CRON expression:
Every 15 minutes: */15 * * * *
Every day at 2am: 0 2 * * *
Every Monday 6am: 0 6 * * 1
First of month: 0 9 1 * *
Writing Scheduled Job Scripts
Example: Close Resolved Incidents After 7 Days
var gr = new GlideRecord('incident');
gr.addQuery('state', '6'); // Resolved
gr.addQuery('resolved_at', '<', gs.daysAgoStart(7));
gr.query();
var count = 0;
while (gr.next()) {
gr.setValue('state', '7'); // Closed
gr.setValue('close_notes',
'Auto-closed after 7 days in Resolved state.');
gr.update();
count++;
}
gs.info('Auto-close job: closed ' + count + ' incidents.');
Example: Daily SLA Breach Report Email
var ga = new GlideAggregate('task_sla');
ga.addQuery('has_breached', true);
ga.addQuery('stage', 'In Progress');
ga.addAggregate('COUNT');
ga.query();
var breachCount = 0;
if (ga.next()) {
breachCount = parseInt(ga.getAggregate('COUNT'));
}
if (breachCount > 0) {
gs.eventQueue(
'sla.daily.breach.summary',
null,
breachCount.toString(),
gs.now()
);
}
Monitoring Scheduled Jobs
Job Execution History
System Definition > Scheduled Jobs → select job → Scheduled Job Executions (related list)
Each execution record shows:
- Start and end time
- Duration
- Status (Success, Error)
- Output messages from
gs.info()andgs.error()
System Logs
System Logs > System Log > Application
Filter by source = your job name to see gs.info() / gs.error() output.
Run As User
Always set a dedicated service account as the Run As user — never use a named person's account:
- Named user accounts can be disabled, locking the job
- Service accounts have controlled, minimal permissions
- Audit trails are cleaner when a service account performs automated actions
Create a user like svc_scheduled_jobs with only the roles required for the specific job's operations.
Background Scripts
Background Scripts (sys_script_plain) execute JavaScript immediately in the current session — no schedule, one-time execution.
System Definition > Background Scripts
Paste your script and click Run script. Output appears directly in the browser.
Use Cases
- Data fixes: correct a batch of mis-populated field values
- One-time migrations: move records to a new structure
- Testing: verify GlideRecord queries before building Business Rules
- Diagnostics: inspect system state, check property values
Example: Fix Assignment Group on Old Records
var gr = new GlideRecord('incident');
gr.addQuery('assignment_group.name', 'Old Group Name');
gr.addQuery('state', 'IN', '1,2,3'); // Open states
gr.query();
var newGroup = new GlideRecord('sys_user_group');
newGroup.get('name', 'New Group Name');
var newGroupId = newGroup.getUniqueValue();
var count = 0;
while (gr.next()) {
gr.setValue('assignment_group', newGroupId);
gr.update();
count++;
}
gs.info('Updated ' + count + ' records.');
Safety Rules for Background Scripts
- Always query first — comment out the
update()ordeleteRecord()call, run the query, log counts, then uncomment and execute - Never use
deleteMultiple()without double-checking the query result set - Test in sub-prod first — background scripts against production data are irreversible
- Document what you ran — add a comment to the script and save it before executing
Best Practices
- Use a dedicated service account for all scheduled jobs
- Log start and completion with
gs.info()— include record counts - Add error handling with
try/catchin scheduled job scripts - Keep scheduled jobs focused — one job per task
- Monitor execution history after deploying new jobs
- Use
setLimit()on queries in scheduled jobs to prevent runaway operations - Test background scripts against a non-production instance before production
Conclusion
Scheduled Jobs and Background Scripts are the administrative backbone of a well-run ServiceNow instance. Used correctly — with proper service accounts, logging, error handling, and sub-prod testing — they automate the boring parts of platform administration and free your team to focus on higher-value work.