Dynamically retrieving the approver user in ATF and impersonating them
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
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 specifiedIn 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.
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
a week ago - last edited a week ago
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
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).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
a week ago
Hi @aruncr0122.
Thanks for your answer.
Unfortunately, in Step 38 – Impersonate – ATF only allows me to select Record → document_id.
See the image below ...
This is the last test log
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
a week ago
@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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
a week ago
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!