brianquinn
ServiceNow Employee

Introduction

 

A healthy CMDB depends on more than just discovery.  While automated discovery tools do an excellent job of finding configuration items and populating technical attributes, they often fall short in one critical area: the operational "grey-matter" data that gives CIs real business context.  Attributes like Owned By, Managed By Group, and Environment are essential for operational health, compliance, and service impact analysis but they are often not populated incompletely, or not at all.

 

The problem isn't that the data doesn't exist, it may already be present in the form of tags on the CIs, or hiding in plain sight within the CI name itself.  The challenge is extracting and applying that data at scale, consistently, without requiring deep CMDB expertise.

 

This blog post and its accompanying video series explore how ServiceNow's Now Assist can be extended, using custom agentic workflows and purpose-built AI agents to close this gap in a practical, repeatable way.

 

Solution Overview

 

Now Assist for CMDB offers many powerful capabilities out of the box, such as Manage Duplicates, SGC Diagnosis, and agentic workflows for CI creation and CMDB governance advice.  These are great starting points, but they don't address the specific challenge of enriching the CMDB with non-discoverable attributes.

 

This is where extending Now Assist does make sense.  By combining the governance, repeatability and power of ServiceNow Flows with the natural language flexibility of Agentic Workflows, AI Agents, and LLM Prompts through Now Assist Skills, end users can easily improve the health of the CMDB, even if they are not a CMDB expert.

 

This proof-of-concept solution consists of three components working together: an agentic workflow that serves as the entry point, and two specialized AI agents that handle the actual enrichment work.

 

  • Agentic Workflow
    • Triggered by Now Assist when a user wants to populate a CMDB attribute
    • ​Prompts user to choose the desired population method
    • ​Triggers appropriate AI Agent

 

  • AI Agent: Populate a CMDB Attribute based on tags
    • Collect basic information from the user
      • CMDB Class (or all classes) to be updated
      • CMDB Attribute to be updated
    • Find relevant tags for the selected attribute
    • Request user confirmation at each step
    • Trigger a subflow to update the CIs

 

  • AI Agent: Populate a CMDB Attribute based on naming convention
    • Collect basic information from the user
      • CMDB Class (or all classes) to be updated
      • CMDB Attribute to be updated
      • Description of the CI naming convention
      • Value to set the attribute to
    • Generate a regular expression to match the specified naming convention
    • Request user confirmation at each step
    • Trigger script to update CIs

 

Video Series

 

 

Solution Overview

 

The following sections provide the sample code and configuration for each component of the proof-of-concept solution demonstrated in the video.  Each episode in the series builds on the previous one, so it's recommended to work through them in order.  Refer to the individual videos for step-by-step instructions on configuring each component in your own environment.

 

Note: This proof of concept is intended for educational purposes and to show the art of the possible.  It is not designed for as-is deployment into a production environment.

 

Framework (Video link)

 

  • Agentic Workflow
    • Workflow Name: Populate a CMDB Attribute
    • Workflow Description
      This agentic workflow will help a user populate a CMDB attribute for specified  CIs using one of the supported population methods
    • List of Steps
      1. Ask the user to determine which method they want to use to populate the attribute.  Only provider the user with population methods that have a corresponding AI Agent 
      2. Use the corresponding AI Agent matching the requested population method to collect the necessary data and populate the CMDB
      3. Inform the user when all updates have been completed.
      4. Ask the user if they are done or if they want to perform another action in step 1.
    • User access: Users with specific roles (sn_cmdb_editor)
    • Now Assist Panel: Enabled
    • Set as Promoted
      • Conversational Interfaces -> Virtual Agent -> Designer -> Now Assist Panel - Platform

 

  • Common Tools
    • Now Assist Skills
      • Find exact CMDB Class based on user input
        • Skill Name: Find CMDB Class
        • Skill Description
          Find a CMDB Class based on user input
        • Now Assist Skill Inputs
          • Name: CMDBClass
            • Type: String
            • Mandatory: True
        • Now Assist Skill Tools
          • Name: GetAllClasses
            • Tool type: Script
            • Script Style: Write my own Script
            • Script:
              var classes = [];
              
              // Query for all tables that extend cmdb_ci
              var records = new GlideRecord('sys_db_object');
              records.addQuery('sys_class_path', 'STARTSWITH', '/!!');
              records.query();
              
              // Store the label for each cmdb_ci table
              while (records.next()) {
              	classes.push(records.getValue('label'));
              }
              	
              return classes.toString();
        • Prompt Name: Find CMDB Class using Now LLM
        • Prompt
          You are helping to populate a CMDB.  Given the list of all available CMDB classes below, find the class that best matches the description of "{{cmdbclass}}".  Only classes from the list of all available classes should be used, no other classes should be considered.  Output only the class name, do not output any other information.
          
          ALL AVAILABLE CLASSES: {{GetAllClasses.output}}
      • Find exact CMDB Attribute based on user input
        • Skill Name: Find CMDB Attribute
        • Skill Description
          Find a CMDB Attribute based on user input
        • Now Assist Skill Inputs
          • Name: CMDBAttribute
            • Type: String
            • Mandatory: True
        • Now Assist Skill Tools
          • Name: GetAllAttributes
            • Tool Type: Script
            • Script Style: Write my own Script
            • Script
              function getAttributes() {
              	var attributes = [];
              
              	// Query for all base cmdb_ci attributes
              	var records = new GlideRecord('sys_dictionary');
              	records.addQuery('name', 'cmdb_ci');
              	records.query();
              
              	// Store the label for each cmdb_ci attribute
              	while (records.next()) {
              		attributes.push(records.getValue('column_label'));
              	}
              	
              	return attributes;
              }
              
              return getAttributes().toString();
          • Prompt Name: Find CMDB Attribute using Now LLM
          • Prompt
            You are helping to populate a CMDB based on tags on the Configuration Items.  Given the list of all available attributes below, find the attribute that best matches the description of "{{cmdbattribute}}".  Only attributes from the list of all available attributes should be used, no other attributes should be considered.  Output only the attribute name, do not output any other information.
            
            ALL AVAILABLE ATTRIBUTES:  {{GetAllAttributes.output}} 

 

