Automated Lifecycle Management: Multi-App & Plugin Batch Updates
Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
16 hours ago
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).
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.
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.
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.
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.
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.
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.
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.
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.
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
