Interested in a ServiceNow event built for developers? Registration for now[dev]26 is officially open!

Kenny Caldwell
ServiceNow Employee

Out of the box, Service Exchange for Providers creates Authorized users for a Consumer connection once, when the connection is established. Users added to the Consumer's company after that point, for example through a bulk load, do not get an Authorized user record.

 

Solution

Create a scheduled script that finds active users in a Consumer's company who have no Authorized user record for that connection and create authorized user records for these users, with Approval status set to Approved. Approval status is set to Approved instead of checking the Consumer Connection setting Auto approve authorized users to allow the script to be in the global scope because the Service Exchange method getAutoApproveAuthUsers is only available inside the Service Exchange for Providers scope.  This script could also be used in an onComplete import transform script.

 

How the Out of the Box behavior works

Authorized users for a connection are created by a business rule named Service Exchange bootstrap on Registration [sn_sb_pro_registration]. It runs after the registration task state changes to Work in Progress.

 

When the business rule runs, it does the following:

  1. Finds the Consumer connection [sn_sb_pro_consumer_connection] that matches the Registration's company and URL.
  2. Creates the Settings[sn_sb_pro_service_bridge_settings] record for the connection.
  3. Calls the addUsersToAuthorizedUsers method of the PSBAuthorizedUserUtil script include.

The addUsersToAuthorizedUsers method queries the User [sys_user] table for active users in the connection's company who have an email address and creates an Authorized user [sn_sb_pro_authorized_user] record for these users. An Authorized user record is not created for the Service Exchange integration user. The Auto approve authorized users connection setting defaults to true so these authorized users are created with Approval status of Approved.

Because this business rule only runs when a Registration changes to Work in Progress. A user created after this point never creates an Authorized user.

 

Note: This can't be solved by just calling addUsersToAuthorizedUsers again. The method has no check for existing records, so a second call would create a duplicate authorized users.

 

What the scheduled job does

On each run, the job does the following for every Consumer connection that has a company:

  1. Queries Authorized users table where the Consumer connection is the current connection and registers the Provider user value. The Provider user field is a reference to the User table.
  2. Next it queries active users in the connection's company that have an email address.
  3. Then it compares the Authorized users and Users. For each user without an Authorized user record, it creates an Authorized user record with Approval status set to Approved.

 

Create the Scheduled Script

  1. Navigate to All > System Definition > Scheduled Jobs
  2. Click New
  3. Select Automatically run a script of your choosing
  4. Set the following values:
    1. Name: Reconcile Authorized Users
    2. Run: On Demand
    3. Run this script:
(function reconcileAuthorizedUsers() {
    var LOG = 'SE_AUTHUSER_RECON: ';
    var AUTHORIZED_USER_TABLE = 'sn_sb_pro_authorized_user';
    var totalCreated = 0;

    var connectionGR = new GlideRecord('sn_sb_pro_consumer_connection');
    connectionGR.addNotNullQuery('company');
    connectionGR.query();

    while (connectionGR.next()) {
        var connectionId = connectionGR.getUniqueValue();
        var created = 0;

        var existing = {};
        var authorizedUserGR = new GlideRecord(AUTHORIZED_USER_TABLE);
        authorizedUserGR.addQuery('consumer_connection', connectionId);
        authorizedUserGR.query();
        while (authorizedUserGR.next()) {
            // Register the authorized user records provider user to
            // compare against users
            existing[authorizedUserGR.getValue('provider_user')] = true;
        }

        var userGR = new GlideRecord('sys_user');
        userGR.addQuery('company', connectionGR.getValue('company'));
        userGR.addActiveQuery();
        userGR.addNotNullQuery('email');
        userGR.query();

        while (userGR.next()) {
            // If authorized user exists for the user don't create an authorized user
            if (existing[userGR.getUniqueValue()]) {
                continue;
            }
            // If user is the SE Service Account don't create an authorized user
            if (new sn_transport.Transport().isTransporterUser(userGR.getValue('user_name'))) {
                continue;
            }

            var newAuthorizedUserGR = new GlideRecord(AUTHORIZED_USER_TABLE);
            newAuthorizedUserGR.initialize();
            newAuthorizedUserGR.setValue('consumer_connection', connectionId);
            newAuthorizedUserGR.setValue('provider_user', userGR.getUniqueValue());
            newAuthorizedUserGR.setValue('first_name', userGR.getValue('first_name'));
            newAuthorizedUserGR.setValue('last_name', userGR.getValue('last_name'));
            newAuthorizedUserGR.setValue('email', userGR.getValue('email'));
            newAuthorizedUserGR.setValue('active', false);
            newAuthorizedUserGR.setValue('approval_status', 'approved');
            
            if (newAuthorizedUserGR.insert()) {
                created++;
            } else {
                gs.error(LOG + 'insert failed for user ' + userGR.getValue('user_name') + ' on connection ' + connectionGR.getValue('number'));
            }
        }

        if (created > 0) {
            gs.info(LOG + 'created ' + created + ' authorized user(s) for connection ' + connectionGR.getValue('number'));
        }
        totalCreated += created;
    }

    gs.info(LOG + 'run complete, total created: ' + totalCreated);
})();

 

Why each part of the logic?

  • addNotNullQuery('company') on the connection: The user query is built from the connection's company. A connection without a company has nothing to match.
  • existing[...] lookup: This is the duplicate check the Out of the Box method does not have. It compares Provider user [provider_user] values, so the job is safe to run.
  • addActiveQuery() and addNotNullQuery('email') on User: These match the Out of the Box method. It only considers active users who have an email address.
  • isTransporterUser(...) check: This is an Out of the Box method. This method is available to All application scopes. This check skips the transport integration user so that it never gets an Authorized user record.
  • Active set to false: This matches the records the Out of the Box method creates. Consumers control whether an authorized user is active while Providers control the approval of authorized users.
  • Approval status: Set Approval status to Approved. getAutoApproveAuthUsers method is only available inside the Service Exchange for Providers scope. A query could be written to get the value.
  • SE_AUTHUSER_RECON log prefix: You can search the system log for this prefix to see what each run created.

 

Notes

  • Sample scripts for demonstration purposes. Please test thoroughly in your sub-production environment and modify as needed for your specific use case. Use at your own discretion.
  • This code is not an official ServiceNow product and is provided as-is without warranty or support. Test thoroughly in a non-production environment before use on any production instance.
Version history
Last update:
19m ago
Updated by:
Contributors