AI Agent to populate a CMDB Attribute based on tags (Video link)

 

  • Agent Specific Tools
    • Now Assist Skills
      • Find Relevant Tags for CMDB Attribute
        • Skill Name: Find Relevant Tags for CMDB Attribute
        • Skill Description
          Find list of tags that are relevant for a specified CMDB Attribute
        • Now Assist Skill Inputs
          • Name: CMDBAttribute
            • Type: String
            • Mandatory: True
        • Now Assist Skill Tools
          • Name: GetAllTags
            • Tool type: Script
            • Script Style: Write my own Script
            • Script
              // Get all unique tags from CMDB Key Value table
              function getUniqueTags() {
              	var tags = [];
              	var records = new GlideAggregate('cmdb_key_value');
              	records.addAggregate('COUNT', 'key');
              	records.groupBy('key');
              	records.query();
              	while (records.next()) {
              		tags.push(records.getValue('key').toString().toLowerCase());
              	}
              	return tags;
              }
              
              return getUniqueTags().toString();
          • Name: ConvertAttributeToLowerCase
            • Tool type: Script
            • Script Style: Write my own Script
            • Script
              // Convert CMDB Attribute Input to lower case
              return context.getAllValues()['cmdbattribute'].toLowerCase();
        • Prompt Name: Find Relevant Tags using Now LLM
        • Prompt
          You are helping to populate a CMDB based on tags on the Configuration Items.  Given the list of all available tags below, create a list of that tags would be most appropriate to populate the CMDB attribute "{{ConvertAttributeToLowerCase.output}}".  Only tags from the list of all available tags should be used, no other tags should be included in the list.  Only include tags that have a high level of confidence.  Format the list as a comma separated list.  Output only the comma separated list, do not output any other information.  Order the list so the most relevant tags are at the end of the list.
          
          ALL AVAILABLE TAGS: {{GetAllTags.output}} 
    • Flow Designer
      • Action
        • Name: Update CMDB Attribute for a Single CI
        • Action Inputs
          • Label: Configuration Item
            • Name: configuration_item
            • Type: Reference.Configuration Item
            • Mandatory: True
          • Label: Attribute Label
            • Name: attribute_label
            • Type: String
            • Mandatory: True
          • Label: Value
            • Name: value
            • Type: String
            • Mandatory: True
        • Action Steps
          • Script Step
            • Variables
              • Name: ciSysId
                • Value: Configuration Item > Sys Id
              • Name: attributeLabel
                • Value: Action > Attribute Label
              • Name: value
                • Value: Action > Value
            • Script
              // Convert a string value to a reference sys_id by searching common fields
              function getReferenceSysId(table, value){
              
                  // Initialize GlideRecord 
                  var tabGr = new GlideRecord(table);
              
                  // Query the dictionary to find the tables display field and other common fields that may store the string value being referenced
                  var dictGr = new GlideRecord('sys_dictionary');
                  dictGr.addQuery('name', table);
                  var query = dictGr.addQuery('display', true);
                  query.addOrCondition('element', 'name');
              
                  // Email and username are primarily for looking up references in the sys_user table
                  query.addOrCondition('element', 'email');
                  query.addOrCondition('element', 'user_name');
                  query.orderBy('display');
                  dictGr.query();
              
                  // Loop through each field that may hold the string value
                  while (dictGr.next()) {
                      tabGr.initialize();
              
                      // Query the table for a record that matches the string value
                      tabGr.addQuery(dictGr.element, value);
                      tabGr.query();
              
                      // Return the records sys_id if there is exactly 1 match
                      if( tabGr.getRowCount() == 1 && tabGr.next() )
                          return tabGr.getUniqueValue();
              
                  }
              
                  // Return an emtry string if a unique exact match isn't found
                  return;
              }
              
              // Function to get Attribute's dictionary record from Attribute Label
              function getAttributeFromLabel(attributeLabel){
                  var dictGr = new GlideRecord('sys_dictionary');
                  dictGr.addQuery('name', 'cmdb_ci');
                  dictGr.addQuery('column_label', attributeLabel);
                  dictGr.query();
                  if (dictGr.next()) 
                      return dictGr;
              }
              
              // Get Field Name from Label
              var attributeGr = getAttributeFromLabel(inputs['attributeLabel']);
              
              if ( !gs.nil(attributeGr) ) {
              
                  // Use GlideRecordSecure to enfore ACLs when updating
                  var ciGr = new GlideRecordSecure('cmdb_ci');
              
                  // Query for the exact record using the sys_id
                  if( !gs.nil(inputs['ciSysId']) && ciGr.get('sys_id', inputs['ciSysId']) ){
              
                      // If the field type is a reference, attempt to convert the string value to the reference sys_id
                      if( attributeGr.internal_type == "reference" ){
                          var value = getReferenceSysId(attributeGr.reference.toString(), inputs['value']);
                          if( !gs.nil(value) )
                              ciGr.setValue(attributeGr.element.toString(), value);
                      // Otherwise set the display value
                      }else{
                          ciGr.setDisplayValue(attributeGr.element.toString(), inputs['value']);
                      }
                          
                      ciGr.update();
                  }
              
              }
      • Subflow
        • Name: Update CMDB Attribute from Tag Values
        • Subflow Inputs
          • Label: Class
            • Name: class
            • Type: String
            • Mandatory: True
          • Label: Tag
            • Name: tag
            • Type: String
            • Mandatory: True
          • Label: Attribute
            • Name: attribute
            • Type: String
            • Mandatory: True
        • Flow Variables
          • Label: Table Name
            • Name: table_name
            • Type: String
        • Steps
        • If
          • Label: All Classes?
          • Condition: "Input > Class is all" OR "Input > Class is all calasses"
            • Note: In both conditions, add a "String -> To Lower Case" transform
        • Then
          • Set Flow Variable
            • Table Name = cmdb_ci
        • Else
          • Set Flow Variable
            • Table Name = <Set by Script>
              • Script
                // Use sys_db_object to retrieve table name from table label input
                var gr = new GlideRecord('sys_db_object');
                if( gr.get('label', fd_data.subflow_inputs.class) )
                    return gr.name.toString();
        • Loop Up Records
          • Table: Key Value
          • Conditions
            • Key is Input > Tag
            • Configuration item.Class is a Flow Variables > Table Name
            • Configuration item.Operational Status is not Retired
            • Configuration item.Install Status is not Retired
            • Configuration item.Lifecycle Stage Status is not Retired OR Configuration item.Lifecycle Stage Status is empty
        • For Each
          • Items: 5 - Look Up Records > Key Value Records
          • Update CMDB Attribute for a Single CI (Custom Flow Action created in previous section)
            • Inputs:
              • Configuration Item: 6 - For Each > Key Value Record >  Configuration Item
              • Attribute: Input > Attribute
              • Value: 6 - For Each >  Key Value Record > Value

 

  • AI Agent
    • AI Agent Name: Update CMDB Attribute based on tag values
    • AI Agent Description
      Update a CMDB Attribute for all CIs based on tag values
    • AI Agent Role
      You are a CMDB administrator and you are tasked with updating a CMDB attribute for all CIs based on tag values.  You need to determine help determine what tags should be used and then update the attributes based on those tags.
    • List of Steps
      1. Ask the user to determine which CMDB class they want to populate.  Inform the user they can select all classes or choose a specific class.
      
      2. If the user does not specify all classes, find the exact CMDB class matching requested class and confirm with the user that the exact CMDB class found is correct before proceeding to the next step.  Do not proceed to the next action without user confirmation.
      
      3. Ask the user to determine which CMDB attribute they want to populate.
      
      4. Find the exact CMDB attribute matching the requested attribute and confirm with the user that the exact CMDB attribute found is correct before proceeding to the next step.  Do not proceed to the next action without user confirmation.
      
      5. Find the list of tags that can be used to populate the requested CMDB attribute and confirm with the user that the list of tags is correct before proceeding to the next step.  If the user requests a change, repeat this step.  Do not proceed to the next action without user confirmation.  
      
      6. Ask the user for a final confirmation that the CMDB class, CMDB attribute and list of tags is correct before proceeding to the next step.  If the user requests a change, repeat this step.  Do not proceed to the next action without user confirmation.
      
      7. After the CMDB class, CMDB attribute and list of tags have all been confirmed by the user, proceed with updating the CMDB attribute with the tag values.  This will have to be done once for each tag in the list and it should be done in the exact order of the list.
    • User access: Users with specific roles (sn_cmdb_editor)
    • Now Assist Skills to add
      • Skill: Find CMDB Class
        • Name:  Find exact CMDB Class
        • Tool Description
          Find exact CMDB class based on user input class
        • Execution Mode: Autonomous
        • Display output: No
        • Processing Message
          Finding CMDB Class {{cmdbclass}}
      • Skill: Find CMDB Attribute
        • Find exact CMDB Attribute 
        • Tool Description
          Find exact CMDB attribute based on user input attribute
        • Execution Mode: Autonomous
        • Display output: No
        • Processing Message
          Finding CMDB Attribute {{cmdbattribute}}
      • Skill: Find Relevant Tags for CMDB Attribute
        • Name:  Find Relevant Tags for CMDB Attribute
        • Tool Description
          Find Relevant Tags for CMDB Attribute
        • Execution Mode: Autonomous
        • Display output: No
        • Processing Message
          Finding relevant tags for CMDB attribute {{cmdbattribute}}
    • Subflow Skills to add
      • Subflow: Update CMDB Attribute from Tag Values
        • Name:  Update CMDB Attribute from Tag Values
        • Tool Description
          Updates CMDB Attribute for all CIs in the specified class with the specified tag.
        • Execution Mode: Autonomous
        • Display output: No
        • Processing Message
          Updating CMDB attribute for CIs with tag {{tag}}

 

