ServiceNow ITSM Scenario-Based Interview Questions 2025
Real-world scenarios every ServiceNow developer gets asked in interviews — with clear answers, working code, and pro tips. No fluff. Just what actually gets asked.
Q1. A major incident is raised but no one is acknowledging it. The manager wants an auto-assign mechanism. How do you handle this in ServiceNow?
Incident Management | Intermediate
Use Assignment Rules as the first approach. Go to System Policy → Rules → Assignment, target the Incident table, set your conditions (e.g. Category = Network), and assign the correct group. As a fallback, use a Business Rule:
// Business Rule: Auto-assign if assignment group is empty
// Table: Incident | When: Before Insert/Update
(function executeRule(current, previous) {
if (!current.assignment_group || current.assignment_group.nil()) {
var gr = new GlideRecord('sys_user_group');
gr.addQuery('name', 'IT Support');
gr.query();
if (gr.next()) {
current.assignment_group = gr.sys_id;
}
}
})(current, previous);
💡 Pro Tip
Always use Assignment Rules first. Business Rules are a fallback. This keeps logic clean and maintainable.
Q2. A VIP user raises an incident. The business wants it automatically set to Priority 1 and the manager gets an email. How do you build this?
Incident Management | Advanced
Tag users as VIP using a custom field u_is_vip on the User table. Then write a Business Rule on the Incident table:
// Business Rule: VIP Auto Priority
// Table: Incident | When: Before Insert
(function executeRule(current, previous) {
var caller = new GlideRecord('sys_user');
if (caller.get(current.caller_id)) {
if (caller.u_is_vip == true) {
current.priority = '1';
current.urgency = '1';
current.impact = '1';
current.work_notes = 'Auto-escalated: VIP caller detected.';
}
}
})(current, previous);
💡 Pro Tip
Create a Notification: Table = Incident, When = Record inserted, Condition = Priority is 1. Send to the caller's manager using the 'Manager of Caller' field in the email template.
Q3. The business says: "No change should go to production on a Friday after 3 PM." How do you enforce this in ServiceNow?
Change Management | Intermediate
Use a Business Rule on the Change Request table that checks the day and time before allowing the state to move to Implement:
// Business Rule: Block Friday Change
// Table: Change Request | When: Before Update
(function executeRule(current, previous) {
if (current.state == '3' && previous.state != '3') {
var now = new GlideDateTime();
var day = now.getDayOfWeekLocalTime(); // 1=Sun, 6=Fri, 7=Sat
var hour = parseInt(now.getLocalTime().getByFormat('HH'));
if (day == 6 && hour >= 15) {
current.setAbortAction(true);
gs.addErrorMessage(
'Change cannot move to Implement on Friday after 3 PM. Please reschedule.'
);
}
}
})(current, previous);
💡 Pro Tip
You can also handle this via Change Approval Policies or Flow Designer. The Business Rule approach is fastest to implement and easiest to explain in interviews.
Q4. Three incidents with the same error code come in within one hour. How do you auto-create a Problem record and link all incidents?
Problem Management | Intermediate
Use a Business Rule (After Insert on Incident) that counts matching incidents in the last hour and auto-creates a Problem when the threshold is hit:
// Business Rule: Auto-create Problem
// Table: Incident | When: After Insert
function autoCreateProblem() {
var errorCode = current.u_error_code;
if (!errorCode || errorCode == '') return;
var incCount = new GlideAggregate('incident');
incCount.addQuery('u_error_code', errorCode);
incCount.addQuery('state', 'IN', '1,2');
var oneHourAgo = new GlideDateTime();
oneHourAgo.subtract(3600000);
incCount.addQuery('sys_created_on', '>=', oneHourAgo);
incCount.addAggregate('COUNT');
incCount.query();
if (incCount.next() && incCount.getAggregate('COUNT') >= 3) {
var prob = new GlideRecord('problem');
prob.addQuery('u_error_code', errorCode);
prob.addQuery('state', 'NOT IN', '4');
prob.query();
if (!prob.next()) {
var newProb = new GlideRecord('problem');
newProb.initialize();
newProb.short_description = 'Auto-created: Recurring error - ' + errorCode;
newProb.u_error_code = errorCode;
newProb.assignment_group = current.assignment_group;
var probId = newProb.insert();
current.problem_id = probId;
} else {
current.problem_id = prob.sys_id;
}
}
}
autoCreateProblem();
💡 Pro Tip
This pattern is called "Event Correlation". In enterprise setups, you can also use ServiceNow's built-in Major Incident Management feature for similar outcomes.
Q5. A user orders a laptop from the Service Catalog. The order must go to the manager for approval, then to IT Procurement. How do you set this up?
Service Catalog | Intermediate
Use Flow Designer for this multi-stage approval workflow:
- Trigger: Service Catalog — when item is ordered
- Ask For Approval → Approver = Requester's Manager
- If approved → Ask For Approval → Group = IT Procurement
- If approved → Create Task assigned to Procurement team
- If rejected → Notify requester with rejection reason
Supporting Catalog Client Script to display the manager name on the form:
// Catalog Client Script — Type: onLoad
function onLoad() {
var userId = g_user.userID;
var ga = new GlideAjax('UserInfoAjax');
ga.addParam('sysparm_name', 'getManagerName');
ga.addParam('sysparm_user_id', userId);
ga.getXMLAnswer(function(answer) {
if (answer) {
g_form.setValue('u_manager_info', answer);
g_form.setReadOnly('u_manager_info', true);
}
});
}
// Script Include: UserInfoAjax
var UserInfoAjax = Class.create();
UserInfoAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getManagerName: function() {
var userId = this.getParameter('sysparm_user_id');
var user = new GlideRecord('sys_user');
if (user.get(userId)) {
var manager = new GlideRecord('sys_user');
if (manager.get(user.manager)) {
return manager.getDisplayValue('name');
}
}
return 'No manager assigned';
},
type: 'UserInfoAjax'
});
💡 Pro Tip
Always use Flow Designer for new catalog workflows. Workflow Editor is legacy and being phased out. Flow Designer gives better visibility and easier debugging.
Q6. An SLA is not pausing when an incident moves to 'Awaiting User Info' state. How do you troubleshoot and fix this?
SLA Management | Advanced
This is one of the most common SLA issues in production. The problem is almost always in the SLA Definition's Pause Conditions.
Troubleshooting Steps:
- Open the Incident → click the SLA tab → check if stage shows 'In Progress' instead of 'Paused'
- Go to Process Automation → SLA → SLA Definitions → find the relevant SLA
- Check the Pause Condition — most common mistake is using the display label instead of the state integer value
// SLA Pause Condition (Script field inside SLA Definition form)
// 'task' refers to the incident record
(function slaCondition(task) {
// Use the INTEGER value, NOT the display label
return (task.state == '10'); // 10 = Awaiting User Info
})(task);
// Verify exact state integer values using Scripts - Background:
var inc = new GlideRecord('incident');
inc.addQuery('state', 10);
inc.setLimit(1);
inc.query();
if (inc.next()) {
gs.info('State 10 = ' + inc.getDisplayValue('state'));
}
💡 Pro Tip
90% of SLA pause issues happen because developers use the display label instead of the actual integer state value. Always verify numeric values using Scripts - Background before writing the SLA condition.
Q7. An incident is raised but the CI linked to it shows the wrong owner. How do you ensure CI data is validated automatically on incident creation?
CMDB & Asset Management | Advanced
Use a Business Rule that cross-validates CI operational status and auto-populates the assignment group from the CI's support group:
// Business Rule: Validate CI on Incident creation
// Table: Incident | When: Before Insert/Update | Condition: CI is not empty
(function executeRule(current, previous) {
if (current.cmdb_ci && !current.cmdb_ci.nil()) {
var ci = new GlideRecord('cmdb_ci');
if (ci.get(current.cmdb_ci)) {
// Warn if CI is not Operational
if (ci.operational_status != '1') {
current.work_notes =
'[CMDB Alert] Linked CI is not Operational. ' +
'Status: ' + ci.getDisplayValue('operational_status') +
'. Please verify before proceeding.';
}
// Auto-set assignment group from CI support group
if ((!current.assignment_group || current.assignment_group.nil())
&& ci.support_group) {
current.assignment_group = ci.support_group;
current.work_notes += '\nAssignment group auto-set from CI support group.';
}
}
}
})(current, previous);
💡 Pro Tip
Pair this Business Rule with ServiceNow Discovery or ITOM to auto-update CI attributes. Schedule a weekly CMDB Health Dashboard review to catch data quality issues early.
Q8. The helpdesk wants an email sent to the caller every time a work note is added to their incident. How do you configure this?
Notifications & Events | Beginner
This is pure configuration — no code needed.
Steps:
- Go to System Notification → Email → Notifications → New
- Table: Incident
- When to send: Record Updated
- Condition: Work notes | changes
- Who will receive: Caller (select the Caller field as recipient)
Email template using built-in variables:
Hello ${caller_id.first_name},
Your incident ${number} has been updated.
Update Details:
${work_notes}
Current Status : ${state}
Priority : ${priority}
To add more information, visit the portal or reply to this email.
Thank you,
IT Support Team
💡 Pro Tip
Uncheck 'Send to event creator' so agents don't receive emails for their own updates. Consider a notification digest to prevent email flooding on busy incidents.
Q9. The IT manager wants a dashboard showing open incidents by priority, SLA compliance rate this month, and top 5 categories. How do you build this?
Reporting & Dashboards | Intermediate
Build three separate reports in Report Designer, then pin them to a shared Dashboard.
- Widget 1: Bar Chart | Table: Incident | Filter: State != Resolved/Closed | Group by: Priority
- Widget 2: Donut | Table: Task SLA | Filter: Planned End = This Month | Has Breached = false
- Widget 3: Top 5 Categories — via Report Designer or the script below
// Scheduled Script: Top 5 Incident Categories this month
var ga = new GlideAggregate('incident');
ga.addQuery('opened_at', '>=', gs.beginningOfThisMonth());
ga.groupBy('category');
ga.addAggregate('COUNT');
ga.orderByAggregate('COUNT');
ga.setMaxTableCount(5);
ga.query();
var results = [];
while (ga.next()) {
results.push({
category : ga.getDisplayValue('category'),
count : ga.getAggregate('COUNT')
});
}
gs.info(JSON.stringify(results));
// Output: [{"category":"Network","count":"45"}, ...]
💡 Pro Tip
For real-time dashboards, use Performance Analytics (PA) instead of standard reports. PA supports trend lines, time-series charts, and auto-refresh. Standard reports are point-in-time snapshots only.
Q10. An incident sits in 'In Progress' for more than 4 hours without any update. The team lead should get an alert. How do you automate this?
Flow Designer & Automation | Advanced
Flow Designer approach: Trigger on Record Updated → Wait 4 hours → Check if still In Progress → Send notification to manager.
Alternative — Scheduled Script (runs every 30 minutes):
// Scheduled Script: Alert on stale incidents
(function checkStaleIncidents() {
var staleTime = new GlideDateTime();
staleTime.subtract(14400000); // 4 hours in milliseconds
var inc = new GlideRecord('incident');
inc.addQuery('state', '2'); // In Progress
inc.addQuery('sys_updated_on', '<=', staleTime);
inc.addQuery('u_stale_notified', '!=', 'true'); // Prevent duplicate alerts
inc.query();
while (inc.next()) {
var group = new GlideRecord('sys_user_group');
if (group.get(inc.assignment_group)) {
gs.eventQueue(
'incident.stale.alert',
inc,
group.manager,
inc.number
);
inc.u_stale_notified = 'true';
inc.work_notes = 'Auto-alert sent: Incident stale 4+ hours.';
inc.update();
}
}
})();
💡 Pro Tip
Register the custom event 'incident.stale.alert' in System Policy → Events → Registry, then build a Notification that listens to it. Much cleaner than hardcoding email logic inside the script.
Q11. When an agent selects 'Hardware' as the category on an incident, only hardware-related subcategories should appear. How do you implement this?
Client Scripts & UI | Beginner
Use an onChange Client Script on the Category field to clear and re-populate Subcategory options dynamically:
// Client Script: Filter Subcategory based on Category
// Type: onChange | Table: Incident | Field: Category
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || newValue === '') return;
g_form.setValue('subcategory', '');
g_form.clearOptions('subcategory');
var subcategoryMap = {
'hardware' : ['Laptop', 'Desktop', 'Printer', 'Monitor', 'Keyboard'],
'software' : ['Installation', 'License', 'Update', 'Crash'],
'network' : ['VPN', 'WiFi', 'Ethernet', 'Firewall'],
'access' : ['Account Locked', 'Password Reset', 'New Access', 'Remove Access']
};
var options = subcategoryMap[newValue.toLowerCase()] || [];
g_form.addOption('subcategory', '', '--Select Subcategory--');
options.forEach(function(opt) {
g_form.addOption('subcategory', opt.toLowerCase().replace(/ /g, '_'), opt);
});
}
💡 Pro Tip
If subcategory is a Reference field (not a choice list), use GlideAjax + a Script Include. A Client Script alone cannot filter reference fields — use a Dynamic Reference Qualifier on the field definition instead.
Q12. Only members of the 'HR Team' group should see incidents where category is 'HR Confidential'. How do you restrict this using ACLs?
Security & Access Control | Advanced
Create two ACLs — one for the record view and one for the list view. Operation: Read | Table: Incident | Condition: Category = HR Confidential. Add this script in the ACL Script field:
// ACL Script: HR Confidential Incident Access
// Applied to: Incident (record) | Operation: Read
(function checkAccess() {
// Allow HR Team members
if (gs.getUser().isMemberOf('HR Team')) {
return true;
}
// Allow admins
if (gs.hasRole('admin')) {
return true;
}
// Allow the caller to see their own incident
if (current.caller_id == gs.getUserID()) {
return true;
}
// Deny everyone else
return false;
})();
// IMPORTANT: Create a second identical ACL for the LIST view:
// Table: Incident [list] | Operation: Read | Same script
// Without this, HR Confidential incidents still appear in list views!
💡 Pro Tip
Always create ACLs in pairs — one for the record and one for the list view. Missing the list ACL is the #1 security gap developers miss. Test with a non-admin user using the Impersonate User feature in ServiceNow.
Key Takeaways
- ✔ Use Assignment Rules before Business Rules for auto-assignment
- ✔ Business Rules handle server-side logic — before/after insert/update
- ✔ Flow Designer is the modern replacement for Workflow Editor
- ✔ SLA pause conditions always use integer state values, not display labels
- ✔ Always create ACLs in pairs — record view + list view
- ✔ Client Scripts handle client-side UI; Business Rules handle server-side data
- ✔ Use GlideAggregate (not GlideRecord) when you only need counts
