Bypassing XDS Safely in D365 Finance & Operations: Using RunAs for Cross-Context Data Access
When working in Dynamics 365 Finance and Operations (D365FO), it’s not uncommon to encounter missing or incomplete data even when records exist. In many cases, this behavior is caused by Extensible Data Security (XDS), which applies row-level security at runtime.
This post walks through a scenario where XDS blocks a buyer name lookup and demonstrates an approach using RunAs to access the required data without compromising security.
Why Buyer Name Lookups are Affected by XDS
PolicyForVendorRoles policy restricts access to worker/employee-related data, which causes the buyer name lookup to return nothing (or incomplete data) when executed under the calling user.BuyerName is derived from worker/person tables (HcmEmployment, HcmWorker, DirPerson), XDS kicks in and hides the data unless the caller has the correct privileges.Why Use RunAs to Bypass XDS
To bypass XDS safely, we execute the lookup under a different security context (typically Admin) using runAs. This avoids modifying or disabling the XDS policy (which would be a terrible idea) and instead isolates the elevated access strictly to the required logic.
- XDS policies intact ✅
- Security scoped ✅
- Behavior predictable ✅
Example of XDS being worked around using RunAs
Calling Method
private DirPartyName getBuyerName(PurchPlacer _buyerRecId, DataAreaId _dataAreaId)
{
container ctParams = [_buyerRecId,
_dataAreaId];
container ret =
SSIPurchGetBuyerName::buyerName(ctParams);
return conPeek(ret,1);
}
Privileged Execution Using RunAs
The static class handles the privilege escalation and execution.
static class SSIPurchGetBuyerName
{
public static container buyerName(container _parms)
{
RunAsPermission perm;
perm = new RunAsPermission("Admin");
perm.assert();
Container returncontainer = runAs("Admin", classnum(SSIPurchGetBuyerName), "get", _parms);
CodeAccessPermission::revertAssert();
return returncontainer;
}
public static container get(container _parms)
{
if(conLen(_parms) != 2)
{
throw Error("Wrong # of params.");
}
PurchPlacer _buyerRecId =
conPeek(_parms,1);
DataAreaId _dataAreaId =
conPeek(_parms,2);
// Retrieve name (XDS would normally filter this)
HcmEmployment hcmEmployment = HcmEmployment::findByWorkerLegalEntity(_buyerRecId,
CompanyInfo::findDataArea(_dataAreaId).RecId);
HcmWorker hcmWorker = HcmWorker::find(hcmEmployment.Worker);
DirPerson dirPerson = DirPerson::find(hcmWorker.Person);
// Return result in container (required for runAs boundary)
container con = [dirPerson.Name];
return con;
}
}
Key Takeaways
In Dynamics 365 Finance and Operations, (XDS) enforces row-level security and can silently filter worker-related data. When lookups depend on HR tables protected by XDS, using RunAs allows you to retrieve the data safely. This approach preserves XDS policies while limiting elevated access to only the required logic.
Under the terms of this license, you are authorized to share and redistribute the content across various mediums, subject to adherence to the specified conditions: you must provide proper attribution to Stoneridge as the original creator in a manner that does not imply their endorsement of your use, the material is to be utilized solely for non-commercial purposes, and alterations, modifications, or derivative works based on the original material are strictly prohibited.
Responsibility rests with the licensee to ensure that their use of the material does not violate any other rights.


