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

Hi @rafaelmach7x ,

Ok...so you are talking about the 'sys_id' of the record of the Record query (in your case Approval), right?

If Yes, then you are wrong. You don't need to set the sys_id every time when you run. 

 

FYI: If you set the Sys Id of the Test step manually in the script (which I showed), then it works all the time. Unless you delete the Test step and Create a new one.

 

Just give it a try by setting the Sys Id of the Test step 36 in the script manually and try to run multiple times.

I am sure it works as I was doing the same in my case.

 

 

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

Regards,

Chidanand

 

aruncr0122
Mega Guru

Hi @rafaelmach7x ,

 

In Step 37, you’re outputting:

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


In Step 38 (Impersonate), you’re dot-walking to Record → document_id.

But ATF’s Impersonate step doesn’t understand document_id — it expects a sys_id in the record object. That’s why you’re seeing “Usuário para representação não especificado” (User to impersonate not specified).

Fix: update Step 37 to return sys_id instead:

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


Alternatively, you can set:

outputs.record_id = approverSysId;


and then in the Impersonate step, select Record → sys_id (not document_id).

Hi @aruncr0122.

Thanks for your answer. 

Unfortunately, in Step 38 – Impersonate – ATF only allows me to select Record → document_id.

See the image below ...

rafaelmach7x_0-1759238806598.png

 

This is the last test log

rafaelmach7x_1-1759241902501.png



The most recent version of the script with your suggested adjustments ...

(function(outputs, steps, stepResult) {
    // --- Try to resolve the sys_id from Step 36 in multiple ways ---
    var prevStep = steps['36'];
    var prevStepSysId = null;

    if (!prevStep) {
        stepResult.setOutputMessage("Step 36 outputs not found.");
        stepResult.setFailed();
        return false;
    }

    // Try the most common patterns
    if (prevStep.record_id) {
        prevStepSysId = prevStep.record_id;
    } else if (prevStep.sys_id) {
        prevStepSysId = prevStep.sys_id;
    } else if (prevStep.record && prevStep.record.sys_id) {
        prevStepSysId = prevStep.record.sys_id;
    } else if (prevStep.record && prevStep.record.document_id) {
        prevStepSysId = prevStep.record.document_id;
    }

    // If still not found, fail gracefully
    if (!prevStepSysId) {
        stepResult.setOutputMessage("Unable to determine sys_id from Step 36 outputs.");
        stepResult.setFailed();
        return false;
    }

    // --- Now query the approval record from sysapproval_approver ---
    var approval = new GlideRecord("sysapproval_approver");
    if (!approval.get(prevStepSysId)) {
        stepResult.setOutputMessage("Approval record not found using sys_id from Step 36.");
        stepResult.setFailed();
        return false;
    }

    // Get the approver sys_id
    var approverSysId = approval.getValue("approver");

    // Lookup the approver in sys_user
    var approver = new GlideRecord("sys_user");
    if (!approver.get(approverSysId)) {
        stepResult.setOutputMessage("User not found for impersonation.");
        stepResult.setFailed();
        return false;
    }

    // Success case
    stepResult.setOutputMessage("User found for impersonation: " + approver.getDisplayValue("name"));
    stepResult.setSuccess();

    // Output for next step (Impersonate)
    outputs.record = {
        sys_id: approverSysId,
        table: "sys_user"
    };

    // Alternative output (in case Impersonate step expects record_id)
    outputs.record_id = approverSysId;

    return true;
})(outputs, steps, stepResult);



 



Is there a way to make it work?

rafaelmach7x
Tera Contributor

@aruncr0122 and @Chidanand_VK 

I also tried this script but it didn't work.

(function(outputs, steps, stepResult) {
    // Try to retrieve Step 36 in multiple ways
    var prevStep = steps['36'] || steps['Step 36'] || steps['step36'] || steps['step_36'];

    if (!prevStep) {
        stepResult.setOutputMessage("❌ Could not find Step 36 in steps object.");
        stepResult.setFailed();
        return false;
    }

    // Try to extract the sys_id from Step 36 outputs
    var prevStepSysId = prevStep.record_id 
        || prevStep.sys_id 
        || (prevStep.record && prevStep.record.sys_id) 
        || (prevStep.record && prevStep.record.document_id);

    if (!prevStepSysId) {
        stepResult.setOutputMessage("❌ Could not retrieve sys_id from Step 36 outputs.");
        stepResult.setFailed();
        return false;
    }

    // Query the sysapproval_approver record using the sys_id
    var approval = new GlideRecord("sysapproval_approver");
    if (!approval.get(prevStepSysId)) {
        stepResult.setOutputMessage("❌ Could not find approval record from Step 36 sys_id.");
        stepResult.setFailed();
        return false;
    }

    // Get the approver sys_id
    var approverSysId = approval.getValue("approver");

    // Look up the sys_user record
    var approver = new GlideRecord("sys_user");
    if (!approver.get(approverSysId)) {
        stepResult.setOutputMessage("❌ Unable to find the User to impersonate.");
        stepResult.setFailed();
        return false;
    }

    // Success
    stepResult.setOutputMessage("✅ User found for impersonation: " + approver.getDisplayValue("name"));
    stepResult.setSuccess();

    // Return the sys_id in a way ATF can use
    outputs.record = {
        sys_id: approverSysId,
        table: "sys_user"
    };

    // Also provide as record_id for flexibility
    outputs.record_id = approverSysId;

    return true;

})(outputs, steps, stepResult);


And after testing with ATF ... I'm getting this error message from the test log ...

Could not find Step 36 in steps object.

Hi @rafaelmach7x ,

 

Can you try to use previous step sys_id to get submitted record sys_id by following way.

var STEP_SYS_ID = 'previous step sys_id'; // Your Submit form test step sys_id

var record_sys_id = steps(STEP_SYS_ID).record_id); // get submitted form record sys_id.

 

Thanks!