Automating DevOps Change Deployment Windows

November 17, 2022
|
3
min read

ServiceNow DevOps provides huge benefits and greatly increases the velocity of changes released. While it increases efficiency, a specific problem that organizations looking to adopt a modern DevOps release model often need to solve is the coordination of these changes with change freezes or maintenance schedules of targeted systems. 

There is, however, an easy way to solve this using the out-of-box Conflict detection system. Flow Designer gives us a way to leverage this by automating the scheduling of a change for an appropriate time, regardless of when the change is created. 

In this example, blackout and maintenance schedules are in use, and we’ll be looking to schedule a DevOps change for the next date and time, with a small buffer, that is outside of a blackout, and within the maintenance window of the CI we’ll be working with.

Using a new custom flow action, we’ll provide as inputs a reference to the change request, and a how far into the future we’ll look for an open window, in hours. As outputs, we’ll have one for the new start date if one is found, and another that symbolizes we’ve reached the end of our iterations and have not found a time without conflicts to deploy the change if not.

In our new action we have one step, a script step that will perform the iterations and call the conflict detector. This script can be found below:

(function execute(inputs, outputs) {
  
  var counter = 0;
  var hrs = inputs.hours;
  var i = 0;
  while (counter < hrs){
    
    var cr = new GlideRecord('change_request');
    cr.get('sys_id', inputs.change.sys_id);
    
    var conflictDetector = new ChangeCheckConflicts(cr);
    var conflictResults = conflictDetector.check();
    gs.info('results ' + conflictResults);
    if(conflictResults > 0){
      var conflict = new GlideRecord('conflict');
      conflict.addQuery('change', inputs.change.sys_id);
      var conflictOr = conflict.addQuery('type', 'blackout');
      conflictOr.addOrCondition('type', 'not_in_maintenance_window');
      conflict.query();
      if (conflict.hasNext()){
        var startgdt = new GlideDateTime(cr.getValue('start_date'));
        var endgdt = new GlideDateTime(cr.getValue('end_date'));
        var chgstart = startgdt.addSeconds(3600);
        var chgend = endgdt.addSeconds(3600);
        cr.start_date.setValue(startgdt);
        cr.end_date.setValue(endgdt);
        cr.autoSysFields(false);
        cr.setWorkflow(false);
        cr.update();
        i++;
        counter ++;
      } else {
        outputs.truestart = cr.start_date;
        return;
      }
    } else {
    outputs.truestart = cr.start_date;
    return;
    }
  } 
    
  conflictDetector = new ChangeCheckConflicts(cr);
  conflictResults = conflictDetector.check();
  if(conflictResults > 0){
    conflict = new GlideRecord('conflict');
    conflict.addQuery('change', inputs.change.sys_id);
    conflictOr = conflict.addQuery('type', 'blackout');
    conflictOr.addOrCondition('type', 'not_in_maintenance_window');
    conflict.query();
    if (conflict.hasNext()){
      outputs.end = true;
    } else {
      outputs.truestart = cr.start_date;
    }
  }
      
})(inputs, outputs);

As can be seen above, this will call the conflict checker and if conflicts are found, it will increment the planned start and end dates by one hour and loop again. Once it has iterated through the number of hours provided as an input, it will perform one more check to see if conflicts exist from the final call, and if so, set the End output, which will be used later in the flow to cancel the change.

With this action in our flow, regardless of the date and time the change is created from our CD pipeline trigger, we’re able to know that there will be no problems deploying the change at the appropriate time.

If you’d like to use this flow action in your own instance, you can download it from our open source GitHub repository, along with other helpful scripts and applications.