Study guide
16.2 Extensible Data Security (XDS) Policies
Microsoft Dynamics 365: Finance and Operations Apps Developer Associate MB-500
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Cloud & Ecosystem Architecture
1.1 Cloud vs On-Premises Architecture & Deployment Typologies 1.2 Microsoft Ecosystem Integration & Extensibility Boundaries 1.3 Lifecycle Services (LCS) & Implementation Portal
2 Chapter 2: ALM, Environments & Unified Developer Experience
2.1 Package Deployment & Servicing 2.2 Unified Developer Experience (UDE) & Power Platform Admin Center 2.3 Environment Management & Governance
3 Chapter 3: Visual Studio Development & Metadata
3.1 Visual Studio Extension Models & Packages 3.2 Application Explorer & Element Designers 3.3 Database Synchronization & Visual Studio Debugging
4 Chapter 4: Azure DevOps Version Control & CI/CD
4.1 Version Control Architecture: TFVC vs Git in F&O 4.2 Branching, Merging & Conflict Resolution 4.3 CI/CD Build Pipelines & Artifact Generation
5 Chapter 5: UI Elements: Forms, Menus & Labels
5.1 Form Design & Form Patterns 5.2 Menus, Menu Items & Navigation 5.3 Label Files & Localization
6 Chapter 6: Data Model: Tables & Relational Design
6.1 Table Design & Extension 6.2 Views, Queries & Maps 6.3 Data Entity Design & Modeling
7 Chapter 7: Classes, Attributes & AOT Modifiers
7.1 Class Architecture & Access Modifiers 7.2 Attributes & Reflection 7.3 Event Handlers & Pre/Post Method Events
8 Chapter 8: X++ Core Programming & CRUD Operations
8.1 X++ Syntax, Control Flow & Global Functions 8.2 Data Manipulation & CRUD Logic 8.3 Best Practice Rules & Compiler Diagnostics
9 Chapter 9: Advanced OOP, Chain of Command & Extensibility
9.1 OOP Principles & QueryBuilder Class 9.2 Chain of Command (CoC) Implementation 9.3 Extensibility Patterns & Non-Breaking Changes 9.4 Event Handler Classes & Delegate Architecture 9.5 Variable Scoping, Declaration Placement & Object Lifetime
10 Chapter 10: F&O Frameworks & SysTest Unit Testing
10.1 SysOperation Framework Architecture 10.2 Workflow Framework & SysExtensionSerializer 10.3 SysTest Framework & Test Explorer
11 Chapter 11: Reporting Architecture & SSRS Reports
11.1 Reporting Architecture & Datastores 11.2 Report Data Providers & Contracts 11.3 SSRS Report Design, Deployment & Security 11.4 Determining When to Use a Specific Reporting Tool or Platform
12 Chapter 12: Business Intelligence, Workspaces & Electronic Reporting
12.1 Electronic Reporting (ER) Framework 12.2 Power BI Integration & Analytical Workspaces 12.3 Workspaces, Tiles, Lists & KPIs 12.4 Excel Integration & Office App
13 Chapter 13: Integration Patterns & Service Endpoints
13.1 Integration Pattern Evaluation & API Selection 13.2 Custom Services & SOAP/REST Endpoints 13.3 Consuming External Services & REST/SOAP APIs
14 Chapter 14: Data Management Framework (DMF) & Batch Processing
14.1 DMF Concepts, Projects & Sequencing 14.2 Entity Change Tracking & Recurring Integrations 14.3 Composite Entities & Batch OData API 14.4 Monitoring Entity Status, Availability & Job Execution
15 Chapter 15: Power Platform, Business Events & Azure Integrations
15.1 Dual-Write & Virtual Entities 15.2 Business Events Framework 15.3 Azure Key Vault Integration & Secure Storage
16 Chapter 16: Role-Based Security & Extensible Data Security
16.1 Role-Based Security Architecture 16.2 Extensible Data Security (XDS) Policies 16.3 Security Enforcement & Best Practices
17 Chapter 17: Performance Optimization & Data Access Patterns
17.1 Table & Form Caching Mechanisms 17.2 Temporary Tables & Set-Based Operations 17.3 Query Tuning, Indexes & Concurrency 17.4 Diagnostics: Trace Parser, Async & Sandbox Frameworks 17.5 Tuning Variable Scope, Buffer Lifetime & Memory Pressure
16.2 Extensible Data Security (XDS) Policies
Key Takeaways Explain This Topic AI
Extensible Data Security (XDS) provides declarative row-level and record-level security by dynamically injecting SQL EXISTS joins and subqueries into database queries generated by the AOS runtime.
A security policy in the AOT consists of a Primary Table, a Policy Query defining filtering criteria, Constrained Tables filtered by the policy, and a Context Type governing policy activation.
Context Type options include RoleName (applies to explicit roles), RoleProperty (matches role ContextString properties), and ContextString (activated programmatically at runtime using XDS::SetContext()).
Developers must avoid outer joins in policy queries and ensure composite index alignment on foreign key relations between constrained and query tables to prevent debilitating table scans.
System Administrators bypass XDS policies unconditionally, and tables with CacheLookup set to EntireTable must never be constrained by dynamic XDS policies.
Last updated: September 2026
16.2 Extensible Data Security (XDS) Policies
Quick Answer: Extensible Data Security (XDS) is the declarative, kernel-level framework in Dynamics 365 Finance and Operations that enforces record-level (row-level) security . Unlike role-based security, which controls access to entire forms, menus, and tables, XDS restricts which specific rows a user can view, edit, or delete within a table. Created under AOT > Security > Security Policies , an XDS policy pairs a Primary Table and an AOT Policy Query with one or more Constrained Tables . The AOS runtime dynamically appends the policy query as an EXISTS JOIN or subquery to every SQL statement executed against constrained tables. Policies are activated based on their Context Type : RoleName , RoleProperty , or programmatically via ContextString using XDS::SetContext() . System Administrators bypass all XDS policies unconditionally.
1. The Extensible Data Security (XDS) Framework
While Role-Based Security determines whether a user can access a menu item or update records in a table, business compliance frequently mandates restricting data by record attributes (e.g., A sales manager should only see customers belonging to their assigned sales region , or A warehouse clerk should only see inventory in their local warehouse ).
Historically, legacy systems utilized Record Level Security (RLS), which relied on raw SQL WHERE clause string manipulation that suffered from poor performance, cache invalidation, and maintenance overhead. XDS completely replaced RLS in modern Dynamics 365 Finance and Operations: Role-Based Security vs. Extensible Data Security (XDS)
- ┌─────────────────────────────────────────┐ ┌─────────────────────────────────────────┐
- │ Role-Based Security │ │ Extensible Data Security (XDS) │
- │ • Object-level authorization │ │ • Record-level / Row-level filtering │
- │ • Governs forms, menus, tables, fields │ │ • Governs specific data rows in tables │
- │ • Can the user open "All Customers"? │ │ • Which specific customers appear? │
- │ • Evaluated during navigation / load │ │ • Injected into every SQL query by AOS │
- └─────────────────────────────────────────┘ └─────────────────────────────────────────┘
- Kernel-Level SQL Rewriting
XDS operates inside the Application Object Server (AOS) data access layer. Whenever an affected user issues a query against a constrained table (whether through a form, report, OData endpoint, data entity, or X++ select statement), the AOS rewrites the generated Transact-SQL statement, appending an EXISTS subquery bound to the policy query. /* Conceptual SQL generated by AOS with an active XDS Policy */
- SELECT T1.ACCOUNTNUM, T1.NAME, T1.RECID
- FROM CUSTTABLE T1
- WHERE (T1.DATAAREAID = 'usmf')
- AND EXISTS (
- SELECT 1
- FROM CUSTGROUP T2
- JOIN DIRPERSONUSER T3 ON T2.RESPONSIBLEWORKER = T3.PERSONPARTY
- WHERE T2.CUSTGROUP = T1.CUSTGROUP
- AND T3.USER = 'alicia'
- );
Because filtering occurs at the database query generation layer, data cannot leak through OData integrations, Excel add-ins, or custom classes, ensuring comprehensive security isolation.
Important
The System Administrator Exception: Users assigned to the System Administrator role bypass all XDS policies unconditionally. When designing and testing XDS policies, developers must never test using an administrator account. Always test using a non-administrative user persona assigned the specific business roles targeted by the policy.
2. AOT Security Policy Components
Developers construct XDS policies declaratively in Visual Studio under the Security > Security Policies node of an AOT project. Every security policy consists of four fundamental structural components: XDS Security Policy Architecture
- ┌─────────────────────────────────────────────────────────────┐
- │ Security Policy │
- ├─────────────────────────────────────────────────────────────┤
- │ 1. Primary Table: CustGroup │
- │ 2. Policy Query: CustGroupByWorkerQuery │
- │ 3. Context Type: RoleName / RoleProperty / ContextString │
- │ 4. Constrained Table: Yes (Primary table constrained) │
- ├──────────────────────────────┬──────────────────────────────┤
- │ │ Relates to
- │ ▼
- │ ┌───────────────────────────────────────────────────────┐ │
- │ │ Constrained Tables Sub-Node │ │
- │ │ • CustTable (CustTable.CustGroup == CustGroup.Group) │ │
- │ │ • SalesTable (SalesTable.CustGroup == CustGroup.Group│ │
- │ └───────────────────────────────────────────────────────┘ │
- └─────────────────────────────────────────────────────────────┘
- Core Properties of a Security Policy
Primary Table ( PrimaryTable ): The anchor table of the policy. It serves as the root data source of the associated policy query. All filtering criteria ultimately evaluate against this table.
Policy Query ( Query ): A standard AOT Query that retrieves the set of records in the Primary Table that the current user is permitted to see. The query typically incorporates ranges based on session variables (e.g., (currentWorker()) or (curUserId()) ).
Constrained Table ( ConstrainedTable ): A boolean property on the policy itself. If set to Yes , the Primary Table is automatically filtered whenever queried by affected users. If set to No , the primary table remains unfiltered, and filtering only applies to tables listed in the Constrained Tables child node.
Constrained Tables Node: A collection of additional tables that should be filtered by the policy. For each constrained table, developers define the table relation or join condition linking the constrained table to the Primary Table.
Operation ( Operation ): Defines which database actions trigger the policy:
AllOperations : Constrains SELECT , UPDATE , DELETE , and INSERT validation.
Select : Constrains read queries only.
Insert : Constrains record insertion.
Update : Constrains record modifications.
Delete : Constrains record deletions.
Comparison of Policy Operations
Operation Value SELECT Constrained? UPDATE Constrained? DELETE Constrained? INSERT Constrained?
- AllOperations Yes Yes Yes Yes (Validation)
- Select Yes No No No
- Update No Yes No No
- Delete No No Yes No
- Insert No No No Yes
- 3. Context Types & Programmatic Context Switching
The ContextType property controls when and for whom the security policy is active. Dynamics 365 Finance and Operations supports three policy context types:
Context Type Activation Mechanism Use Case
RoleName The policy is tied to a single specific security role designated in the RoleName property. Dedicated operational roles (e.g., Retail Store Associate can only see their retail channel).
RoleProperty The policy applies to any security role whose ContextString property matches the policy's ContextString . Multi-role policies (e.g., applying regional filtering across Sales Clerk , Sales Manager , and Customer Rep ).
ContextString The policy remains dormant until explicitly activated programmatically in X++ code using XDS::SetContext() . Controlled process execution (e.g., applying row-level constraints only during a specific wizard or batch run).
Programmatic Context Switching in X++
When an XDS policy has ContextType = ContextString , the AOS does not apply the policy during normal navigation. Instead, developers activate the policy in code by calling the static method XDS::SetContext() : /// <summary>
/// Demonstrates programmatic XDS context activation and cleanup.
- /// </summary>
- class ConRegionalDataProcessor
- {
- public static void processRegionalInvoices(str _regionContextCode)
- {
- // Activate the XDS security policy bound to this ContextString
- XDS::SetContext(_regionContextCode);
- try
- {
- CustInvoiceJour custInvoiceJour;
- // This select statement is now dynamically constrained by the XDS policy
- while select custInvoiceJour
- where custInvoiceJour.InvoiceDate == today()
- {
- info(strFmt("Processing invoice %1 for customer %2",
- custInvoiceJour.InvoiceId,
- custInvoiceJour.InvoiceAccount));
- }
- }
- finally
- {
- // Critical: Clear the XDS context to avoid leaking constraints across the session
- XDS::SetContext('');
- }
- }
- }
Caution
Session Leakage Hazard: XDS::SetContext() modifies the security context for the current user's entire session thread. If code fails to clear the context ( XDS::SetContext('') ) in a finally block, all subsequent forms and operations opened by the user will continue to execute under the restricted XDS context, leading to missing data and unexplainable application errors.
4. Performance Optimization, Index Alignment & Caching
Because XDS rewrites SQL statements executed against constrained tables, a poorly designed security policy can severely degrade database performance across the entire environment.
Index Alignment
When the AOS injects an EXISTS JOIN linking a constrained table (e.g., CustTrans ) to a primary table (e.g., CustTable ), the SQL Server optimizer must join those two tables on every single query. If the join fields are not covered by an index, SQL Server will execute a full table scan on multi-million row tables. Index Alignment for XDS Join Performance
- Constrained Table: CustTrans Primary Table: CustTable
- ┌──────────────────────────────┐ ┌──────────────────────────────┐
- │ AccountNum (Foreign Key) │──────────>│ AccountNum (Primary Key) │
- │ TransDate │ JOIN │ CustGroup │
- │ Voucher │ │ Currency │
- └──────────────────────────────┘ └──────────────────────────────┘
- ▲ ▲
- │ Mandatory Database Index │ Mandatory Database Index
- └──────────────────────────────────────────┴─────────────────────────────
Must have composite index on: Must have index on:
- [DataAreaId, AccountNum] [DataAreaId, AccountNum]
- Avoiding Outer Joins in Policy Queries
Policy queries must strictly utilize Inner Joins or Exists Joins . Developers must never use Outer Joins inside an XDS policy query. Outer joins force the SQL Server query engine to preserve non-matching rows and evaluate NULL logic across the join, which prevents the optimizer from using index seeks and frequently results in query plan hash joins, deadlocks, and query timeouts.
Table Caching Interactions
Dynamics 365 Finance and Operations implements multiple levels of Application Object Server caching:
EntireTable Cache: The AOS loads the entire table into server memory on the first read. Never apply an XDS policy to a table with CacheLookup = EntireTable ! If an XDS policy is applied, the global in-memory cache conflicts with user-specific row filtering. The system must repeatedly invalidate and reload the cache, creating massive CPU spikes and thread locking.
Found and FoundAndEmpty Cache: Individual record caches are security-aware. The cache key includes the security context, allowing XDS to function safely with single-record lookups.
- 5. Scenario Walk-Through: Multi-Regional Sales Security Policy
- Business Context
Contoso Retail operates stores across three sales territories: East, Central, and West. The corporate compliance board establishes the following security rule:
"Sales clerks must only view and edit customer records and sales orders belonging to their assigned territory's Customer Group. Under no circumstances may a sales clerk see customers or sales orders from other territories."
Implementation Walk-Through Contoso Territory Policy Implementation Structure
- ┌─────────────────────────────────────────────────────────────────────────────┐
- │ 1. AOT Query: ConTerritoryCustGroupQuery │
- │ Data Source: CustGroup │
- │ Child Data Source: ConTerritoryWorkerAssignment (Inner Join) │
- │ Range on Worker: (currentWorker()) │
- └──────────────────────────────────────┬──────────────────────────────────────┘
- │ Used as Policy Query
- ▼
- ┌─────────────────────────────────────────────────────────────────────────────┐
- │ 2. AOT Security Policy: ConTerritoryCustomerPolicy │
- │ Primary Table: CustGroup │
- │ Query: ConTerritoryCustGroupQuery │
- │ Context Type: RoleName │
- │ Role Name: ConSalesClerkRole │
- │ Constrained Table: Yes (CustGroup itself is constrained) │
- ├─────────────────────────────────────────────────────────────────────────────┤
- │ 3. Constrained Tables Sub-Node │
- │ • Table: CustTable │
- │ Relation: CustTable.CustGroup == CustGroup.CustGroup │
- │ • Table: SalesTable │
- │ Relation: SalesTable.CustGroup == CustGroup.CustGroup │
- └─────────────────────────────────────────────────────────────────────────────┘
Create the Policy Query ( ConTerritoryCustGroupQuery ):
Root data source: CustGroup .
Joined data source: ConTerritoryWorkerAssignment , joined on CustGroup.TerritoryId == ConTerritoryWorkerAssignment.TerritoryId .
Query range on ConTerritoryWorkerAssignment.Worker : Value = (currentWorker()) .
Create the Security Policy ( ConTerritoryCustomerPolicy ):
Set PrimaryTable = CustGroup .
Set Query = ConTerritoryCustGroupQuery .
Set ContextType = RoleName and set RoleName = ConSalesClerkRole .
Set ConstrainedTable = Yes .
Add Constrained Tables:
Add CustTable . Set relation to CustTable.CustGroup == CustGroup.CustGroup .
Add SalesTable . Set relation to SalesTable.CustGroup == CustGroup.CustGroup .
Verification & Index Alignment:
Confirm that CustTable has an index covering [DataAreaId, CustGroup] .
Confirm that SalesTable has an index covering [DataAreaId, CustGroup] .
Log in as a test user assigned to ConSalesClerkRole (not System Administrator) and verify that only territory-specific customers and sales orders render.
6. Advanced Pattern: The "MyTable" Pattern and Cross-Company XDS
In complex multi-company environments, filtering criteria frequently span multiple legal entities or depend on dynamic organizational hierarchy positions that are expensive to evaluate repeatedly inside SQL queries.
The "MyTable" Design Pattern
To avoid joining massive HR and hierarchy tables on every single constrained query, developers implement the MyTable pattern (e.g., MyLegalEntities , MyCostCenters , MyDepartments ):
A temporary table or lightweight persisted session table is created containing the list of entity IDs accessible to the current user.
When the user logs in or begins a session, a startup routine populates the user's MyTable records.
The XDS Policy Query simply joins the Primary Table directly to MyTable where MyTable.UserId == curUserId() .
Because MyTable contains only the current user's authorized IDs, the resulting SQL query plan is exceptionally lightweight, converting complex hierarchy evaluations into a simple indexed primary key lookup.
7. Exam Traps & Troubleshooting Reference
Issue / Trap Root Cause Resolution
Admin sees all data Developer tests XDS while assigned the System Administrator role. System Administrators bypass XDS by design. Test exclusively with a non-admin test user account.
Severe query slowdowns Join fields between constrained table and primary table lack database indexes. Add composite indexes on the foreign key join fields in the constrained table.
AOS CPU spikes on lookup XDS policy is applied to a table configured with CacheLookup = EntireTable . Remove the XDS constraint or change table caching strategy to Found or None .
Context leaking across forms Developer called XDS::SetContext() without clearing it in a finally block. Always invoke XDS::SetContext('') inside a structured finally block.
Blank lookup dropdowns Primary table has ConstrainedTable = Yes and the user has no matching query rows. Verify that the policy query returns valid master records for the active worker identity.
Outer join query timeout The policy query uses an Outer Join between data sources. Refactor the query to use InnerJoin or ExistsJoin exclusively.