_
_
Back to Blog
ServiceNow
No items found.

SAM Normalization Failing and How to Fix It

Troubleshooting SAM Normalization Failures: Quick Fixes and Best Practices
3
min read
|
by
Ben Savage
September 3, 2024

I suspect you ended up reading this blog post as your SAMP normalization is failing and you don’t know how to fix it. Luckily, we at Rapdev have the answer.

SAM Normalization in your instance is probably, 99% of the time, failing as you activated the “Automatically create software models for all ‘licensable’ products” property in the SAMP properties. 

This property:

Located within the SAM Properties:

The key is that this property SHOULD be active. You need software models associated with your software discovery models. It’s what links installs to the software models. And creating the software models manually is a brutal task. 

In SAMP it is vital to have valid mapping to publishers and manufacturers from your software discovery models and software models. These relationships often get misaligned and that is the root cause of the failing of normalization.  AKA, this:

In case you haven’t found that table yet, it’s called “samp_reconciliation_result”. Add the “Progress summary” column to the list to view the error(s). 

The fix

So, normalization is failing and it’s probably due to the publishers and manufacturers tied into your SAM data. Luckily ServiceNow has a knowledge article that contains two fix scripts. Here is a link to the article.

As KB articles can “disappear”, here is a screen shot of the article:

Let’s break down a component of the scripts that the article refers to. 

Within “sysauto_script_9785060487a15110293c31173cbb3539 (1).xml” the first function is fixing the publishers associated to the installs. This function queries all of your installs and updates the associated normalized publisher to the manufacturer associated to the normalized products publisher’s. Hopefully I didn’t lose you there 😊:


var installGa = new GlideAggregate('cmdb_sam_sw_install');
    if (installGa.isValid()) {
        installGa.addNotNullQuery('discovery_model');
        installGa.addNotNullQuery('norm_product');
        installGa.addNotNullQuery('norm_product.publisher.manufacturer');
        installGa.groupBy('norm_product.publisher.manufacturer');
        installGa.query();
        while (installGa.next()) {
            var manufacturer = installGa.getValue('norm_product.publisher.manufacturer');
            var installGr = new GlideRecord('cmdb_sam_sw_install');
            installGr.addQuery('norm_product.publisher.manufacturer', manufacturer);
            installGr.setValue('norm_publisher', manufacturer);
            installGr.setWorkflow(false);
            installGr.updateMultiple();
        }
        gs.log('**********Fix script - Fixed Software Installs**********', "CoreCompanyFixScript");
       }

The remainder of that script and the subsequent script basically rinse and repeat alignment activities. Here is a full copy of both scripts.


