If discovery tools are used within ServiceNow it can happen that based on the information provided it is not possible to determine the specific CI Class in ServiceNow. Therefore it could be useful to change the class after creation. This is possible as described in the following article by a colleague of mine: Change the Class of a CI. This article enables the option for the end user (without admin access) to change the ServiceNow CI class.
In this article a custom class Middleware CIs is used to explain the functionality.
The class structure looks like this:
Base Class | Extended Class |
Middleware Cis (u_cmdb_ci_middleware) | (none) |
Application Servers (u_cmdb_ci_application_server) | u_cmdb_ci_middleware |
Database Instances (u_cmdb_ci_database_instance) | u_cmdb_ci_middleware |
Interfaces (u_cmdb_ci_interface) | u_cmdb_ci_middleware |
MFX (u_cmdb_ci_mfx) | u_cmdb_ci_middleware |
MQ Cis (u_cmdb_ci_mq) | u_cmdb_ci_middleware |
Below an example of a Middleware record in ServiceNow which is created by the interface as a ‘Discovered’ CI.

On the form in the “Related Links” section a link is available called ‘Change class’ to change the class of a CI. When clicking on this link the following Dialog Window is visible.

The Dialog Window is initiated by using a UI Action and created with the following details below:
Field | Value |
Name | Change class |
Table | Any CI table on which you want to enable this functionality (in this example it is the u_cmdb_ci_middleware table) |
Onclick | changeClass(); |
Condition | new TableUtils(”+current.getTableName()).hasExtensions() && !current.isNewRecord() && current.operational_status == 0 && current.canWrite() |
Script | function changeClass() { var gdw = new GlideDialogWindow(‘cmdb_change_class’); gdw.setTitle(‘Change CI class’); gdw.setPreference(‘sysparm_sys_id’, g_form.getUniqueValue()); gdw.setPreference(‘sysparm_sys_class_name’, g_form.getTableName()); gdw.render(); } |
The condition can be changed as needed, in this example it is shown when the class of the CI has extended tables, if it is a existing record, the status of the CI is “Discovered” and the user can update (write) the record.
One of the options on how to make the UI Action visible can be chosen below (in this article the it is shown as a link):
One of the options on how to make the UI Action visible can be chosen below (in this article the it is shown as a link):
The Dialog window is a UI Page (cmdb_change_class), is created with the following details:
Field | Value |
Name | cmdb_change_class |
Category | General |
HTML | <?xml version=”1.0″ encoding=”utf-8″ ?> <j:jelly trim=”false” xmlns:j=”jelly:core” xmlns:g=”glide” xmlns:j2=”null” xmlns:g2=”null”> <g:ui_form> <input type=”hidden” name=”sysparm_sys_id” id=”sysparm_sys_id” value=”${RP.getParameterValue(‘sysparm_sys_id’)}”/> <input type=”hidden” name=”sysparm_sys_class_name” id=”sysparm_sys_class_name” value=”${RP.getParameterValue(‘sysparm_sys_class_name’)}”/> <g2:evaluate> var ciclass = []; var tbu = new TableUtils(‘${RP.getParameterValue(“sysparm_sys_class_name”)}’).getTableExtensions(); <!– tag “<” equals the “<” sign in html –> for(var i=0;i<tbu.size();i++) { ciclass.push(tbu.get(i)); } ciclass = ciclass.join(‘,’); ciclass; </g2:evaluate><g2:tokenize var=”jvar_ci_classes” delim=”,”> $[ciclass] </g2:tokenize>
<label for=”available_select”>${gs.getMessage(‘Change reason’)}</label><br/>
<g:ui_reference name=”u_change_reason” id=”u_change_reason” table=”task” query=”${new CIValidationUtils(current).refQualChangeReason()}”/><br/><br/>
<label for=”available_ci_classes”>${gs.getMessage(‘Please select a ci class to change to.’)}</label><br/>
<select name=”ci_class_chooser” id=”ci_class_chooser”> <j2:forEach var=”jvar_ci_class” items=”$[jvar_ci_classes]”> <option value=”$[jvar_ci_class]”>$[jvar_ci_class]</option> </j2:forEach> </select><br/> <g:dialog_buttons_ok_cancel ok=”return validateMandatoryfields()”/> </g:ui_form> </j:jelly> |
Client script | function validateMandatoryfields() { if(gel(‘u_change_reason’).value == ”) { alert(‘Please provide a change reason.’); return false; } else if(gel(‘ci_class_chooser’).value == ”) { alert(‘Please provide a ci class to convert’); } } |
Processing script | moveCIClass();function moveCIClass() { var grci = new GlideRecord(‘cmdb_ci’); if(grci.get(sysparm_sys_id)) { source = {}; source.x_need_change_reason = false;
grci.sys_class_name = ci_class_chooser;
grci.u_change_reason = u_change_reason; grci.update();
var graudit = new GlideRecord(‘sys_audit’);
graudit.initialize(); graudit.documentkey = sysparm_sys_id; graudit.fieldname = ‘sys_class_name’; graudit.tablename = sysparm_sys_class_name; var grchg = new GlideRecord(‘task’); if(grchg.get(u_change_reason)) { graudit.reason = grchg.getDisplayValue(); } graudit.insert(); response.sendRedirect(grci.getLink()); } } |
I found a some helpful examples on the community website of ServiceNow (https://community.servicenow.com/thread/164789) which helped me to create the script below. It gets all the extended tables as a comma separated string and uses the g2:tokenize tag to split it is a array function does.
<g2:evaluate>
var ciclass = [];
var tbu = new TableUtils('${RP.getParameterValue("sysparm_sys_class_name")}').getTableExtensions();
<!-- tag "<" equals the "<" sign in html -->
for(var i=0;i<tbu.size();i++) {
ciclass.push(tbu.get(i));
}
ciclass = ciclass.join(',');
ciclass;
</g2:evaluate>
<g2:tokenize var="jvar_ci_classes" delim=",">
$[ciclass]
</g2:tokenize>
var ciclass = [];
var tbu = new TableUtils('${RP.getParameterValue("sysparm_sys_class_name")}').getTableExtensions();
<!-- tag "<" equals the "<" sign in html -->
for(var i=0;i<tbu.size();i++) {
ciclass.push(tbu.get(i));
}
ciclass = ciclass.join(',');
ciclass;
</g2:evaluate>
<g2:tokenize var="jvar_ci_classes" delim=",">
$[ciclass]
</g2:tokenize>
Then it was possible to loop through the delimited string using the forEach tab below.
<j2:forEach var="jvar_ci_class" items="$[jvar_ci_classes]">
<option value="$[jvar_ci_class]">$[jvar_ci_class]</option>
</j2:forEach>
<option value="$[jvar_ci_class]">$[jvar_ci_class]</option>
</j2:forEach>
In this article the extended tables were made available, however it is also possible to select parent tables or all the classes available within the class tree. The available options are listed below. (http://wiki.servicenow.com/index.php?title=TableUtils).
Function Purpose
getHierarchy() Returns all the classes within the tree
getTableExtensions() Retuns all the extended classes in the tree
getTables() Returns all the parent classes in the tree
getHierarchy() Returns all the classes within the tree
getTableExtensions() Retuns all the extended classes in the tree
getTables() Returns all the parent classes in the tree
After a function is chosed in the table above, the following changes have to be done in the scripts:
UI Action
Change the condition of the UI Action, at least remove the “new TableUtils(”+current.getTableName()).hasExtensions()” part.
UI Page
Update the part “getTableExtensions” below with one of the functions available above.
Change the condition of the UI Action, at least remove the “new TableUtils(”+current.getTableName()).hasExtensions()” part.
UI Page
Update the part “getTableExtensions” below with one of the functions available above.
<g2:evaluate>
var ciclass = [];
var tbu = new TableUtils('${RP.getParameterValue("sysparm_sys_class_name")}').getTableExtensions();
<!-- tag "<" equals the "<" sign in html -->
for(var i=0;i<tbu.size();i++) {
ciclass.push(tbu.get(i));
}
ciclass = ciclass.join(',');
ciclass;
</g2:evaluate>
var ciclass = [];
var tbu = new TableUtils('${RP.getParameterValue("sysparm_sys_class_name")}').getTableExtensions();
<!-- tag "<" equals the "<" sign in html -->
for(var i=0;i<tbu.size();i++) {
ciclass.push(tbu.get(i));
}
ciclass = ciclass.join(',');
ciclass;
</g2:evaluate>
In case the class (sys_class_name) is updated in ServiceNow it is not audited, therefore a ‘custom’ audit record is created so the change of the class is visible in the History List and Calendar.

It’s a lot of code, but it’s also a nice result. Do you like it? Let me know!
abhi.jain39@outlook.com
abhi.jain39@outlook.com
-AJNow
ReplyDeleteIam very impressive your site gives the best and the most interesting information. This is just the kind of information that i had been looking for, i'm already your rss reader now and i would regularly watch out for the new posts, once again hats off to you
servicenow online training hyderabad
Thanks
DeleteGreat Article. its is very very helpful for all of us and I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.
ReplyDeleteservicenow online training
It is Great Article it is very useful information for us . i am searching for same information to save my time please check it once.Hadoop Online Traning Hyderabad
ReplyDeleteGreat Article. its is very very helpful for all of us and I never get bored while reading your article because, they are
ReplyDeletebecomes a more and more interesting from the starting lines until the end.
AWS Online Course