Blast Radius for ServiceNow
Blast Radius provides configuration dependency analysis across a ServiceNow instance. Before making changes to fields, tables, or artifacts, use these tools to understand the full blast radius.
Tool Overview
| Tool | Purpose | When to Use |
|------|---------|-------------|
| snow_blast_radius_apps | List apps with config counts | Starting point — understand the instance landscape |
| snow_blast_radius_table_configs | All configs on a table | "What runs on the incident table?" |
| snow_blast_radius_field_references | Reverse field lookup | "What touches incident.assignment_group?" |
| snow_blast_radius_artifact_dependencies | Forward dependency analysis | "What does this business rule read/write?" |
| snow_blast_radius_dependents | Find what uses / calls an artifact (deep 3-phase scan, 100+ tables) | "What calls IncidentUtils?" / "Can I safely delete this business rule?" |
| snow_code_search | Substring search across every script-bearing table (business rules, script includes, client scripts, widgets, scripted REST ops, UI actions, ACLs, notifications, UX scripts, …) | "Where is IncidentUtils.resolve referenced anywhere?" — use when reverse-dependency tools don't index what you need |
Coverage
snow_blast_radius_field_references and snow_blast_radius_table_configs scan the following artifact types in parallel. Types backed by plugins that are not activated on the instance fail silently — the tool reports them under errors_by_type and continues.
Table-scoped (queried with the table name as filter):
- Business rules (
sys_script) — script + condition + filter_condition + role_conditions - Client scripts (
sys_script_client) - UI actions (
sys_ui_action) — script + condition - UI policies (
sys_ui_policy) — script_true + script_false + conditions - UI policy actions (
sys_ui_policy_action) — exact field match - Data policies (
sys_data_policy2) - Data policy rules (
sys_data_policy_rule) — exact table_field match - ACLs (
sys_security_acl) — field-level name match always included, table-level only when script/condition references the field - Email notifications (
sysevent_email_action) — subject + message_html + message_text + condition + advanced_condition - Metric definitions (
metric_definition) — script + exact field match - Inbound email actions (
sysevent_in_email_action) — script + condition - Transform entries (
sys_transform_entry) — target_field / source_field - Dictionary
dependent/dependent_on_field— UI-level field dependencies
Global (no table column — grep all records, filter client-side by script content mentioning the table name):
- Script includes (
sys_script_include) - Scheduled jobs (
sysauto_script) - Fix scripts (
sys_script_fix) - Script actions (
sysevent_script_action) - Transform scripts (
sys_transform_script) - Processors (
sys_processor) - UI scripts (
sys_ui_script) - Scripted REST resources (
sys_ws_operation) — operation_script - Email scripts (
sys_script_email) - Service Portal widgets (
sp_widget) — server script + client script + link - Catalog client scripts (
catalog_script_client) - Catalog UI policies (
catalog_ui_policy) - ATF step inputs (
sys_atf_step)
Table hierarchy: By default the tools walk sys_db_object.super_class up to three levels, so a query on incident also returns artifacts scoped to task. Disable via include_parent_tables: false.
When results are truncated
snow_blast_radius_field_references returns a truncated_types array naming artifact types where the sysparm_limit was hit. Global-scope types (script_includes, scheduled_jobs, widgets, ATF steps) are the usual suspects — they have no table column, so the pre-filter matches any script mentioning the field name anywhere.
What to do when truncated_types is non-empty:
- Re-run
snow_blast_radius_field_referenceswith a higherlimit_per_type(e.g. 100) — often enough for medium-size instances. - If still truncated, switch to
snow_code_searchfor the affected tables with a tighter pattern. Example forincident.priorityreferences in script includes:
Repeat withsnow_code_search({ query: "current.priority", tables: ["sys_script_include"], per_table_limit: 100, })"'priority'"and'"priority"'to catchsetValue('priority')/"priority"patterns. Results are unclassified (no read/write split) but complete. - Intersect the
snow_code_searchhits with the script-analyzer output by runningsnow_blast_radius_artifact_dependencieson each individual script include for accurate read/write classification.
Treat truncated_types as a signal that blast-radius's structured answer is incomplete, not as a safe zero.
Known Limitations
These gaps are inherent to static regex-based analysis over the REST Table API:
- Reference dotwalks:
current.assignment_group.manageris parsed as a reference toassignment_group, not tosys_user_group.manager. Dotwalked field changes are not flagged transitively. - Dynamic field access:
gr.setValue(someVar, value)cannot be resolved becausesomeVaris computed at runtime. - Dynamic table access:
new GlideRecord(tableVar)— global artifacts won't be matched unless the table name appears literally in the script. - Legacy workflows (
wf_workflow,wf_activity) are not scanned — migrating to Flow Designer is recommended anyway. - Reports/dashboards filter conditions are not scanned.
- Service Portal option schemas / angular providers beyond
sp_widgetare not scanned. - Field-level ACLs without scripts that also don't match the
name=<table>.<field>exact pattern (e.g., using a regex-like ACL naming) may be missed. include_parent_tablesadds at most threesys_db_objectlookups; deeper hierarchies (incident_task → task → incident) are capped at depth 3.
If the tool reports "0 references", treat it as "no static references found in the artifact types we scan" — not as a proof of safety. For high-risk changes (renaming a widely-used field), combine with grep of the export XML, and consider running a sandboxed test after the change.
Recommended Workflow
1. Instance Overview
Start with snow_blast_radius_apps to see which applications have the most configurations:
→ snow_blast_radius_apps(include_config_counts: true)
2. Table Deep-Dive
Pick a table and see everything running on it:
→ snow_blast_radius_table_configs(table_name: "incident")
3. Field Impact Check
Before changing a field, find every artifact that touches it:
→ snow_blast_radius_field_references(table_name: "incident", field_name: "assignment_group")
4. Artifact Analysis
Analyze what a specific artifact depends on:
→ snow_blast_radius_artifact_dependencies(artifact_type: "business_rule", artifact_sys_id: "abc123")
Returns fields read/written, tables queried, script includes called, and a complexity rating.
5. Reverse Dependencies
Find what depends on a specific artifact (e.g., a script include):
→ snow_blast_radius_dependents(artifact_type: "script_include", artifact_identifier: "IncidentUtils")
Common Questions and Which Tool to Use
| Question | Tool |
|----------|------|
| "What apps are in this instance?" | snow_blast_radius_apps |
| "What business rules run on incident?" | snow_blast_radius_table_configs |
| "What touches the state field on change_request?" | snow_blast_radius_field_references |
| "What does this business rule do?" | snow_blast_radius_artifact_dependencies |
| "What uses the TaskUtils script include?" | snow_blast_radius_dependents |
| "Is it safe to remove this field?" | snow_blast_radius_field_references |
| "How complex is this business rule?" | snow_blast_radius_artifact_dependencies |
| "What's the blast radius of changing this script include?" | snow_blast_radius_dependents |
Script Analysis Patterns
The script analyzer detects these patterns in ServiceNow scripts:
Field reads: current.field, gr.getValue('field'), g_form.getValue('field'), .addQuery('field'), .orderBy('field')
Field writes: current.field = value, gr.setValue('field', value), g_form.setValue('field', value)
Table queries: new GlideRecord('table'), new GlideAggregate('table')
Script include calls: new ClassName() (excludes Glide built-in classes)
What blast-radius does NOT cover — and how to communicate it
Every snow_blast_radius_dependents result includes two fields the user must understand before acting on the data:
scope_caveat— a single sentence stating that the scan covers script-bearing fields, not the entire instance.out_of_scope_surfaces— a list of areas that were NOT searched. Common items: row data inside records, saved filters (sys_filter) and reports (sys_report), pending update sets (sys_update_xml), external consumers (MID server, Integration Hub, REST callers), plain-string properties (sys_property), inactive records, and inactive plugins.
You MUST relay these to the user whenever:
- The result has zero dependents AND the user is considering deletion — the absence of dependents in script-bearing fields is NOT the same as "safe to delete". Cite
out_of_scope_surfacesand recommend manual verification of those surfaces. - The user explicitly asks "is it safe to delete X" or "can I remove X" — always include the caveat. Even with 50 dependents, the picture may be incomplete.
- The user is preparing a refactor that renames or removes an artifact. Mention which out-of-scope surfaces are most likely to break and how to check them (e.g. for a field rename, also run an encoded-query check against
sys_filter).
Do not bury the caveat. A one-liner ("Note: this scan does not cover ...") is enough — but it must be visible to the user, not just to you.
Companion tools — RUN ALL OF THESE for delete / rename decisions
When the user asks any of:
- "is it safe to delete X" / "kan ik X verwijderen"
- "what breaks if I rename X" / "wat breekt als ik X hernoem"
- "can I refactor / remove / drop X"
- "blast radius of changing X"
…do NOT stop at snow_blast_radius_dependents. The dependents scan only covers script-bearing fields; three other surfaces silently hold references that gs.getProperty(), saved filters, and pending update sets pull in at runtime. Missing one of them is the difference between a clean refactor and a Monday-morning production incident.
You MUST run the full set in parallel (single response, multiple tool calls). Do not serialize. Do not skip a tool because "the user didn't ask for it" — "is this safe" already implies all of them.
Script include / business rule / widget / scheduled job / processor
snow_blast_radius_dependents(artifact_type, artifact_identifier)
snow_blast_radius_update_sets(artifact_identifier)
snow_blast_radius_sys_properties(artifact_identifier)
Field deletion or rename
snow_blast_radius_field_references(table_name, field_name, include_filters: true)
snow_blast_radius_update_sets(field_name)
snow_blast_radius_sys_properties(field_name)
Table deletion
snow_blast_radius_dependents(artifact_type: "table", artifact_identifier: <table>)
snow_blast_radius_update_sets(<table>)
snow_blast_radius_sys_properties(<table>)
…and if the user is also dropping or renaming columns, add one snow_blast_radius_field_references per column.
Synthesizing the answer
After the tools return, present ONE unified picture to the user. Lead with the totals across surfaces ("33 references across scripts, 2 in update sets, 4 in system properties"), call out cross-scope hits, and end with the out-of-scope surfaces that even the full sweep does NOT cover (row data, external consumers — see out_of_scope_surfaces on the dependents result). The user shouldn't have to mentally aggregate four tool outputs themselves.
If any of the companion tools returns hits in in_progress update sets, in plugin-category sys_properties, or with cross-scope dependents — treat that as a hard "do not delete without addressing these first" and say so in plain language, not buried in a bullet list.