Dataverse Security Laboratory: Privileges, Ownership, Business Units, Teams, and Record-Level Authorization
Engineering and testing Microsoft Dataverse security through C#, security roles, business units, teams, record ownership, sharing, and the effective authorization model.
Power Platform Engineering Academy — Five Years in Five Days
Application: APP-010 — Dataverse Security Lab
Technical domain: Microsoft Dataverse security architecture
Technologies: C#, Dataverse SDK, Microsoft Entra ID, security roles, business units, owner teams, access teams, sharing, Model-Driven Apps
Status: Pre-implementation engineering design — experiments not yet executed
Introduction
Security in Microsoft Dataverse is not simply a question of whether a user has permission to open an application.
A user may have access to a Model-Driven App but lack permission to read a particular table. Another user may have permission to read the table but only see records owned by themselves or their team.
A third user may gain access to an individual record through sharing, even though they do not own it.
These outcomes are governed by multiple interacting security mechanisms.
Dataverse combines table privileges, privilege depth, record ownership, business units, security roles, teams, and record-level access rights.
This creates a powerful authorization model, but it also introduces complexity.
A developer who tests every operation using a System Administrator account can easily miss security defects that appear when the application is used by ordinary business users.
APP-010 — Dataverse Security Laboratory investigates the effective authorization model through controlled users, teams, records, SDK operations, and a Model-Driven App.
Our central engineering question is:
How does Dataverse determine whether an identity can perform a particular operation on a particular record, and how can engineers verify that decision?
1. Authentication Is Not Authorization
Authentication establishes the identity making a request.
Authorization determines what that identity is allowed to do.
For example, a C# application may successfully authenticate to Dataverse through Microsoft Entra ID.
That does not mean the authenticated identity can create, read, update, or delete every Dataverse record.
The effective authorization depends on the Dataverse security configuration and the requested operation.
A useful conceptual model is:
Microsoft Entra ID │ ▼Authenticated Identity │ ▼Dataverse User / Application User │ ▼Security Roles + Teams + Ownership + Sharing │ ▼Effective Access Evaluation │ ▼Operation Allowed or Rejected
This model is simplified.
Actual access evaluation also depends on table type, privilege requirements, hierarchy security where configured, and other applicable platform security mechanisms.
The laboratory will isolate the principal mechanisms before combining them.
2. Security Roles and Privileges
Security roles define collections of privileges.
Common table privileges include:
| Privilege | Purpose |
|---|---|
| Create | Create records |
| Read | Read records |
| Write | Update records |
| Delete | Delete records |
| Append | Associate a record with another record |
| Append To | Allow another record to be associated with this record |
| Assign | Change record ownership |
| Share | Grant supported record-level access |
Privileges are not interchangeable.
For example, permission to read a record does not imply permission to update it.
Similarly, having Write privilege does not automatically grant permission to assign ownership.
A developer must evaluate the privileges required by the actual operation.
Reference: Security roles and privileges.
3. Privilege Depth
For applicable owner-owned tables, privileges can be configured with different access depths.
| Access depth | Meaning |
|---|---|
| User / Basic | Records accessible through the user’s applicable ownership and access context |
| Business Unit / Local | Records within the relevant business unit scope |
| Parent Business Units / Deep | Records in the business unit and subordinate units |
| Organization / Global | Records throughout the organization |
Privilege depth determines the scope of a privilege.
It does not replace the privilege itself.
For example, Organization-level Read does not imply Organization-level Write.
A user can have broad visibility while retaining limited modification rights.
Dataverse also supports modernized business-unit security configurations, so ownership and role assignment should not be reduced to a simplistic assumption that every user belongs to exactly one effective security scope.
The laboratory must record the environment’s business-unit security configuration before interpreting results.
4. Record Ownership
Dataverse supports different table ownership models.
Two important categories are:
User or team-owned tables: Records can be owned by users or eligible teams, and owner-based security is applicable.
Organization-owned tables: Records do not use individual user/team ownership for access in the same way.
The ownership model is selected when the table is created and has significant architectural consequences.
For APP-010, the principal test table will be user or team-owned.
This allows the laboratory to investigate record ownership, reassignment, and team-based access.
5. Business Units
Business units provide an organizational security structure.
Consider this simplified hierarchy:
Organization│└── Headquarters ├── Engineering │ ├── Platform Team │ └── Integration Team │ └── Operations ├── Support Team └── Audit Team
Business units can influence the scope of privileges and record access.
However, the hierarchy itself does not grant every user access to all records beneath a particular node.
Effective access depends on assigned roles, privilege depths, ownership, team membership, and the applicable security configuration.
The laboratory will use separate identities and records to verify access across business-unit boundaries.
6. Owner Teams Versus Access Teams
Dataverse supports different team models.
6.1 Owner teams
Owner teams can own records.
Security roles can be assigned to owner teams, subject to the platform’s supported configuration.
This makes them useful when a group of users jointly owns business records.
6.2 Access teams
Access teams are designed to provide record-level collaboration.
They do not function as record owners in the same way as owner teams.
Access teams are commonly used when multiple users need access to particular records without changing ownership.
6.3 Engineering comparison
| Dimension | Owner team | Access team |
|---|---|---|
| Can own records | Yes | No |
| Can be assigned security roles | Yes | No |
| Supports collaborative record access | Yes | Yes |
| Primary mechanism | Ownership and role-based access | Record-level access |
| Typical scenario | Shared operational responsibility | Temporary or targeted collaboration |
The laboratory will compare owner-team access with explicitly shared record access.
Reference: Manage Dataverse teams.
7. Record Sharing
Dataverse supports explicit sharing of records.
Sharing grants supported access rights to a user or team for a particular record.
For example, a record owner may share a record with another user and grant Read access.
The recipient may then read the record without becoming its owner.
However, sharing does not bypass every other security requirement.
The recipient must have the relevant table privileges and any other required permissions.
This distinction is critical.
A share is not a substitute for the underlying security role privileges.
The laboratory will test this explicitly.
8. Application Architecture
APP-010 Dataverse Security Laboratory│├── C# Security Test Runner│ ├── Identity Verification│ ├── Create / Read / Update / Delete│ ├── Ownership Tests│ ├── Assign Tests│ ├── Sharing Tests│ ├── Access Inspection│ └── Authorization Diagnostics│├── Dataverse│ ├── pea_securerecord│ ├── pea_securityscenario│ └── pea_securityexecution│├── Security Configuration│ ├── Test Users│ ├── Business Units│ ├── Security Roles│ ├── Owner Teams│ └── Access Teams│└── Model-Driven App ├── Entry Dashboard ├── Secure Records ├── Security Scenarios ├── Execution Results └── Access Diagnostics
The C# runner performs controlled operations under different identities.
The Model-Driven App provides the operational interface for inspecting records and security results.
9. Dataverse Data Model
9.1 Secure Record
Logical name: pea_securerecord
Ownership: User or team-owned
| Column | Type | Purpose |
|---|---|---|
pea_name | Primary name | Record identifier |
pea_externalcode | Single line of text | Experiment correlation |
pea_classification | Choice | Example classification |
pea_description | Multiple lines of text | Test content |
pea_amount | Decimal number | Update test |
pea_scenarioid | Single line of text | Scenario correlation |
The classification field is initially informational.
It does not automatically enforce field-level security.
If field-level security is required, it must be configured explicitly using supported column security mechanisms.
9.2 Security Scenario
Logical name: pea_securityscenario
| Column | Type | Purpose |
|---|---|---|
pea_name | Primary name | Scenario identifier |
pea_operation | Single line of text | Requested operation |
pea_identity | Single line of text | Test identity |
pea_expectedaccess | Yes/No | Expected authorization |
pea_description | Multiple lines of text | Scenario objective |
pea_enabled | Yes/No | Scenario availability |
9.3 Security Execution
Logical name: pea_securityexecution
| Column | Type | Purpose |
|---|---|---|
pea_name | Primary name | Execution identifier |
pea_scenario | Single line of text | Scenario |
pea_identity | Single line of text | Executing identity |
pea_operation | Single line of text | Requested operation |
pea_recordid | Single line of text | Target record |
pea_expectedaccess | Yes/No | Expected outcome |
pea_actualaccess | Yes/No | Observed authorization |
pea_passed | Yes/No | Assertion result |
pea_message | Multiple lines of text | Diagnostic details |
pea_executedon | Date and time | Execution timestamp |
Security test results should be written by a trusted diagnostic identity.
A restricted test user may not have permission to create diagnostic records.
This is intentional: the test harness must not grant additional privileges merely to make logging easier.
10. Controlled Test Identities
The laboratory should define at least four identities.
| Identity | Intended role |
|---|---|
| Security Administrator | Configure roles, teams, and test records |
| Engineering User | Own and modify selected records |
| Operations User | Access records within an assigned scope |
| External Collaborator | Receive explicit record-level sharing |
These identities must be configured with known roles and privileges.
The experiment should document each identity’s business unit, team memberships, and assigned roles.
Otherwise, unexpected access results may be caused by hidden privilege inheritance or additional role assignments.
The test identities should be dedicated to the laboratory rather than reusing production users with complex security configurations.
11. Inspecting Effective Access
The SDK provides specialized messages for inspecting record access.
One important request is:
RetrievePrincipalAccessRequest
This request retrieves the access rights a specified principal has to a record.
Example:
var request = new RetrievePrincipalAccessRequest{ Principal = new EntityReference( "systemuser", userId), Target = new EntityReference( "pea_securerecord", recordId)};var response = (RetrievePrincipalAccessResponse) service.Execute(request);AccessRights rights = response.AccessRights;
The returned rights provide a useful diagnostic signal.
However, the test harness should still execute the actual operation under the target identity.
An access-inspection result is not a replacement for end-to-end authorization testing.
12. Granting Record Access
The SDK supports record sharing through GrantAccessRequest.
var request = new GrantAccessRequest{ Target = new EntityReference( "pea_securerecord", recordId), PrincipalAccess = new PrincipalAccess { Principal = new EntityReference( "systemuser", collaboratorId), AccessMask = AccessRights.ReadAccess }};service.Execute(request);
The operation grants the specified record-level access rights, subject to the caller’s privileges and platform rules.
After sharing, the laboratory should verify whether the collaborator can retrieve the record.
It should also test whether the collaborator can update the record.
If only Read access was granted and no other mechanism supplies Write access, the update should be rejected.
13. Revoking Record Access
Dataverse supports revoking previously granted access.
var request = new RevokeAccessRequest{ Target = new EntityReference( "pea_securerecord", recordId), Revokee = new EntityReference( "systemuser", collaboratorId)};service.Execute(request);
After revocation, the test harness should repeat the access checks.
However, revoking one explicit share does not guarantee that the user loses all access.
The user may still have access through ownership, security roles, team membership, or another applicable mechanism.
This is a central lesson of the laboratory:
Effective access can have multiple independent sources.
14. Assigning Record Ownership
Ownership can be changed using supported Dataverse operations.
For example, an SDK update can set ownerid to an eligible user or owner team.
var record = new Entity( "pea_securerecord", recordId);record["ownerid"] = new EntityReference( "systemuser", newOwnerId);service.Update(record);
The operation requires appropriate privileges and must respect the platform’s ownership rules.
Changing ownership can alter who can access the record.
It may also interact with business-unit security and related-record behavior.
The laboratory should inspect access before and after reassignment.
15. Independent C# Security Test Harness
The proposed project is:
PowerPlatformEngineeringAcademy/└── Dataverse/ └── APP010.DataverseSecurityLab/ ├── APP010.DataverseSecurityLab.csproj └── Program.cs
Create the project:
dotnet new console --name APP010.DataverseSecurityLabcd APP010.DataverseSecurityLabdotnet add package Microsoft.PowerPlatform.Dataverse.Client
The initial experiment will use two Dataverse connections:
Administrative connection: Creates test records, configures sharing, inspects access, and persists diagnostics.
Restricted connection: Executes read and update operations under the target test identity.
The connections must authenticate as genuinely different Dataverse identities.
Changing a label in the C# application does not change the effective security principal.
The first executable experiment will:
- Authenticate both identities.
- Verify their user IDs.
- Create a test record under the administrative identity.
- Attempt retrieval under the restricted identity.
- Grant explicit Read access.
- Repeat the retrieval.
- Attempt an update.
- Revoke explicit sharing.
- Repeat the retrieval.
- Persist diagnostic results.
The actual outcome depends on the configured roles and ownership.
The harness must not assume that sharing is the only possible access mechanism.
16. Complete C# Security Test Runner
The following implementation exercises a controlled record-sharing scenario using two independently authenticated Dataverse identities.
It deliberately separates the administrator’s operations from the restricted user’s operations.
The restricted identity must have table-level Read privilege at an appropriate depth, but should initially lack access to the administrator-owned test record. Microsoft documents that record sharing cannot grant privileges the recipient does not have on the table. <Cite refs={[“turn848315search0″,”turn848315search3”]}/>
using System;using System.Collections.Generic;using System.Diagnostics;using System.ServiceModel;using Microsoft.Crm.Sdk.Messages;using Microsoft.PowerPlatform.Dataverse.Client;using Microsoft.Xrm.Sdk;using Microsoft.Xrm.Sdk.Messages;using Microsoft.Xrm.Sdk.Query;const string RecordTable = "pea_securerecord";const string ExecutionTable = "pea_securityexecution";string Required(string name) => Environment.GetEnvironmentVariable(name) ?? throw new InvalidOperationException( $"Missing environment variable: {name}");using var admin = new ServiceClient( Required("DATAVERSE_ADMIN_CONNECTION"));using var restricted = new ServiceClient( Required("DATAVERSE_RESTRICTED_CONNECTION"));if (!admin.IsReady || !restricted.IsReady){ throw new InvalidOperationException( $"Connection failure. Admin: {admin.LastError}; " + $"Restricted: {restricted.LastError}");}IOrganizationService adminService = admin;IOrganizationService restrictedService = restricted;Guid adminId = ((WhoAmIResponse)adminService.Execute( new WhoAmIRequest())).UserId;Guid restrictedId = ((WhoAmIResponse) restrictedService.Execute( new WhoAmIRequest())).UserId;if (adminId == restrictedId){ throw new InvalidOperationException( "Test identities must be different.");}Console.WriteLine($"Administrator: {adminId}");Console.WriteLine($"Restricted user: {restrictedId}");string scenarioId = $"APP010-{Guid.NewGuid():N}";var record = new Entity(RecordTable){ ["pea_name"] = "Security Laboratory", ["pea_externalcode"] = scenarioId, ["pea_description"] = "Controlled sharing experiment", ["pea_amount"] = 100m, ["pea_scenarioid"] = scenarioId, ["ownerid"] = new EntityReference( "systemuser", adminId)};Guid recordId = adminService.Create(record);var target = new EntityReference( RecordTable, recordId);var restrictedPrincipal = new EntityReference( "systemuser", restrictedId);void LogResult( string scenario, string operation, bool expectedAccess, bool actualAccess, string message){ var log = new Entity(ExecutionTable) { ["pea_name"] = $"APP010-{Guid.NewGuid():N}", ["pea_scenario"] = scenario, ["pea_identity"] = restrictedId.ToString(), ["pea_operation"] = operation, ["pea_recordid"] = recordId.ToString(), ["pea_expectedaccess"] = expectedAccess, ["pea_actualaccess"] = actualAccess, ["pea_passed"] = expectedAccess == actualAccess, ["pea_message"] = message, ["pea_executedon"] = DateTime.UtcNow }; adminService.Create(log);}bool CanRead(string scenario, bool expected){ bool allowed = false; string message; try { restrictedService.Retrieve( RecordTable, recordId, new ColumnSet("pea_name")); allowed = true; message = "Retrieve succeeded"; } catch (FaultException<OrganizationServiceFault> ex) { message = $"Dataverse fault {ex.Detail.ErrorCode}: " + ex.Detail.Message; } LogResult( scenario, "Retrieve", expected, allowed, message); Console.WriteLine( $"{scenario}: Read={allowed}, Expected={expected}"); if (allowed != expected) { throw new InvalidOperationException( $"Unexpected read access in {scenario}"); } return allowed;}// BASELINE: no access expected.CanRead("BeforeSharing", expected: false);// GRANT READ ACCESSadminService.Execute(new GrantAccessRequest{ Target = target, PrincipalAccess = new PrincipalAccess { Principal = restrictedPrincipal, AccessMask = AccessRights.ReadAccess }});// Verify effective rights.var accessResponse = (RetrievePrincipalAccessResponse) adminService.Execute( new RetrievePrincipalAccessRequest { Principal = restrictedPrincipal, Target = target });Console.WriteLine( $"Effective rights: {accessResponse.AccessRights}");// Read should now succeed.CanRead("AfterSharing", expected: true);// UPDATE SHOULD BE DENIEDbool writeAllowed = false;string writeMessage;try{ var update = new Entity(RecordTable, recordId) { ["pea_amount"] = 500m }; restrictedService.Update(update); writeAllowed = true; writeMessage = "Update succeeded";}catch (FaultException<OrganizationServiceFault> ex){ writeMessage = $"Dataverse fault {ex.Detail.ErrorCode}: " + ex.Detail.Message;}LogResult( "ReadOnlyShare", "Update", expectedAccess: false, actualAccess: writeAllowed, message: writeMessage);if (writeAllowed){ throw new InvalidOperationException( "Restricted user unexpectedly updated the record.");}// REVOKE EXPLICIT SHARINGadminService.Execute(new RevokeAccessRequest{ Target = target, Revokee = restrictedPrincipal});// Read should again be denied.CanRead("AfterRevocation", expected: false);Console.WriteLine( "Security laboratory completed.");
16.1 Expected observations
| Scenario | Read | Write |
|---|---|---|
| Before sharing | Denied | Not tested |
| After Read sharing | Allowed | Denied |
| After revocation | Denied | Not tested |
These results require the prescribed security configuration. They are not universal outcomes for every Dataverse environment.
If the restricted user already has access through a broader security role or team membership, revoking an explicit share will not necessarily remove access.
The harness intentionally fails when observed access differs from the expected baseline.
16.2 Implementation limitations
The sample catches Dataverse service faults, but a production test harness should distinguish authorization failures from unrelated failures such as invalid metadata, connection problems, or unavailable services.
It should also verify that the restricted identity has the expected security roles before running.
The example does not automatically create users, teams, business units, or security roles.
Those components must be configured before execution.
17. Effective Access Is Additive
One of the most important Dataverse security principles is that access can be granted through multiple mechanisms.
For example:
User │ ├── Direct Security Roles │ ├── Team Security Roles │ ├── Record Ownership │ ├── Team Ownership │ ├── Explicit Sharing │ └── Other Applicable Security Mechanisms │ ▼ Effective Access
Dataverse evaluates the applicable access mechanisms rather than using a simple deny-overrides-allow model for ordinary record permissions.
A user may have access through several independent paths.
Removing one path does not necessarily eliminate access.
Microsoft documents the effective privilege model and the relevant access mechanisms. <Cite refs={[“turn848315search1″,”turn848315search14”]}/>
18. Security Roles Versus Record Sharing
Security roles and record sharing solve different problems.
| Concern | Security role | Record sharing |
|---|---|---|
| Scope | Table privileges and access depth | Particular records |
| Typical administration | Security administrators | Authorized record collaborators |
| Defines table-level privilege | Yes | No |
| Grants record-specific access | Through applicable scope | Yes |
| Can replace required table privilege | Not applicable | No |
| Primary purpose | Baseline authorization | Targeted collaboration |
A secure architecture should establish appropriate baseline privileges through roles.
Record sharing should be used where individual-record collaboration is required.
Excessive sharing can make effective access harder to reason about and maintain.
19. Column-Level Security
Dataverse also supports column-level security for eligible columns.
A user may have permission to read a record but not permission to read a particular secured column.
This is a different authorization dimension from record ownership and sharing.
For example, a user might be allowed to read a customer record while being restricted from viewing a sensitive financial field.
Column security requires explicit configuration and supported column types.
The laboratory should introduce a secured column in a later experiment and compare:
- Record access without secured-column access.
- Record access with secured-column Read permission.
- Record access with secured-column Update permission.
The Model-Driven App must not be treated as the security boundary.
Hiding a field from a form is not equivalent to securing it at the Dataverse service layer.
20. Security and the Model-Driven App
The Model-Driven App is an interface to Dataverse.
It does not replace Dataverse authorization.
Removing a table from navigation does not necessarily prevent an authorized user from accessing the table through another supported client or API.
Similarly, hiding a command does not necessarily prevent a user from performing the underlying operation through the SDK or Web API.
The laboratory must therefore test both:
User interface behavior: What does the user see and what actions are available?
Server authorization: Which operations does Dataverse actually permit?
A secure implementation requires both appropriate UI behavior and enforced server-side permissions.
21. Model-Driven App: PEA Dataverse Security Laboratory
The operational application will be named:
PEA Dataverse Security Laboratory
Entry dashboard
| Metric | Purpose |
|---|---|
| Total security tests | Number of recorded authorization checks |
| Passed assertions | Expected access matched actual access |
| Failed assertions | Unexpected authorization outcomes |
| Denied operations | Number of rejected operations |
| Unexpected grants | Operations allowed when denial was expected |
| Latest execution | Most recent security test |
Secure Records
A view exposes test records, ownership, classification, and scenario identifiers.
Security Scenarios
A form documents the expected operation, target identity, and authorization result.
Security Executions
A view displays expected access, observed access, assertion outcome, and diagnostic information.
Access Diagnostics
A custom page may later display the effective rights returned by RetrievePrincipalAccessRequest alongside the actual result of an operation performed under the target identity.
The dashboard should make unexpected grants immediately visible.
An unexpected authorization success can be more dangerous than a denied operation.
22. Failure Injection Matrix
| Experiment | Expected observation |
|---|---|
| User without table Read privilege | Record retrieval denied |
| User with Read but insufficient record scope | Record retrieval denied |
| User with explicit Read share | Retrieval allowed when baseline privilege exists |
| User with Read share but no Write access | Update denied |
| Revoke explicit share | Access removed only if no other path remains |
| Assign record to another user | Ownership and effective access may change |
| Add user to owner team | Team-based access may become available |
| Remove user from owner team | Team-derived access may be removed |
| Grant Organization-level Read | Broad record visibility |
| Secure a column | Record access does not automatically grant column access |
| Hide a form field | Does not enforce server-side authorization |
| Use an application user | Permissions follow the application’s Dataverse security configuration |
Each experiment should record the security configuration before execution.
Without that evidence, a failed assertion may be difficult to explain.
23. Architectural Lessons
APP-010 establishes several important engineering principles.
Authentication and authorization are separate. A valid token does not grant unrestricted Dataverse access.
Privileges and privilege depth are different dimensions. The ability to perform an operation must be evaluated together with the applicable record scope.
Record access can have multiple sources. Ownership, roles, teams, and sharing can combine.
Sharing does not replace baseline privileges. A user must have the necessary table privilege.
UI restrictions are not server-side security. Hidden fields and commands do not substitute for Dataverse authorization.
Security testing requires multiple identities. Testing exclusively as System Administrator conceals important defects.
Unexpected access is a critical test failure. A security laboratory must treat unintended authorization as seriously as functional errors.
24. Microsoft Learn Technical References
- Security concepts for developers — Privileges, access levels, and effective roles.
- Security and data access — Dataverse security architecture.
- Data operations and access rights — Privileges required by operations.
- Sharing and assigning — GrantAccess, ModifyAccess, RevokeAccess, and assignment.
- Verifying access in code — Access inspection through SDK and Web API.
- Access teams and owner teams — Team collaboration and ownership.
- Manage Dataverse teams — Team administration.
- Security roles and privileges — Security role configuration.
Conclusion
Dataverse security is a layered authorization system.
The effective ability to perform an operation depends on the requested privilege, applicable access depth, ownership, security roles, team membership, sharing, and other relevant platform mechanisms.
A Model-Driven App can present or hide operations, but the underlying security boundary is enforced by Dataverse.
The most reliable way to understand this model is to test it under controlled identities and inspect actual service responses.
The central engineering principle is:
Security must be verified through effective permissions and real operations, not inferred from interface visibility or role names.
APP-010 establishes the foundation for advanced experiments involving plug-in impersonation, Custom APIs, application users, integration identities, and enterprise security architecture.
Power Platform Engineering Academy — Five Years in Five Days
Article 010/100 — Dataverse Security Laboratory
Identify the principal. Inspect the privilege. Execute the operation. Verify effective access.