<?xml version="1.0" encoding="UTF-8"?>
<unload unload_date="2022-08-21 15:13:55">
  <sysauto_script action="INSERT_OR_UPDATE">
    <active>true</active>
    <business_calendar display_value=""/>
    <condition/>
    <conditional>false</conditional>
    <entered_time>1970-01-01 00:00:00</entered_time>
    <name>Fix Core Company References</name>
    <offset/>
    <offset_type>0</offset_type>
    <run_as display_value="System Administrator">6816f79cc0a8016401c5a33be04be441</run_as>
    <run_dayofmonth>1</run_dayofmonth>
    <run_dayofweek>1</run_dayofweek>
    <run_start>2022-08-21 15:11:54</run_start>
    <run_time>1970-01-01 08:00:00</run_time>
    <run_type>on_demand</run_type>
    <script><![CDATA[
      function _fixImpactedInstallRecords() {
        var installGa = new GlideAggregate('cmdb_sam_sw_install');
        if (installGa.isValid()) {
          installGa.addNotNullQuery('discovery_model');
          installGa.addNotNullQuery('norm_product');
          installGa.addNotNullQuery('norm_product.publisher.manufacturer');
          installGa.groupBy('norm_product.publisher.manufacturer');
          installGa.query();
          while (installGa.next()) {
            var manufacturer = installGa.getValue('norm_product.publisher.manufacturer');
            var installGr = new GlideRecord('cmdb_sam_sw_install');
            installGr.addQuery('norm_product.publisher.manufacturer', manufacturer);
            installGr.setValue('norm_publisher', manufacturer);
            installGr.setWorkflow(false);
            installGr.updateMultiple();
          }
          gs.log('**********Fix script - Fixed Software Installs**********', "CoreCompanyFixScript");
        }
      }
      function _fixImpactedDiscoveryModels() {
        var dmGa = new GlideAggregate('cmdb_sam_sw_discovery_model');
        if (dmGa.isValid()) {
          dmGa.addNotNullQuery('norm_product');
          dmGa.addNotNullQuery('norm_product.publisher.manufacturer');
          dmGa.groupBy('norm_product.publisher.manufacturer');
          dmGa.query();
          while (dmGa.next()) {
            var norm_publisher = dmGa.getValue('norm_product.publisher.manufacturer');
            var dmGr = new GlideRecord('cmdb_sam_sw_discovery_model');
            dmGr.addQuery('norm_product.publisher.manufacturer', norm_publisher);
            dmGr.setValue('norm_publisher', norm_publisher);
            dmGr.setWorkflow(false);
            dmGr.updateMultiple();
          }
          gs.log('**********Fix script - Fixed Discovery Models**********', "CoreCompanyFixScript");
        }
      }
      function _fixImpactedSoftwareModels() {
        var smGa = new GlideAggregate('cmdb_software_product_model');
        if (smGa.isValid()) {
          smGa.addNotNullQuery('product');
          smGa.addNotNullQuery('product.publisher.manufacturer');
          smGa.groupBy('product.publisher.manufacturer');
          smGa.query();
          while (smGa.next()) {
            var norm_publisher = smGa.getValue('product.publisher.manufacturer');
            var smGr = new GlideRecord('cmdb_software_product_model');
            smGr.addQuery('product.publisher.manufacturer', norm_publisher);
            smGr.query();
            while (smGr.next()) {
              smGr.setValue('manufacturer', norm_publisher);
              global.ModelUtils.calculateDisplayName(smGr);
              smGr.setWorkflow(false);
              smGr.update();
            }
          }
          gs.log('**********Fix script - Fixed Software Models**********', "CoreCompanyFixScript");
        }
      }
      function _fixImpactedSubscriptions() {
        var subscriptionGa = new GlideAggregate('samp_sw_subscription');
        if (subscriptionGa.isValid()) {
          subscriptionGa.addNotNullQuery('software_model');
          subscriptionGa.addNotNullQuery('software_model.manufacturer');
          subscriptionGa.groupBy('software_model.manufacturer');
          subscriptionGa.query();
          while (subscriptionGa.next()) {
            var publisher = subscriptionGa.getValue('software_model.manufacturer');
            var subscriptionGr = new GlideRecord('samp_sw_subscription');
            subscriptionGr.addQuery('software_model.manufacturer', publisher);
            subscriptionGr.query();
            while (subscriptionGr.next()) {
              subscriptionGr.setValue('publisher', publisher);
              subscriptionGr.setValue('display_name', subscriptionGr.getDisplayValue('software_model'));
              subscriptionGr.setWorkflow(false);
              subscriptionGr.update();
            }
          }
          gs.log('**********Fix script - Fixed Software Subscriptions**********', "CoreCompanyFixScript");
        }
      }
      function _fixImpactedSuggestions() {
        var suggestion = new GlideRecord('samp_normalization_suggestion');
        if (suggestion.isValid() && suggestion.isValidField('suggested_publisher') && suggestion.isValidField('norm_publisher')) {
          var suggestionGa = new GlideAggregate('samp_normalization_suggestion');
          suggestionGa.addNotNullQuery('suggested_product');
          suggestionGa.addNotNullQuery('suggested_product.publisher.manufacturer');
          suggestionGa.groupBy('suggested_product.publisher.manufacturer');
          suggestionGa.query();
          while (suggestionGa.next()) {
            var suggested_publisher = suggestionGa.getValue('suggested_product.publisher.manufacturer');
            var suggestionGr = new GlideRecord('samp_normalization_suggestion');
            suggestionGr.addQuery('suggested_product.publisher.manufacturer', suggested_publisher);
            suggestionGr.setValue('suggested_publisher', suggested_publisher);
            suggestionGr.setWorkflow(false);
            suggestionGr.updateMultiple();
          }
          suggestionGa = new GlideAggregate('samp_normalization_suggestion');
          suggestionGa.addNotNullQuery('norm_product');
          suggestionGa.addNotNullQuery('norm_product.publisher.manufacturer');
          suggestionGa.groupBy('norm_product.publisher.manufacturer');
          suggestionGa.query();
          while (suggestionGa.next()) {
            var publisher = suggestionGa.getValue('norm_product.publisher.manufacturer');
            var suggestionGr = new GlideRecord('samp_normalization_suggestion');
            suggestionGr.addQuery('norm_product.publisher.manufacturer', publisher);
            suggestionGr.setValue('norm_publisher', publisher);
            suggestionGr.setWorkflow(false);
            suggestionGr.updateMultiple();
          }
          gs.log('**********Fix script - Fixed Normalization Suggestions**********', "CoreCompanyFixScript");
        }
      }
      function _fixImpactedReclamation() {
        var recCandidateGa = new GlideAggregate('samp_sw_reclamation_candidate');
        if (recCandidateGa.isValid()) {
          recCandidateGa.addNotNullQuery('product');
          recCandidateGa.addNotNullQuery('product.publisher.manufacturer');
          recCandidateGa.groupBy('product.publisher.manufacturer');
          recCandidateGa.query();
          while (recCandidateGa.next()) {
            var publisher = recCandidateGa.getValue('product.publisher.manufacturer');
            var recCandidateGr = new GlideRecord('samp_sw_reclamation_candidate');
            recCandidateGr.addQuery('product.publisher.manufacturer', publisher);
            recCandidateGr.query();
            while (recCandidateGr.next()) {
              recCandidateGr.setValue('publisher', publisher);
              if (recCandidateGr.getValue('applies_to') === 'installed_software') {
                if (recCandidateGr.getValue('justification') === 'optimize') {
                  recCandidateGr.name = gs.getMessage('Optimize {0}', [recCandidateGr.software_install.display_name]);
                } else {
                  recCandidateGr.name = gs.getMessage('Remove {0}', [recCandidateGr.software_install.display_name]);
                }
              } else if (recCandidateGr.getValue('applies_to') === 'subscription_software' && !gs.nil(recCandidateGr.optimized_subscription)) {
                recCandidateGr.name = gs.getMessage('Downgrade: {0} to {1}', [recCandidateGr.user_subscription.display_name, recCandidateGr.optimized_subscription.entitlement_definition.software_title]);
              } else if (recCandidateGr.getValue('applies_to') === 'subscription_software') {
                recCandidateGr.name = gs.getMessage('Remove subscription: {0}', [recCandidateGr.user_subscription.display_name]);
              }
              recCandidateGr.setWorkflow(false);
              recCandidateGr.update();
            }
          }
          gs.log('**********Fix script - Fixed Reclamation Candidates**********', "CoreCompanyFixScript");
        }
      }
      _fixImpactedDiscoveryModels();
      _fixImpactedInstallRecords();
      _fixImpactedSoftwareModels();
      _fixImpactedSubscriptions();
      _fixImpactedSuggestions();
      _fixImpactedReclamation();
    ]]></script>
    <sys_class_name>sysauto_script</sys_class_name>
    <sys_created_by>admin</sys_created_by>
    <sys_created_on>2022-08-21 15:13:42</sys_created_on>
    <sys_id>9785060487a15110293c31173cbb3539</sys_id>
    <sys_mod_count>0</sys_mod_count>
    <sys_name>Fix Core Company References</sys_name>
    <sys_package display_value="Global" source="global">global</sys_package>
    <sys_scope display_value="Global">global</sys_scope>
    <sys_updated_by>admin</sys_updated_by>
    <sys_updated_on>2022-08-21 15:13:42</sys_updated_on>
