The CreatorCon Call for Content is officially open! Get started here.

Dynamically retrieving the approver user in ATF and impersonating them

rafaelmach7x
Tera Contributor

Hello community — I'm facing an issue with ATF impersonation and hope someone can point out what I might be missing.

Environment & context

  • I have an ATF test where:

    • Step 36 is a Record Query that successfully returns a sysapproval_approver record (I can see the approval record sys_id in Step 36 output).

    • Step 37 is a Run Server-side Script that should read the Step 36 output, query sysapproval_approver, extract the approver (a reference to sys_user), and return a record output in the format the Impersonate step expects:

       
      outputs.record = { document_id: "<sys_user_sys_id>", table: "sys_user" }

What I expect to happen

  • Step 37 prepares outputs.record with the approver sys_user id.

  • Step 38 (Impersonate) is configured to use {{Step 37: Run Server Side Script.Record}} (dot-walk → Step 37 → Record → document_id) and should impersonate that user so the remaining ATF steps run in that user's context.

What actually happens

  • Step 37 executes successfully (no script error).

  • However, Step 38 fails with:
    User to impersonate not specified

  • In Step 38 I selected the user via the dot-walk UI (Step 37 → Record → document_id). The Impersonate field shows that reference, but the step fails at runtime.

What I tried already

  • I made the Step 37 script robust so it detects either steps[36].first_record.document_id or steps[36].record_id (I log both).

  • I tried returning outputs.record as shown above.

  • I also tried returning an alternative name (outputs.user) and other shapes, but Impersonate only accepts a dot-walkable Record → document_id.

    Here´s the code - Step 37 script (full) that I used

(function(outputs, steps, stepResult) {
    try {
        gs.info("ATF Step 37: starting - reading Step 36 outputs...");

        var approvalRecordSysId = null;
        if (steps[36] && steps[36].first_record && steps[36].first_record.document_id) {
            approvalRecordSysId = steps[36].first_record.document_id;
            gs.info("ATF Step 37 debug: found steps[36].first_record.document_id = " + approvalRecordSysId);
        } else if (steps[36] && steps[36].record_id) {
            approvalRecordSysId = steps[36].record_id;
            gs.info("ATF Step 37 debug: found steps[36].record_id = " + approvalRecordSysId);
        } else if (steps[36] && steps[36].first_record) {
            approvalRecordSysId = steps[36].first_record;
            gs.info("ATF Step 37 debug: found steps[36].first_record (fallback) = " + approvalRecordSysId);
        }

        if (!approvalRecordSysId) {
            stepResult.setFailed("Step 37 failed: could not read approval record id from Step 36. Inspect Step 36 outputs.");
            return;
        }

        var grApproval = new GlideRecord("sysapproval_approver");
        if (!grApproval.get(approvalRecordSysId)) {
            stepResult.setFailed("Step 37 failed: sysapproval_approver record not found for id: " + approvalRecordSysId);
            return;
        }

        var approverSysId = grApproval.getValue("approver");
        if (!approverSysId) {
            stepResult.setFailed("Step 37 failed: 'approver' field is empty on sysapproval_approver record: " + approvalRecordSysId);
            return;
        }

        gs.info("ATF Step 37 debug: approver sys_id = " + approverSysId);

        outputs.record = {
            document_id: approverSysId,
            table: "sys_user"
        };

        outputs.record_id = approverSysId;
        outputs.record_table = "sys_user";

        stepResult.setOutputMessage("Step 37 succeeded: prepared outputs.record for sys_user " + approverSysId);
    } catch (ex) {
        stepResult.setFailed("Step 37 exception: " + ex.message);
    }
})(outputs, steps, stepResult);


Important: If I manually enter the approver’s username in the Impersonate step, the test runs successfully until the end. This confirms that Step 36 is correct and is returning the sys_id of the approval record.

Here´s some prints that might me usefull.

rafaelmach7x_0-1758296930140.png

 

rafaelmach7x_1-1758297058581.png

 

rafaelmach7x_2-1758297296143.png

Step 38 failed: User to impersonate not specified

 

  • Is the outputs.record = { document_id: "<sys_id>", table: "sys_user" } shape definitely the correct format for the ATF Impersonate step to consume?

     

  • Could there be a timing/serialization issue where the Impersonate step evaluates the dot-walk reference before the Run Script outputs are fully available? If so, any workaround?


  • Has anyone seen the Impersonate step return “User to impersonate not specified” even though the preceding Run Script produced outputs.record? If so, what fixed it?


    Thank you in advance for any help or pointers. Any sample working snippet or a note about version-specific behavior would be very appreciated.

    Kind regards,
    Rafael





11 REPLIES 11

Chidanand_VK
Mega Guru

Hi @rafaelmach7x ,

You can use the below simple script to do the same action.

 
(function(outputs, steps, stepResult) {
    var prevStep = "XXXXXXXXX"; //Sys Id of Step 36
    var approval = new GlideRecord("sysapproval_approver");
    approval.get(steps(prevStep).first_record);

    var approverSysId = approval.getValue("approver");

    var approver = new GlideRecord('sys_user');
    approver.get(approverSysId);

    if (!approver.getUniqueValue()) {
        stepResult.setOutputMessage("Unable to find the User to Impersonate");
        stepResult.setFailed();
        return false;
    } else {
        stepResult.setOutputMessage("User found for Impersonation is - " + approver.getDisplayValue("name"));
        stepResult.setSuccess();
        outputs.record_id = approverSysId;
        return true;
    }

})(outputs, steps, stepResult);

 

If my answer helped, please mark it as helpful or accept as the solution.

Regards,

Chidanand

 

Hi Chidanand_VK!

Thanks for your help!

I don’t want to manually enter the sys_id, because that’s not sustainable in tests that may change.

Ideally, the script or ATF itself should be able to dynamically determine the sys_id of step 36, or the step should be configured to pass this information through an output.

In this line of code, I understood that you want me to manually enter the sys_id of step 36.
 var prevStep = "XXXXXXXXX"; //Sys Id of Step 36

That’s why I tried this approach below in my script, but it didn’t work...

steps[36] && steps[36].first_record && steps[36].first_record.document_id


Is there a way to dynamically determine the sys_id of step 36?

Kind regards!



Hi @rafaelmach7x ,

"I don’t want to manually enter the sys_id, because that’s not sustainable in tests that may change."

 

When you mention NOT sustainable, What that mean? any below statement or anything else?

->If you copy the Test and run that New Test, it will not work?

->The execution will NOT successfully works every time?

Hi @Chidanand_VK 

I really appreciate your effort in helping me.

Every time I run the test, the sys_id is different. I can’t set the sys_id manually, you know?

That’s why I need a way to retrieve the sys_id dynamically from the step 36.