I recently encountered an issue with ServiceNow Flows and Domain Separation. I learned that the domain in which a flow runs depends on the domain of the task that triggers it, regardless of whether the flow is set to run as a system or not. This can be problematic when the flow needs to access information from a different domain than the one it runs.

For example, suppose you have a flow that is triggered by a Task SLA in Top/DomainA, but your users live in another domain. In that case, the flow won’t be able to access the details of the assigned user, which is a problem.

Fortunately, I found a solution in a post by Tom over at ServiceNow. However, the solution had a drawback: it made all flows set to run as system run in the global domain. While this may be fine for some users, it was too broad for our use cases. We wanted some flows to be universal but run on Mid servers for a particular domain, and we didn’t want to specify a specific Mid server. We hoped to run these flows as a system and let ServiceNow find the first available Mid server for us. However, if we used Tom’s script, it would see all our Mid servers instead of just the ones in the desired domain.

To address this issue, we took Tom’s script and modified it to add a system property that allows us to maintain an allowlist of flows that can run in the global domain. If a flow is not on the allowlist, flows will not be allowed to run in the global domain and run in the domain the flow was triggered from. This solution works well for us and allows us to control which flows can run globally and which can’t.

You might be wondering why we needed so much code to do this. The reason is that the sys_flow_context in ServiceNow does not store the flow sys_id until update 1, which is too late to change the domain of the flow execution. Therefore, we had to look for a way to get the flow id on insert (update 0).

I hope this solution helps someone else who has a similar need. If you want to learn more about flow execution across domains in ServiceNow, I recommend checking out Tom’s post on the ServiceNow community website.

Tom’s Orginal Post:

https://www.servicenow.com/community/workflow-automation-articles/flow-execution-across-domains-yes-you-can/ta-p/2447842

Business Rule:

  • Name: sys_flow_context – set domain
  • Table: Flow engine context (sys_flow_context)
  • Domain: Global
  • When: before
  • Order: 10,000
  • Insert: Yes

Script:

(function executeRule(current, previous /*null when async*/ ) {
    try {
        // Declare our variables, system property 'u_flow_domain_sep_exempt' is the AllowList
        var flowId;
        var oAttr = JSON.parse(current.getValue('attributes'));
        var pAttr = JSON.parse(current.getValue('plan'));
        var sysIdArr = [];
        var sysIds = gs.getProperty("u_flow_domain_sep_exempt");
        var onList = false;
        var bRunAsSystem = false;

        // sys_context_flow lacks the flow id on insert, so we need to walk into snapshot to get the sys_id of the flow
        if (pAttr.hasOwnProperty('persistor')) {
            var grFlowSnap = new GlideRecord('sys_hub_snapshot');
            grFlowSnap.addEncodedQuery('sys_id=' + pAttr['persistor']['snapshot']);
            grFlowSnap.query();
            while (grFlowSnap.next()) {
                flowId = grFlowSnap.parent;
            }
        }

        // Let's see if the flow id is on our allow list
        if (sysIds) {
            sysIdArr = sysIds.split(",");
            for (var i = 0; i < sysIdArr.length; i++) {
                if (sysIdArr[i] == flowId) {
                    onList = true;
                }
            }
        }

        // Refer to https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB0998966
        if (gs.getProperty('glide.sys.domain.use_record_domain_for_data') == 'false') {
            if ((oAttr.hasOwnProperty('run_as')) && (oAttr['run_as'] == 'system') && (onList)) {
                bRunAsSystem = true;
            }
            if (bRunAsSystem) {
                gs.info("Flow ID: " + flowId + " Running in Global Domain as on Allow List.");
                current.setValue('sys_domain', 'global');
            } else {
                // Running in Record Domain that triggered the flow.
            }
        }
    } catch (ex) {}
})(current, previous);