Find your people. Pick a challenge. Ship something real. The CreatorCon Hackathon is coming to the Community Pavilion for one epic night. Every skill level, every role welcome. Join us on May 5th and learn more here.

Automated Lifecycle Management: Multi-App & Plugin Batch Updates

BlGeorgiev
Tera Contributor
Automated Lifecycle Management: Multi-App and Plugin Batch Updates
The Challenge
Managing updates for dozens of ServiceNow Store Applications and Plugins is traditionally a manual, time-consuming process. Administrators usually have to navigate to the Application Manager for every single update, which is inefficient for large-scale environments or Personal Developer Instances (PDIs).
The Solution: Zero-Touch Updates
This solution uses two custom Script Includes and a Scheduled Job to automate the entire flow: Sync, Discovery, Batch Execution, and OOTB Tracking.
1. Technical Architecture
Component A: UpgradeDiscoveryService
This service acts as the Scout. It queries both the Store Application table and the Plugin table to find version mismatches.
  • Table sys_store_app: Compares version vs latest_version.
  • Table v_plugin: Compares version vs available_version.
Component B: AppUpgradeManager
The Executor. It uses the internal sn_appclient.AppPluginInstallation API.
Key Innovation: It packages all discovered updates into a single JSON payload. By using the versionObj structure, it bypasses common internal API errors and ensures the system accepts the batch.
Component C: The Scheduled Job
The Orchestrator. It runs the sync, triggers the discovery, and executes the batch. It includes a gs.sleep function to account for the asynchronous nature of batch creation, ensuring the Plan ID is captured and logged.
2. OOTB Tracking and Visibility
The true power of this solution lies in its integration with ServiceNow native deployment tracking. Once the script executes, the status is managed by the platform engine.
Table: Batch Install Plans (sys_batch_install_plan)
Immediately after execution, a record is created here titled FKA Force Update/Install.
  • Visibility: This table shows exactly which applications were included in the batch.
  • State Management: You can watch the state transition from Draft to Execution Scheduled to Completed.
Table: Execution Tracker (sys_execution_tracker)
This is the progress engine of the installation. Every batch plan is linked to a tracker.
  • Real-time Progress: It provides a percentage-based progress bar of the total installation.
  • Detail Logs: If a specific plugin fails to install, the Execution Tracker captures the exact Java stack trace or dependency error, making troubleshooting simple.
3. The Codebase
UpgradeDiscoveryService
 
javascript
var UpgradeDiscoveryService = Class.create();
UpgradeDiscoveryService.prototype = {
    initialize: function() {},

    getInstallQueue: function() {
        var itemsFound = [];

        // 1. STORE APPS (sys_store_app)
        var appGR = new GlideRecord('sys_store_app');
        appGR.addNotNullQuery('version');
        appGR.addNotNullQuery('latest_version');        appGR.query();

        while (appGR.next()) {
            if (appGR.getValue('version') !== appGR.getValue('latest_version')) {
                itemsFound.push(appGR.getValue('scope'));
                gs.info('Discovery: Found App update for ' + appGR.name);
            }
        }

        // 2. PLUGINS (v_plugin)
        var pluginGR = new GlideRecord('v_plugin');
        pluginGR.addNotNullQuery('version');
        pluginGR.addNotNullQuery('available_version');        pluginGR.query();

        while (pluginGR.next()) {
            if (pluginGR.getValue('version') !== pluginGR.getValue('available_version')) {
                itemsFound.push(pluginGR.getValue('id'));
                gs.info('Discovery: Found Plugin update for ' + pluginGR.name);
            }
        }
        return itemsFound;
    },
    type: 'UpgradeDiscoveryService'};
 
AppUpgradeManager
 
javascript
var AppUpgradeManager = Class.create();
AppUpgradeManager.prototype = {
    initialize: function() {},

    processList: function(arrToBeInstalled, dryRun, loadDemoData) {
        if (!Array.isArray(arrToBeInstalled) || arrToBeInstalled.length === 0) return null;

        var _objToBeInstalled = {};
        var _grPlugins = new GlideRecord('v_plugin');
        var _grStore = new GlideRecord('sys_store_app');

        arrToBeInstalled.forEach(function(strID) {
            if (_grPlugins.get('id', strID)) {
                if (_grPlugins.getValue('active') != 'active') {
                    _objToBeInstalled[strID] = {
                        "plugin_id": strID,
                        "scope": _grPlugins.getValue('scope'),
                        "app_name": _grPlugins.getValue('name'),
                        "loadDemoData": loadDemoData,
                        "isPlugin": true                    };
                }
            } else {
                var query = _grStore.addQuery('scope', strID);
                query.addOrCondition('vendor_id', strID);
                _grStore.query();
                if (_grStore.next()) {
                    _objToBeInstalled[_grStore.getUniqueValue()] = {
                        "sys_id": _grStore.getUniqueValue(),
                        "app_name": _grStore.getValue('name'),
                        "loadDemoData": loadDemoData,
                        "isStoreApp": true,
                        "appScope": _grStore.getValue('scope'),
                        "versionObj": { "version": _grStore.getValue('latest_version') }
                    };
                }
            }
        });

        if (Object.keys(_objToBeInstalled).length > 0 && !dryRun) {
            return new sn_appclient.AppPluginInstallation().validateAndBatchInstall('FKA Force Update/Install', _objToBeInstalled);
        }
        return _objToBeInstalled;
    },
    type: 'AppUpgradeManager'};
 
Scheduled Job
 
javascript
(function() {
    // 1. Sync Store Metadata
    var refreshMetadata = new GlideRecord('sys_trigger');
    if (refreshMetadata.get('name', 'Refresh App Store Metadata')) {
        gs.executeNow(refreshMetadata);
    }
    
    var discovery = new UpgradeDiscoveryService();
    var updateList = discovery.getInstallQueue();

    if (updateList && updateList.length > 0) {
        var manager = new AppUpgradeManager();
        var result = manager.processList(updateList, false, false);

        gs.sleep(2000); // Wait for async record creation

        var planGR = new GlideRecord('sys_batch_install_plan');
        planGR.addQuery('name', 'CONTAINS', 'FKA Force Update/Install');
        planGR.orderByDesc('sys_created_on');
        planGR.setLimit(1);
        planGR.query();

        if (planGR.next()) {
            gs.info('Scheduled Auto-Update: Batch Plan ID: ' + planGR.getUniqueValue() + ' | Status: ' + planGR.state);
            gs.info('Monitor Execution Tracker: ' + planGR.execution_tracker);
        }
    } else {
        gs.info('Scheduled Auto-Update: No new updates. Done!');
    }
})();
Conclusion
By implementing this flow, you transform a manual task into a robust, logs-backed automated service. Using sys_batch_install_plan ensures your updates are handled with the same rigor as a manual deployment, while sys_execution_tracker provides the granular visibility needed for enterprise-level auditing.
0 REPLIES 0