All articles

The Dataverse Security Model Explained Without the Microsoft Jargon

Business units, security roles, teams, field-level security, and record sharing — how they interact, where the sharp edges are, and how to design a security model that doesn't break your users.

· 13 min read

The Dataverse security model is powerful. It’s also one of the more complex things you’ll have to understand when building on the platform — because when it goes wrong, it goes wrong in subtle ways. Users see empty grids. Records disappear after assignment. Flows fail with generic permission errors.

This is a developer-focused reference for how the security model actually works.

The Hierarchy

Dataverse security is built on a hierarchy:

graph TD A["Organization"] --> B["Business Units"] B --> C["Teams"] C --> D["Users"]

Every user belongs to exactly one Business Unit. Business units can be nested. Every user also belongs to at least one Team (the default team of their business unit). Security roles are granted to users or teams — not directly to business units.

Security Roles: What They Actually Control

A security role is a collection of privileges. Each privilege has:

  • Table: which table it applies to (Account, Contact, your custom tables)
  • Action: Create, Read, Write, Delete, Append, Append To, Assign, Share
  • Scope: User, Business Unit, Parent: Child Business Units, Organization

The scope is the part people get wrong most often.

ScopeWhat the user can see/do
UserOnly records they own
Business UnitRecords owned by anyone in their BU
Parent: ChildRecords owned by anyone in their BU or any child BU
OrganizationAll records

“Own” here means the record’s Owner field. Most tables have an Owner field. When a user creates a record, they’re the default owner.

Append and Append To are the confusing ones:

  • Append gives you the right to attach a record to another record (e.g., adding a note to a case)
  • Append To gives you the right to have something appended to your records

To create a note on a case, the user needs Append on Notes and Append To on Cases. Both are required.

Teams and Their Types

There are three types of teams:

Owner teams can own records. A record’s Owner can be a team instead of a user. Owner teams are useful when a group of users should have shared access to a set of records without having to configure individual sharing.

Access teams don’t own records. They’re temporary collections of users that get access to a specific record through record sharing. Access teams are typically auto-managed using Access Team Templates on a table.

Microsoft Entra ID (AAD) Group teams are mapped to an Azure AD security group. Members of the AD group automatically get the security roles assigned to the team. This is the recommended approach for production environments — manage group membership in Azure AD, not Dataverse.

The Record Ownership Model and Its Implications

Every record is owned by either a user or a team. The owner has full control of the record (assuming their security role supports it). Other users’ access is determined by:

  1. Their security role scope (BU-level access, org-level access, etc.)
  2. Whether the record has been shared with them
  3. Whether they’re on an access team that has access to the record

The implication: if you change a record’s owner (via assignment), you may change who can see it. A user with “User” scope Read access will lose visibility to a record they previously owned once it’s reassigned to someone else.

This catches teams off guard constantly. A flow that reassigns records needs to account for this.

Field-Level Security

Field-level security (FLS) lets you restrict read/write access at the column level, independently of record-level access. Use it for sensitive data — salary information, SSNs, confidential notes.

To enable FLS on a column:

  1. In the table definition, mark the column as Enable column security
  2. Create a Column Security Profile that grants the desired access
  3. Add users or teams to the profile

FLS applies even if the user has org-level Read on the table. A user without a Column Security Profile for a secured column sees **** instead of the value.

Developer gotcha: Dataverse Web API and the SDK still return the column in queries, but the value is blank (or **** in some contexts) if the user doesn’t have the profile. Your code needs to handle this gracefully — don’t assume a blank value means the field is empty.

Sharing

Record sharing gives a specific user or team access to a specific record, with specific privileges. It’s a surgical tool for when the security role model is too blunt.

// SDK: Sharing a record
var grantAccess = new GrantAccessRequest
{
    Target = new EntityReference("account", accountId),
    PrincipalAccess = new PrincipalAccess
    {
        Principal = new EntityReference("systemuser", userId),
        AccessMask = AccessRights.ReadAccess | AccessRights.WriteAccess
    }
};
service.Execute(grantAccess);

Sharing is flexible but comes with a cost: it creates rows in PrincipalObjectAttributeAccess (POAA), which is queried every time a user accesses a record. In large environments with extensive sharing, POAA can grow very large and impact performance.

Design your security model to minimize sharing. If you find yourself writing flows that share hundreds of records, revisit the role and team design.

Hierarchical Security

If you need managers to see their reports’ records, hierarchical security is built in. Enable it at the organization level and configure whether the hierarchy follows the manager relationship or the business unit structure.

With position-based hierarchy, a user with Read at User scope and hierarchy enabled can read records owned by anyone below them in the hierarchy. This avoids having to grant BU-level access to managers who only need to see their direct team’s data.

Designing a Security Model

A good process:

  1. Identify record ownership. Who owns which records? Does ownership need to transfer? When?

  2. Identify access patterns. Who needs to read what? Who needs to edit? Who should never see certain data?

  3. Map to BUs. If your access patterns follow an org chart, your BU structure should too. If everyone needs org-level access, you might not need BUs at all.

  4. Design roles. One role per persona, not one role per use case. Roles compose — a user can have multiple roles and gets the union of all their privileges.

  5. Use AAD group teams. Map roles to AAD groups. Add people in Azure AD, not Dataverse. This integrates with your existing identity management.

  6. Minimize sharing and FLS. Use them where genuinely needed. Each adds complexity and performance cost.

  7. Test with actual users. The security model is notoriously hard to reason about from a design document. Create test users with the intended roles and verify they see what they should — and don’t see what they shouldn’t.

Common Mistakes

Giving everyone org-level Read “just to be safe.” This eliminates meaningful security segmentation and makes it impossible to use record-level security later. Start restrictive.

Creating a new security role for every minor variation. Five roles with clear scopes are better than twenty roles with overlapping permissions nobody can explain.

Ignoring the Owner field in flows. When a flow creates a record, it’s owned by the flow’s connection user (often a service account). If users need to own their own records, explicitly set the Owner field in the Create action.

Using sharing as a substitute for proper role design. Sharing is a patch. If you’re sharing everything to everyone, your roles are wrong.

If you’re designing a security model and want to visualize what each role can do, the Security Role Editor in XrmToolBox gives you a much clearer view than the built-in role editor. The built-in one works, but comparing two roles side by side is painful without a dedicated tool.

Share this article LinkedIn X / Twitter

Related articles