Skip to main content

Security Policies (XDS)

A Security Policy (AxSecurityPolicy), also known as Extensible Data Security (XDS), implements row-level security in D365 F&O. While roles, duties, and privileges control what operations a user can perform, security policies control which records they can see and modify.

Security policies work by injecting additional WHERE clause conditions into every query that accesses the constrained tables. This filtering happens transparently at the kernel level — application code does not need to be aware of it.


How XDS Works

  1. A query defines the filtering logic — it specifies which records from the primary table should be visible based on the user's context.
  2. The policy identifies a primary table and optional constrained tables (related tables that are also filtered).
  3. The policy is bound to a context — either a specific role, a role property, or a context string — that determines when the policy is active.
  4. At runtime, when the user's security context matches the policy's context, the query is automatically applied as an EXISTS JOIN (or NOT EXISTS JOIN) to every data access on the constrained tables.

XDS Policy Execution Flow — Row-level security at the kernel level


Context Types

The ContextType property determines when the policy is active:

Context TypeValueDescription
ContextString0The policy is active when application code explicitly sets the XDS context to a matching string via XDSServices::setXDSContext().
RoleName1The policy is active whenever the current user is assigned to the role specified in the RoleName property.
RoleProperty2The policy is active based on a property defined on the security role's ContextString.

RoleName Context

The most common scenario — the policy automatically applies whenever a user has a specific role:

ContextString Context

Programmatic control — your code activates the policy by setting a context string:

warning

When using ContextString-based policies, always reset the context to an empty string when your operation completes. Failing to do so leaves the filter active for subsequent operations in the same session, which can cause unexpected data visibility.


Constrained Tables

A policy has a primary table (the first data source in the query) and optionally additional constrained tables. Each constrained table is an AxSecurityPolicyConstrainedEntity:

PropertyTypeDescription
NameStringThe AOT name of the constrained table.
ConstrainedNoYesWhether the policy filtering is applied to this table.
TagsStringTags for this element separated by semicolon.

Constrained tables can themselves contain nested ConstrainedTables children, forming a hierarchy. When the policy's ConstrainedTable property is set to Yes, the primary table itself is also constrained.


Join Behaviour

The UseNotExistJoin property controls how the policy query integrates with the user's data access:

ValueBehaviour
No (default)Uses an EXISTS JOIN — only records matching the policy query are returned (whitelist).
YesUses a NOT EXISTS JOIN — records matching the policy query are excluded (blacklist).

Operations

The Operation property controls which data operations the policy filters:

ValueApplies To
Select (0)Read operations only.
Insert (1)Insert operations only.
Update (2)Update operations only.
Delete (3)Delete operations only.
InsertUpdateDelete (4)All write operations (insert, update, delete).
AllOperations (5)All operations including reads and writes.

Creating a Security Policy

  1. Create a query that defines the row-level filter logic. The query's first data source is the primary table.
  2. In Visual Studio, add a new Security Policy item to your project.
  3. Set the Query property to your query.
  4. Set the PrimaryTable to the first data source table in the query.
  5. Set the ContextType and either RoleName or ContextString.
  6. Set the Operation to indicate which operations are filtered.
  7. Add constrained tables if the filter should apply to related tables beyond the primary table.
  8. Set Enabled to Yes.
  9. Build and deploy.

Example — Company-Level Data Filtering

A common pattern is restricting users to records belonging to their assigned legal entity:


Properties

18/18 properties
PropertyDisplay NameTypeDescription
Security PolicyAxSecurityPolicy
NameNameStringThe name of the element.
IsObsoleteIs ObsoleteNoYesDetermines whether the element is deprecated or not. Values: No (0), Yes (1)
VisibilityVisibilityCompilerVisibilityThe visibility of the element. Values: Private (0), Protected (1), Public (2), Internal (3), InternalProtected (4)
TagsTagsStringTags for this element separated by semicolon.
LabelLabelStringDisplay name for the policy.
HelpTextHelp TextStringHelp text for the policy.
EnabledEnabledNoYesIndicates whether the policy has been enabled. Values: No (0), Yes (1)
PrimaryTablePrimary TableStringThe first data source in the query for the policy.
QueryQueryStringQuery that is the basis for the policy.
ConstrainedTableConstrained TableNoYesIndicates whether the policy restricts the data values in records returned from the primary table. Values: No (0), Yes (1)
OperationOperationSecurityPolicyApplicabilityWhich data operations the policy filters. Values: Select (0), Insert (1), Update (2), Delete (3), InsertUpdateDelete (4), AllOperations (5)
ContextTypeContext TypeSecurityPolicyContextTypeHow the policy determines applicability. Values: ContextString (0), RoleName (1), RoleProperty (2)
ContextStringContext StringStringIf the context type is ContextString, this property displays the string.
RoleNameRole NameStringIf the context type is RoleName, this property displays the AOT name of the role.
UseNotExistJoinUse Not Exist JoinNoYesWhether the security query is applied as a NOT EXISTS JOIN (blacklist) instead of an EXISTS JOIN (whitelist). Values: No (0), Yes (1)
Constrained EntityAxSecurityPolicyConstrainedEntity
NameNameStringThe AOT name of the constrained table.
ConstrainedConstrainedNoYesIndicates whether the policy filtering is applied to this table. Values: No (0), Yes (1)
TagsTagsStringTags for this element separated by semicolon.