- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎02-07-2025 01:13 PM - edited ‎02-07-2025 01:17 PM
I need a sanity check on this idea. We have 214 application services (cmdb_ci_service_auto) table that we want to map to the CIs via tags. Its too many to go through and manually set the tag categories and we don't want to create duplicates. The CI owners will be tagging their VMs with the alias names of our Application services. My plan is to have a business rule on cmdb_key_value watching for newly tagged CIs coming through with the Application tag. It would then find and transition the application service to a tag-based application service and set the tag categories so the new CI candidates can be mapped automatically. I've spent a couple of days going through the Service Mapping Script Includes trying to reverse engineer how this is accomplished in the UI and this is the best I can come up with. I've tested this and it appears to work. Is there a better way to accomplish what we are trying to do?
(function() {
var serviceSysId = 'xxxxxxxxxxxxxxxxxxxxx';
// Step 1: Lookup and update cmdb_ci_service_auto table first
var autoServiceGr = new GlideRecord('cmdb_ci_service_auto');
if (autoServiceGr.get(serviceSysId)) {
// Update the sys_class_name to cmdb_ci_service_by_tags
autoServiceGr.setValue('sys_class_name', 'cmdb_ci_service_by_tags');
if (autoServiceGr.update()) {
gs.info('Step 1: cmdb_ci_service_auto record with sys_id ' + serviceSysId + ' sys_class_name updated to cmdb_ci_service_by_tags.');
} else {
gs.warn('Step 1: Failed to update sys_class_name in cmdb_ci_service_auto for sys_id ' + serviceSysId);
return; // Exit script if this update fails
}
} else {
gs.warn('Step 1: No record found in cmdb_ci_service_auto with sys_id ' + serviceSysId);
return; // Exit script if no record is found
}
// Step 2: Update the cmdb_ci_service_by_tags table after successful modification of cmdb_ci_service_auto
var tagsServiceGr = new GlideRecord('cmdb_ci_service_by_tags');
if (tagsServiceGr.get(serviceSysId)) {
// Retrieve the 'aliases' and 'used_for' values for categories
var applicationValue = tagsServiceGr.getValue('aliases') || 'Unknown Application';
var environmentRawValue = tagsServiceGr.getValue('used_for') || 'Unknown';
// Substitution for Environment values
var environmentValue;
switch (environmentRawValue.toLowerCase()) {
case 'production':
environmentValue = 'Prod';
break;
case 'test':
environmentValue = 'Test';
break;
case 'development':
environmentValue = 'Dev';
break;
default:
environmentValue = environmentRawValue; // Use as-is for other values
}
// Construct new metadata with Application and Environment categories
var newMetadata = {
"category_values": [
{ "category": "Application", "value": applicationValue },
{ "category": "Environment", "value": environmentValue }
],
"checksum": "WAITING_FOR_UPDATE"
};
tagsServiceGr.setValue('metadata', JSON.stringify(newMetadata));
// Update the service_populator field
tagsServiceGr.setValue('service_populator', 'cae02879c3b23300daa79624a1d3ae2f');
// Save the changes
if (tagsServiceGr.update()) {
gs.info('Step 2: cmdb_ci_service_by_tags record with sys_id ' + serviceSysId + ' updated successfully with Application: '
+ applicationValue + ' and Environment: ' + environmentValue);
} else {
gs.warn('Step 2: Failed to update cmdb_ci_service_by_tags record with sys_id ' + serviceSysId);
}
} else {
gs.warn('Step 2: No record found in cmdb_ci_service_by_tags with sys_id ' + serviceSysId);
}
})();
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-30-2025 01:56 PM - edited ‎06-02-2025 03:30 PM
I've completed some work to monitor for new application/environment tag candidates to transition to tag based app service. This can be setup as a Scheduled job to run on a daily basis.
(function executeRule(current, gsnc) {
var today = new GlideDateTime();
today.setDisplayValue(new GlideDateTime().getLocalDate().getByFormat("yyyy-MM-dd") + " 00:00:00");
//If you want to run this as the end of the day and only process new tags for just that day add a filter for appGR.addQuery('sys_created_on', '>=', today);
var appGR = new GlideRecord('cmdb_key_value');
appGR.addQuery('key', 'Application');
appGR.query();
var seen = {};
while (appGR.next()) {
var appValue = appGR.value.toString();
var configItem = appGR.configuration_item.toString();
var landscapeGR = new GlideRecord('cmdb_key_value');
landscapeGR.addQuery('configuration_item', configItem);
//Our 'Environment' is called 'Landscape'
landscapeGR.addQuery('key', 'Landscape');
landscapeGR.query();
if (!landscapeGR.next()) continue;
var landscapeValue = landscapeGR.value.toString();
if (seen[appValue + landscapeValue]) continue; // Ensure uniqueness
seen[appValue + landscapeValue] = true;
var serviceTagGR = new GlideRecord('cmdb_ci_service_by_tags');
serviceTagGR.addQuery('aliases', appValue);
serviceTagGR.addQuery('environment', normalizeEnvironment(landscapeValue));
serviceTagGR.query();
if (!serviceTagGR.hasNext()) {
process(appValue, normalizeEnvironment(landscapeValue)); // Placeholder function
}
}
})();
function normalizeEnvironment(env) {
if (!env) return env;
var map = {
'production': 'Prod',
'prod': 'Production',
'test': 'Test',
'development': 'Dev',
'dev': 'Development'
};
var lowerEnv = env.toLowerCase();
// If it's a full name, return abbreviation
if (['production', 'test', 'development'].includes(lowerEnv)) {
return map[lowerEnv];
}
// If it's an abbreviation, return full name
if (['prod', 'test', 'dev'].includes(lowerEnv)) {
return map[lowerEnv];
}
return env;
}
function process(applicationValue, environmentValue) {
// Step 1: Lookup in cmdb_ci_service_auto by application and environment
var autoServiceGr = new GlideRecord('cmdb_ci_service_auto');
autoServiceGr.addQuery('aliases', applicationValue);
autoServiceGr.addQuery('environment', environmentValue);
autoServiceGr.query();
if (autoServiceGr.next()) {
var serviceSysId = autoServiceGr.getUniqueValue();
var serviceName = autoServiceGr.name;
// Update sys_class_name
autoServiceGr.setValue('sys_class_name', 'cmdb_ci_service_by_tags');
if (!autoServiceGr.update()) {
gs.warn('Step 1: Failed to update sys_class_name for ' + serviceSysId);
return;
}
gs.info('Step 1: Updated sys_class_name to cmdb_ci_service_by_tags for ' + serviceSysId);
// Step 2: Update cmdb_ci_service_by_tags
var tagsServiceGr = new GlideRecord('cmdb_ci_service_by_tags');
if (tagsServiceGr.get(serviceSysId)) {
var newMetadata = {
"category_values": [{
"category": "Application",
"value": applicationValue
},
{
"category": "Landscape",
"value": normalizeEnvironment(environmentValue)
}
],
"checksum": "WAITING_FOR_UPDATE"
};
tagsServiceGr.setValue('metadata', JSON.stringify(newMetadata));
tagsServiceGr.setValue('service_populator', 'cae02879c3b23300daa79624a1d3ae2f');
tagsServiceGr.setValue('populator_status', '0'); //Draft - do not calculate yet
tagsServiceGr.setValue('calculation_status', '1'); //Waiting for Calculation
tagsServiceGr.setValue('type', '4'); //Tags Based
if (tagsServiceGr.update()) {
gs.info('Step 2: Updated metadata for service ' + serviceSysId);
} else {
gs.warn('Step 2: Failed to update metadata for service ' + serviceSysId);
}
gs.info("SBT: service " + serviceName + " (" + serviceSysId + ") has been created/updated.");
//update the service to contain an entry point
var payload = "{ " +
"\"name\": \"" + serviceName + "\", " +
"\"service_relations\": [ " +
"{ \"parent\": \"\", " +
"\"child\": \"" + serviceSysId + "\" " +
"} " +
"] " +
"}";
try {
var createOrUpdateServiceHandler = new CreateOrUpdateITService();
var result = createOrUpdateServiceHandler.processJSON(payload);
gs.info("SBT: Initial topology was created for service " + serviceName + " (" + serviceSysId + "): " + this.jsonParser.encode(result));
} catch (e) {
gs.warn("SBT: Error while creating initial topology for service " + serviceName + " (" + serviceSysId + "): " + (e.msg || e.toString()));
}
//update the service as ready for calculation
serviceGr = new GlideRecord('cmdb_ci_service_by_tags');
if (serviceGr.get(serviceSysId)) {
serviceGr.setValue('populator_status', '1'); //Ready for calculation
if (serviceGr.update()) {
gs.info("SBT: service " + serviceName + " ( " + serviceSysId + " ) is ready for calculation");
} else {
gs.warn("SBT: Failed updating populator status for service " + serviceName + " ( " + serviceSysId + ")");
}
} else {
gs.warn("SBT: service " + serviceName + " ( " + serviceSysId + ") creation DID NOT complete properly");
}
if (serviceSysId) {
var calcGr = new GlideRecord('cmdb_ci_service_calculated');
if (calcGr.get(serviceSysId)) {
gs.info("SBT: Calculating service " + serviceSysId);
var srvClassName = calcGr.getValue("sys_class_name");
var classGr = new GlideRecord(srvClassName);
classGr.get(serviceSysId);
var servicePopulatorRunner = new SNC.ServicePopulatorRunner('INTERACTIVE');
servicePopulatorRunner.run(classGr);
}
}
} else {
gs.warn('Step 2: No cmdb_ci_service_by_tags record for sys_id ' + serviceSysId);
}
} else {
gs.warn('Step 1: No cmdb_ci_service_auto record found for alias=' + applicationValue + ', env=' + environmentValue);
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎02-10-2025 08:25 AM
Hi @Greg D,
I assume that the Application Services in cmdb_ci_service_auto have no CI Relationships to CIs in a service map already? And you plan to convert them to tag based services then relate the tagged assets to the App Service by listening for Application-tagged CI values changing?
If this is running when a key value pair changes then would you not need to check the Application Service had already been migrated? Unless you have uniqueness enforced, the pair of 'aliases' and 'used_for' values could reference an existing migrated Application Service, and you could potentially have two Tag based Application Services with the same Metadata as a result, so it may be worth adding a check on these fields first before setting the Metadata and updating the record. Of course, I could be missing some existing logic that you haven't mentioned that caters for this.
But as far as the approach goes, it seems to make sense.
I hope this helps!
Mat
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎02-14-2025 08:28 AM
The only CI relationships that exist would be to the Business Application and then later down the road to the offerings. Good call on the check to see if the service has already been transitioned. Thank your for taking the time to provide feedback. I needed some confirmation I was heading down the correct path. I hear that tag mapping to existing services is coming in a future release.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-30-2025 01:56 PM - edited ‎06-02-2025 03:30 PM
I've completed some work to monitor for new application/environment tag candidates to transition to tag based app service. This can be setup as a Scheduled job to run on a daily basis.
(function executeRule(current, gsnc) {
var today = new GlideDateTime();
today.setDisplayValue(new GlideDateTime().getLocalDate().getByFormat("yyyy-MM-dd") + " 00:00:00");
//If you want to run this as the end of the day and only process new tags for just that day add a filter for appGR.addQuery('sys_created_on', '>=', today);
var appGR = new GlideRecord('cmdb_key_value');
appGR.addQuery('key', 'Application');
appGR.query();
var seen = {};
while (appGR.next()) {
var appValue = appGR.value.toString();
var configItem = appGR.configuration_item.toString();
var landscapeGR = new GlideRecord('cmdb_key_value');
landscapeGR.addQuery('configuration_item', configItem);
//Our 'Environment' is called 'Landscape'
landscapeGR.addQuery('key', 'Landscape');
landscapeGR.query();
if (!landscapeGR.next()) continue;
var landscapeValue = landscapeGR.value.toString();
if (seen[appValue + landscapeValue]) continue; // Ensure uniqueness
seen[appValue + landscapeValue] = true;
var serviceTagGR = new GlideRecord('cmdb_ci_service_by_tags');
serviceTagGR.addQuery('aliases', appValue);
serviceTagGR.addQuery('environment', normalizeEnvironment(landscapeValue));
serviceTagGR.query();
if (!serviceTagGR.hasNext()) {
process(appValue, normalizeEnvironment(landscapeValue)); // Placeholder function
}
}
})();
function normalizeEnvironment(env) {
if (!env) return env;
var map = {
'production': 'Prod',
'prod': 'Production',
'test': 'Test',
'development': 'Dev',
'dev': 'Development'
};
var lowerEnv = env.toLowerCase();
// If it's a full name, return abbreviation
if (['production', 'test', 'development'].includes(lowerEnv)) {
return map[lowerEnv];
}
// If it's an abbreviation, return full name
if (['prod', 'test', 'dev'].includes(lowerEnv)) {
return map[lowerEnv];
}
return env;
}
function process(applicationValue, environmentValue) {
// Step 1: Lookup in cmdb_ci_service_auto by application and environment
var autoServiceGr = new GlideRecord('cmdb_ci_service_auto');
autoServiceGr.addQuery('aliases', applicationValue);
autoServiceGr.addQuery('environment', environmentValue);
autoServiceGr.query();
if (autoServiceGr.next()) {
var serviceSysId = autoServiceGr.getUniqueValue();
var serviceName = autoServiceGr.name;
// Update sys_class_name
autoServiceGr.setValue('sys_class_name', 'cmdb_ci_service_by_tags');
if (!autoServiceGr.update()) {
gs.warn('Step 1: Failed to update sys_class_name for ' + serviceSysId);
return;
}
gs.info('Step 1: Updated sys_class_name to cmdb_ci_service_by_tags for ' + serviceSysId);
// Step 2: Update cmdb_ci_service_by_tags
var tagsServiceGr = new GlideRecord('cmdb_ci_service_by_tags');
if (tagsServiceGr.get(serviceSysId)) {
var newMetadata = {
"category_values": [{
"category": "Application",
"value": applicationValue
},
{
"category": "Landscape",
"value": normalizeEnvironment(environmentValue)
}
],
"checksum": "WAITING_FOR_UPDATE"
};
tagsServiceGr.setValue('metadata', JSON.stringify(newMetadata));
tagsServiceGr.setValue('service_populator', 'cae02879c3b23300daa79624a1d3ae2f');
tagsServiceGr.setValue('populator_status', '0'); //Draft - do not calculate yet
tagsServiceGr.setValue('calculation_status', '1'); //Waiting for Calculation
tagsServiceGr.setValue('type', '4'); //Tags Based
if (tagsServiceGr.update()) {
gs.info('Step 2: Updated metadata for service ' + serviceSysId);
} else {
gs.warn('Step 2: Failed to update metadata for service ' + serviceSysId);
}
gs.info("SBT: service " + serviceName + " (" + serviceSysId + ") has been created/updated.");
//update the service to contain an entry point
var payload = "{ " +
"\"name\": \"" + serviceName + "\", " +
"\"service_relations\": [ " +
"{ \"parent\": \"\", " +
"\"child\": \"" + serviceSysId + "\" " +
"} " +
"] " +
"}";
try {
var createOrUpdateServiceHandler = new CreateOrUpdateITService();
var result = createOrUpdateServiceHandler.processJSON(payload);
gs.info("SBT: Initial topology was created for service " + serviceName + " (" + serviceSysId + "): " + this.jsonParser.encode(result));
} catch (e) {
gs.warn("SBT: Error while creating initial topology for service " + serviceName + " (" + serviceSysId + "): " + (e.msg || e.toString()));
}
//update the service as ready for calculation
serviceGr = new GlideRecord('cmdb_ci_service_by_tags');
if (serviceGr.get(serviceSysId)) {
serviceGr.setValue('populator_status', '1'); //Ready for calculation
if (serviceGr.update()) {
gs.info("SBT: service " + serviceName + " ( " + serviceSysId + " ) is ready for calculation");
} else {
gs.warn("SBT: Failed updating populator status for service " + serviceName + " ( " + serviceSysId + ")");
}
} else {
gs.warn("SBT: service " + serviceName + " ( " + serviceSysId + ") creation DID NOT complete properly");
}
if (serviceSysId) {
var calcGr = new GlideRecord('cmdb_ci_service_calculated');
if (calcGr.get(serviceSysId)) {
gs.info("SBT: Calculating service " + serviceSysId);
var srvClassName = calcGr.getValue("sys_class_name");
var classGr = new GlideRecord(srvClassName);
classGr.get(serviceSysId);
var servicePopulatorRunner = new SNC.ServicePopulatorRunner('INTERACTIVE');
servicePopulatorRunner.run(classGr);
}
}
} else {
gs.warn('Step 2: No cmdb_ci_service_by_tags record for sys_id ' + serviceSysId);
}
} else {
gs.warn('Step 1: No cmdb_ci_service_auto record found for alias=' + applicationValue + ', env=' + environmentValue);
}
}