</sysauto_script>
</unload>

<?xml version="1.0" encoding="UTF-8"?>
<unload unload_date="2021-07-08 22:13:28">
  <sysauto_script action="INSERT_OR_UPDATE">
    <active>true</active>
    <business_calendar display_value=""/>
    <condition/>
    <conditional>false</conditional>
    <entered_time>1970-01-01 00:00:00</entered_time>
    <name>Core Company Fix Script</name>
    <offset/>
    <offset_type>0</offset_type>
    <run_as display_value="System Administrator">6816f79cc0a8016401c5a33be04be441</run_as>
    <run_dayofmonth>1</run_dayofmonth>
    <run_dayofweek>1</run_dayofweek>
    <run_start>2021-07-08 22:10:15</run_start>
    <run_time>1970-01-01 08:00:00</run_time>
    <run_type>on_demand</run_type>
    <script><![CDATA[
      gs.log('**********Fix script - Starting script to fix core company references, discovery model records and relevant install records :**********', "CoreCompanyFixScript");
      var fixedPublishers = [];
      var newCreatedCoreCompanys = [];
      var updatedCanonicalToTrueCoreComp = [];
      
      var isDomainDataSeparationEnabled = GlideDomainSupport.isDataSeparationEnabled();
      var isCanonicalPluginActive = GlidePluginManager.getActivePlugin('com.glide.data_services_canonicalization.client');
      
      execute();
      
      function execute() {
        var gr = new GlideRecord('samp_sw_publisher');
        gr.addEncodedQuery('manufacturerISNOTEMPTY');
        gr.query();
      
        while (gr.next()) {
          var existingManfID = gr.manufacturer.sys_id;
          var canonicalManfID = '';
      
          canonicalManfID = SNC.CanonicalName.normalizeCompany(gr.name, false);
      
          if (gs.nil(canonicalManfID)) {
            canonicalManfID = _resolveCoreCompanyByName(gr.getValue('name'));
          }
      
          if (!gs.nil(canonicalManfID) && (existingManfID != canonicalManfID)) {
            _impactedSWModels(existingManfID, canonicalManfID);
            _impactedDMs(existingManfID, canonicalManfID);
            _impactedInstalls(existingManfID, canonicalManfID);
            _impactedSubscriptions(existingManfID, canonicalManfID);
            _impactedSuggestions(existingManfID, canonicalManfID);
            _impactedReclamation(existingManfID, canonicalManfID);
      
            gr.setValue('manufacturer', canonicalManfID);
            gr.setWorkflow(false);
            gr.update();
      
            fixedPublishers.push('\n' + gr.getValue('name') + '(' + gr.getUniqueValue() + ')');
          }
        }
      
        gs.log('**********Fix script - fixed publishers :**********' + fixedPublishers, "CoreCompanyFixScript");
        gs.log('**********Fix script - new created core companys :**********' + newCreatedCoreCompanys, "CoreCompanyFixScript");
        gs.log('**********Fix script - core companys canonical updated to true :**********' + updatedCanonicalToTrueCoreComp, "CoreCompanyFixScript");
      
        gs.log('**********Fix script - fixing any remaining discovery models and installs**********', "CoreCompanyFixScript");
      
        _fixRemainingDiscoveryModels();
        _fixRemainingInstallRecords();
      
        gs.log('**********Fix script - COMPLETED: script to fix core company references, discovery model records and relevant install records :**********', "CoreCompanyFixScript");
      
        var smGr = new GlideRecord('cmdb_software_product_model');
        smGr.addEncodedQuery('product.publisher.manufacturerNSAMEASmanufacturer^product.product_type=licensable');
        smGr.query();
        while (smGr.next()) {
          smGr.setValue('manufacturer', smGr.product.publisher.manufacturer.toString());
          smGr.update();
        }
      }
      
      function _impactedSWModels(oldCompanyId, newCompanyId) {
        var swmodel = new GlideRecord('cmdb_software_product_model');
        if (swmodel.isValid() && swmodel.isValidField('manufacturer')) {
          swmodel.addQuery('manufacturer', oldCompanyId);
          swmodel.query();
          while (swmodel.next()) {
            swmodel.setValue('manufacturer', newCompanyId);
            global.ModelUtils.calculateDisplayName(swmodel);
            swmodel.setWorkflow(false);
            swmodel.update();
            _updateEntitlement(swmodel);
          }
        }
      }
      
      function _updateEntitlement(swmodelGr) {
        var enGr = new GlideRecord('alm_license');
        if (enGr.isValid() && enGr.isValidField('software_model')) {
          enGr.addQuery('software_model', swmodelGr.sys_id);
          enGr.query();
          while (enGr.next()) {
            new AssetUtils().calculateDisplayName(enGr);
            enGr.setWorkflow(false);
            enGr.update();
          }
        }
      }
      
      function _impactedDMs(oldCompanyId, newCompanyId) {
        var dm = new GlideRecord('cmdb_sam_sw_discovery_model');
        if (dm.isValid() && dm.isValidField('norm_publisher')) {
          dm.addQuery('norm_publisher', oldCompanyId);
          dm.setValue('norm_publisher', newCompanyId);
          dm.setWorkflow(false);
          dm.updateMultiple();
        }
      }
      
      function _impactedInstalls(oldCompanyId, newCompanyId) {
        var install = new GlideRecord('cmdb_sam_sw_install');
        if (install.isValid() && install.isValidField('norm_publisher')) {
          install.addQuery('norm_publisher', oldCompanyId);
          install.setValue('norm_publisher', newCompanyId);
          install.setWorkflow(false);
          install.updateMultiple();
        }
      }
      
      function _impactedSubscriptions(oldCompanyId, newCompanyId) {
        var sub = new GlideRecord('samp_sw_subscription');
        if (sub.isValid() && sub.isValidField('publisher')) {
          sub.addQuery('publisher', oldCompanyId);
          sub.query();
          while (sub.next()) {
            sub.setValue('publisher', newCompanyId);
            sub.setValue('display_name', sub.software_model.display_name);
            sub.setWorkflow(false);
            sub.update();
          }
        }
      }
      
      function _impactedSuggestions(oldCompanyId, newCompanyId) {
        var suggestion = new GlideRecord('samp_normalization_suggestion');
        if (suggestion.isValid() && suggestion.isValidField('suggested_publisher') && suggestion.isValidField('norm_publisher')) {
          var qc = suggestion.addQuery('suggested_publisher', oldCompanyId);
          qc.addOrCondition('norm_publisher', oldCompanyId);
          suggestion.query();
          while (suggestion.next()) {
            if (suggestion.getValue('suggested_publisher') == oldCompanyId)
              suggestion.setValue('suggested_publisher', newCompanyId);
      
            if (suggestion.getValue('norm_publisher') == oldCompanyId)
              suggestion.setValue('norm_publisher', newCompanyId);
      
            suggestion.setWorkflow(false);
            suggestion.update();
          }
        }
      }
      
      function _impactedReclamation(oldCompanyId, newCompanyId) {
        var rec = new GlideRecord('samp_sw_reclamation_candidate');
        if (rec.isValid() && rec.isValidField('publisher')) {
          rec.addQuery('publisher', oldCompanyId);
          rec.query();
          while (rec.next()) {
            rec.setValue('publisher', newCompanyId);
            if (rec.getValue('applies_to') === 'installed_software') {
              if (rec.getValue('justification') === 'optimize') {
                rec.name = gs.getMessage('Optimize {0}', [rec.software_install.display_name]);
              } else {
                rec.name = gs.getMessage('Remove {0}', [rec.software_install.display_name]);
              }
            } else if (rec.getValue('applies_to') === 'subscription_software' && !gs.nil(rec.optimized_subscription)) {
              rec.name = gs.getMessage('Downgrade: {0} to {1}', [rec.user_subscription.display_name, rec.optimized_subscription.entitlement_definition.software_title]);
            } else if (rec.getValue('applies_to') === 'subscription_software') {
              rec.name = gs.getMessage('Remove subscription: {0}', [rec.user_subscription.display_name]);
            }
            rec.setWorkflow(false);
            rec.update();
          }
        }
      }
      
      function _resolveCoreCompanyByName(companyNameParam) {
        var companySysId = '';
        var companyName = companyNameParam;
      
        var domainID = '';
        if (isDomainDataSeparationEnabled) {
          domainID = 'global';
        }
        var cdsmap = _getCDSMappingEntry(companyName);
        if (cdsmap.next()) {
          companyName = cdsmap.canonical_name.name;
        }
      
        companySysId = _getCoreCompanyId(companyName, domainID);
      
        if (!gs.nil(companySysId)) {
          var coreCompanyGr = new GlideRecord('core_company');
          coreCompanyGr.get(companySysId);
          if (coreCompanyGr.getValue('canonical') == false){
            coreCompanyGr.setValue('canonical', true);
            coreCompanyGr.setWorkflow(false);
            coreCompanyGr.update();
      
            updatedCanonicalToTrueCoreComp.push('\n' + coreCompanyGr.getValue('name') + '(' + companySysId + ')');
          }
        } else {
          companySysId = new global.AssetCoreCompanyUtil().createCoreCompany(companyName, domainID);
      
          newCreatedCoreCompanys.push('\n' + companyName + '(' + companySysId + ')');
        }
      
        return companySysId;
      }
      
      function _getCoreCompanyId(companyName, domainID) {
        var coreCompany = new GlideRecord('core_company');
        if (!gs.nil(domainID)) {
          if (!coreCompany.isValidField('sys_domain')) {
            gs.error('No such field sys_domain in core_company');
          }
          coreCompany.addQuery('sys_domain', domainID);
        }
      
        if (isCanonicalPluginActive) {
          var companyNameHash = SNC.CanonicalName.getHash(companyName);
          coreCompany.addQuery('hash', companyNameHash);
          coreCompany.orderByDesc('canonical');
        } else {
          coreCompany.addQuery('name', companyName);
        }
      
        coreCompany.setLimit(1);
        coreCompany.query();
        if (coreCompany.next()) {
          return coreCompany.getUniqueValue();
        }
        return null;
      }
      
      function _getCDSMappingEntry(manufacturerName) {
        var manufacturerHash = SNC.CanonicalName.getHash(manufacturerName);
        var cdsmap = new GlideRecord('cds_client_mapping');
        cdsmap.addQuery('discovered_name_hash', manufacturerHash);
        cdsmap.addQuery('table', 'core_company');
        cdsmap.addQuery('field', 'name');
        cdsmap.query();
        return cdsmap;
      }
      
      function _fixRemainingInstallRecords() {
        var installGa = new GlideAggregate('cmdb_sam_sw_install');
        installGa.addNotNullQuery('discovery_model');
        installGa.addNullQuery('norm_publisher');
        installGa.addNotNullQuery('norm_product');
        installGa.addNotNullQuery('norm_product.publisher.manufacturer');
        installGa.groupBy('norm_product.publisher.manufacturer');
        installGa.query();
        while(installGa.next()) {
          var manufacturer = installGa.getValue('norm_product.publisher.manufacturer');
          var installGr = new GlideRecord('cmdb_sam_sw_install');
          installGr.addNullQuery('norm_publisher');
          installGr.addQuery('norm_product.publisher.manufacturer', manufacturer);
          installGr.setValue('norm_publisher', manufacturer);
          installGr.setWorkflow(false);
          installGr.updateMultiple();
        }
      }
      
      function _fixRemainingDiscoveryModels() {
        var dmGa = new GlideAggregate('cmdb_sam_sw_discovery_model');
        dmGa.addNullQuery('norm_publisher');
        dmGa.addNotNullQuery('norm_product');
        dmGa.addNotNullQuery('norm_product.publisher.manufacturer');
        dmGa.groupBy('norm_product.publisher.manufacturer');
        dmGa.query();
        while (dmGa.next()) {
          var norm_publisher = dmGa.getValue('norm_product.publisher.manufacturer');
          var dmGr = new GlideRecord('cmdb_sam_sw_discovery_model');
          dmGr.addNullQuery('norm_publisher');
          dmGr.addQuery('norm_product.publisher.manufacturer', norm_publisher);
          dmGr.setValue('norm_publisher', norm_publisher);
          dmGr.setWorkflow(false);
          dmGr.updateMultiple();
        }
      }
    ]]></script>
    <sys_class_name>sysauto_script</sys_class_name>
    <sys_created_by>admin</sys_created_by>
    <sys_created_on>2021-07-08 22:11:12</sys_created_on>
    <sys_id>eb0355e065513010fa9b1e02edc28a2d</sys_id>
    <sys_mod_count>0</sys_mod_count>
    <sys_name>Core Company Fix Script</sys_name>
    <sys_package display_value="Global" source="global">global</sys_package>
    <sys_scope display_value="Global">global</sys_scope>
    <sys_updated_by>admin</sys_updated_by>
    <sys_updated_on>2021-07-08 22:11:12</sys_updated_on>
  </sysauto_script>
</unload>

Suggested next steps:

In SANDBOX or a recent clone of the instance.

  1. Log the percentage complete on your last normalization run. 68.87% etc.
  2. Run “sysauto_script_9785060487a15110293c31173cbb3539 (1).xml” as a background script. 
  3. Rerunning reconciliation and note the percentage complete. If it completes, you are good to go. 
  4. If not, log the second run time percentage and run the second script.
  5. Rerun normalization and you should have fixed the problem.

FYI, when pushing to prod you can also run both script as one time scheduled jobs to automate the promotion!

Interested to learn more? Reach out to our team at chat@rapdev.io

Written by
Ben Savage
Boston
UK-born and raised ITAM veteran now residing in Chicago.
you might also like
back to blog