AI Agent to populate a CMDB Attribute based on naming convention (Video link)

 

  • Agent Specific Tools
    • Now Assist Skills
      • Generate Javascript Regular Expression
        • Skill Name: Generate Javascript Regular Expression
        • Skill Description
          Generate a Javascript Regular Expression from a user provided description that is compatible with ES5 Standards
        • Default Provider: Azure OpenAI
        • Prover API: Chat Completions
          • If there are 2 options, select the first one
        • Now Assist Skill Inputs
          • Name: Description
            • Type: String
            • Mandatory: True
        • Now Assist Skill Tools
          • Name: ConvertDescriptionToLowerCase
            • Tool type: Script
            • Script Style: Write my own Script
            • Script
              // Convert CMDB Attribute Input to lower case
              return context.getAllValues()['cmdbattribute'].toLowerCase();
        • Prompt Name: Generate Javascript Regular Expression using Azure OpenAI Responses
        • Prompt Model: gpt4
        • Prompt
          As an expert in Javascript and Regular expressions generate a regular expression based on the following description.  Ensure that the regular expression is compatible with ES5 Standards.  The output should contain only the regular expression represented as a string.  Do not include the leading and trailing / in the output.  Do not include any other information in the output.
          
          DESCRIPTION:  {{ConvertDescriptionToLowerCase.output}} 
    • Scripts
      • Set CMDB Attribute for matching CIs
        • Script is defined on demand when adding the script tool to the AI Agent 
        • See Script Skills to add section below

 

  • AI Agent
    • AI Agent Name: Update CMDB Attribute based on naming convention
    • AI Agent Description
      Update a CMDB Attribute for all CIs based on a naming convention
    • AI Agent Role
      You are a CMDB administrator and you are tasked with updating a CMDB attribute for all CIs based on their naming convention.  You need to create a regular expression based on the users description and then use that regular expression to find the CIs to be updated.
    • List of Steps
      1. Ask the user to determine which CMDB class they want to populate.  Inform the user they can select all classes or choose a specific class.
      
      2. If the user does not specify all classes, find the exact CMDB class matching requested class and confirm with the user that the exact CMDB class found is correct before proceeding to the next step.  Do not proceed to the next action without user confirmation.
      
      3. Ask the user to determine which CMDB attribute they want to populate.
      
      4. Find the exact CMDB attribute matching the requested attribute and confirm with the user that the exact CMDB attribute found is correct before proceeding to the next step.  Do not proceed to the next action without user confirmation.
      
      5. Ask the user for a description of the naming convention to find relevant CIs to be updated.
      
      6. Generate a regular expression based on the user's description to find relevant CIs.
      
      7. Ask the user for the value to set the CMDB attribute to for relevant CIs.
      
      8. Ask the user for a final confirmation that the CMDB class, CMDB attribute, regular expression, and value are all correct before proceeding to the next step.  If the user requests a change, repeat this step.  Do not proceed to the next action without user confirmation.
      
      9.  After the CMDB class, CMDB attribute, regular expression, and value have all been confirmed by the user, proceed with updating the CMDB attribute with the specified value.  Only CIs with a name that matches the regular expression will be updated
    • User access: Users with specific roles (sn_cmdb_editor)
    • Now Assist Skills to add
      • Skill: Find CMDB Class
        • Name:  Find exact CMDB Class
        • Tool Description
          Find exact CMDB class based on user input class
        • Execution Mode: Autonomous
        • Display output: No
        • Processing Message
          Finding CMDB Class {{cmdbclass}}
      • Skill: Find CMDB Attribute
        • Find exact CMDB Attribute 
        • Tool Description
          Find exact CMDB attribute based on user input attribute
        • Execution Mode: Autonomous
        • Display output: No
        • Processing Message
          Finding CMDB Attribute {{cmdbattribute}}
      • Skill: Generate JavaScript Regular Expression
        • Name:  Generate JavaScript Regular Expression
        • Tool Description
          Generate a Javascript Regular Expression based on a user's description to help find CIs with a matching name in the CMDB
        • Execution Mode: Autonomous
        • Display output: No
        • Processing Message
          Creating a regular expression to find matching CIs
    • Script Skills to add
      • A new script
        • Name:  Set CMDB Attribute for matching CIs
        • Inputs
          • Input Name: regex
            • Description: Regular Expression
          • Input Name: value
            • Description: Value
          • Input Name: attribute
            • Description: CMDB Attribute
          • Input Name: class
            • Description: CMDB Class
        • Script
          // Function to get Field Name from Attribute Label
          function getAttributeFieldName(attribute){
              var dictGr = new GlideRecord('sys_dictionary');
              dictGr.addQuery('name', 'cmdb_ci');
              dictGr.addQuery('column_label', attribute);
              dictGr.query();
              if (dictGr.next()) 
                  return dictGr.element.toString();
          }
          
          // Function to get Table Name from Class Label
          function getClassTableName(classLabel){
              if( classLabel.toLowerCase() == 'all' || classLabel.toLowerCase() == 'all classes')
                  return 'cmdb_ci'
              var tableGr = new GlideRecord('sys_db_object');
              if( tableGr.get('label', classLabel) )
                  return tableGr.name.toString();
          }
          
          // Get names from labels
          var tableName = getClassTableName(inputs['class']);
          var fieldName = getAttributeFieldName(inputs['attribute']);
          
          if ( !gs.nil(tableName) && !gs.nil(fieldName) ) {
          
              // Use GlideRecordSecure to enfore ACLs when updating
              var ciGr = new GlideRecordSecure(tableName);
                  
              // Exclude retired CIs (Operational Status, Install Status, or Lifecycle Stage)
              ciGr.addQuery('operational_status', '!=', 6);
              ciGr.addQuery('install_status', '!=', 7);
              ciGr.addNullQuery('life_cycle_stage_status').addOrCondition('life_cycle_stage_status', '!=', 'Retired');
          
              ciGr.query();
              while(ciGr.next()){
                  // Set display value if name matches regular expression
                  if( ciGr.name.toString().match(inputs['regex'])){
                      ciGr.setDisplayValue(fieldName, inputs['value']);
                      ciGr.update();
                  }
              }
          }
          
        • Tool Description
          Find CIs matching the specific naming convention and update the CMDB  attribute to the specified value.
        • Execution Mode: Autonomous
        • Display output: No
        • Processing Message
          Setting {{attribute}} to {{value}} for matching CIs

 

Additional Resources for Now Assist

 

Licensing

 

ServiceNow University

 

Other Resources

 

​ServiceNow Community - YouTube