rhysbrennan
Tera Expert

Need:

I was presented with a need to schedule a standard change to be created every 6 months to change passwords of the domain admin accounts. Though this will be something we can incorporate for any repeated standard change.

Because Flow is the new thing as much as some people call the it the Accountants developer screen I think it works well to develop and understand how processes work. So to that effect we are building it in the Flow Engine. 

Initial Thoughts:

I planned to create a reoccurring Flow that created a Change Request using a Standard Change Template, this was mainly to allow us to do this process on demand and also meet the requirement of scheduling a standard change. 

My plan was to build a standard change template, get it approved and then simply use the Flow action "Create standard change". Servicenow have an "Action" to do this, however it doesn't actually work and is simply a placeholder:
find_real_file.png

So I turned to google and found examples of others making this work through custom actions where by they create a change, with the Type = Standard and the Standard Change Template = "Template"

https://community.servicenow.com/community?id=community_question&sys_id=0a337b7cdb2aa410ab0202d5ca96191e was my initial result where @Kieran Anson  showed that just by setting the Change request record to a Type = Standard and the Standard Change Template Version = "The template create" we can accomplish the needs. This was a great help however this didn't work entirely. 


Creating a Standard Change from Script/The Server and the problem built into Standard Changes: 

If you create a change record with with a Type = Standard and the Standard Change Template Version = "The templated change to create" this triggers a business rule "Restrict fields from Standard Change" on the change request table. This BR does it's job that if a Change Request is created and meets the BR conditions the Change's fields are populated from the template. However the catch is that ONLY read only fields are copied, this is meant to stop someone changing a records details from the client side and is for security purposes. The read only fileds are configured by the Standard Change Properties:
find_real_file.png

So if the template has fields "templated" that are not read only, they are ignored. Which may have no impact if all fields are locked down in your instance; however we only lock down some of the fields, eg:
find_real_file.png

The majority of fields may be read only however the probelm is we may allow for the Justification, or Risk and impact analysis to be set for example. Because the business rule only set the "Read Only" fields the template isn't applied in full and the Flow doesn't error because it didn't set evey field; the BR did.

How ServiceNow stop Flow from filling all fields:

Now I tried to see why the UI action to create a Change from a Standard Change Template works without these restrictions however I am short of time so I focused on my need to use Flow on the "Server side" to create the change. So to that end I chased the Business Rule that was setting Values for all the "Read Only" fields.

I won't examplin all of the process in details, the ServiceNow Business Rule process is to lookup readonly fields and if they are on the record they are set those fields to the values in the template. I believe this is to stop people manipulating standard change template before submitting a "New Reciord". 

Fix:

Firstly to allow future updates and to customise the business rule: Make the OOB "Restrict fields from Standard Change" business rule inactive.

Then copy (Insert and Stay) the "Restrict fields from Standard Change" business rule /nav_to.do?uri=sys_script.do?sys_id=d39eec581be9f810a034639b274bcb28

Change the script to call a custom override: new StdChangeUtils().applyTemplateFields(current); //Custom script. This is to make use of OOB functions that were only copying the read only fields.

(function executeRule(current, previous /*undefined when async or display*/) {
	
    //If action is insert then reapply the readonly fields from the template as fields could be edited via the browser console.
    //Unable to check if the fields have changes because on the insert the changes are from the template and are valid changes.
	if (current.operation() === "insert")
		new StdChangeUtils().applyTemplateFields(current); //Custom script. This is to make use of OOB functions that were only copying the read only fields.

	//If action is update then check that none of the read only fields have been edited and if they have then abort the action
	//and notify the user of the fields that cannot be edited.
	else if (current.operation() === "update" & gs.getUserID() != 'system') {
		var fieldNames = new StdChangeUtils().getReadOnlyFields();
		var modifiedFields = [];

		for (var i = 0; i < fieldNames.length; i++) {
			if(current[fieldNames[i]].changes())
				modifiedFields.push(current[fieldNames[i]].getLabel());
		}

		if (modifiedFields.length > 0) {
			gs.addErrorMessage(gs.getMessage("Fields {0} populated from a Standard Change Template cannot be modified.", modifiedFields.toString()));
			current.setAbortAction(true);
		}
	}
})(current, typeof previous != 'undefined' ?  previous : null);

Go to the "StdChangeUtils"  /sys_script_include.do?sys_id=3f894542ffb00200b18affffffffff79 scipt include and create "applyTemplateFields" as a function that will get all fields, regardless of what is readonly. 

var StdChangeUtils = Class.create();
StdChangeUtils.prototype = Object.extendsObject(StdChangeUtilsSNC, {

	initialize: function(request, responseXML, gc) {
		StdChangeUtilsSNC.prototype.initialize.call(this, request, responseXML, gc);
	},

	/***************************
	 *
	 *Add customer changes below
	 *
	 ****************************/
	//Orginally this would JUST: Reapplies the values for the READONLY FIELDS from the template associated to the change record
	//Now this just applies ALL FIELDS, Read Only Fields and all the others.
	//Called by /nav_to.do?uri=sys_script.do?sys_id=d39eec581be9f810a034639b274bcb28 "TCC-Restrict fields from Standard Change" on INSERT of a change that is change template != EMPTY
	applyTemplateFields: function(record) {
		if (!record)
			return;

		var template = new GlideRecord(this.TABLE_NAME_TEMPLATE);
		if (template.get(record.std_change_producer_version.std_change_producer.template)) {
			//var readonlyFields = this.getReadOnlyFields();
			var queryValues = this._parseEncodedQuery(template.getValue('template'));
			//Foreach Readonly Field 
			//for (var j = 0; j < readonlyFields.length; j++) {
			//Foreach Field in the "Record"
			gru = new GlideRecordUtil();
			var fieldList = gru.getFields(record);
			for (var j = 0; j < fieldList.length; j++) {
				//See if it is in the template
				var nameIndex = queryValues.names.indexOf(fieldList[j]);
				
				if (nameIndex > -1)
					//Found a match so set the value
					record.setValue(fieldList[j], queryValues.vals[nameIndex]);
			}
		}
	},

    type: 'StdChangeUtils'
});

Conclusion:

By changing the Business Rule to source copy all fields, we can schedule a standard change.

I hope that helps someone else, and I'm happy clarify anything further if needed. 

 

 

Comments
stevesmith
Giga Sage

This seems like an alternative way to achieve the same goal without modifying code.

https://community.servicenow.com/community?id=community_article&sys_id=cd75aa85db2dfc10a538826305961...

 

rhysbrennan
Tera Expert
Very interested but that link takes be back here
Version history
Last update:
‎07-22-2021 08:42 PM
Updated by: