M365Con 2027 – free online conference · 11–22 Jan 2027 · 120 sessions→ m365con.net

2026-10-31

The Dataverse Security Model Explained for Architects

The Dataverse Security Model Explained for Architects
Dataverse security looks simple until business units, teams, roles and column-level rules start to interact. In this episode of M365 FM, we walk through the building blocks of the Dataverse security model and show how they combine in real solutions: environments, security roles, business units…

Show notes

Dataverse security looks simple until business units, teams, roles and column-level rules start to interact. In this episode of M365 FM, we walk through the building blocks of the Dataverse security model and show how they combine in real solutions: environments, security roles, business units, teams and record ownership. You will learn how to design access that is least-privilege by default, avoid role sprawl and keep the model understandable as your Power Platform estate grows. Who it is for: Power Platform architects, solution architects, Dataverse developers and security-minded admins. Become a supporter of this podcast: https://www.spreaker.com/podcast/m365-fm-microsoft-365-azure-cloud-power-platform-ai--6704921/support. This episode includes AI-generated content.

Apple Podcasts · YouTube · Deezer

Host: Mirko Peters

Transcript

Read the full transcript

M365 FM - The Dataverse Security Model Explained for Architects 00:00:00 Speaker: Most teams treat Dataverse security like a checklist. Someone asks for access, you find the right role, you assign it ticket closed. But that's not what Dataverse security is. It's a structural model and a checklist can't run a model. So here's the thesis. A Dataverse security model only scales when it's designed around ownership and hierarchy, not around individual access requests. Because every request you grant one by one is a decision the structure should have made for you. We'll go through it in order. Business units, security roles, teams, record ownership, column security, hierarchy sharing, and then how you design all of it so it doesn't collapse under its own weight. If you want the structural view of the platform, not the feature tour, subscribe. But first, a quick word from the sponsor sponsor segment. Stellar Migrator for exchange. This episode is sponsored by stellar Migrator for exchange, and it connects to today's topic more than you'd think. When you migrate mailboxes, you're not just moving data, you're moving permissions, ownership boundaries around who can see what delegates, shared, mailboxes, folder rights, all of it has to land correctly. Or the security model you had on the old side quietly breaks on the new side. Stellar migrator for exchange is a migration tool. It moves mailboxes between exchange environments, on premises or online, and it's built for admins and consultants who run tenant moves, mergers or consolidations. That's it. It does one job and the job is moving things without losing the structure around them. You'll find the link in the description. Now back to Dataverse and the question that starts everything. The question Dataverse actually answers. Ask most people what Dataverse security does and you'll hear this. It controls who can open the app. That's wrong. Or at least it's the wrong layer. Opening an app is the easy part. Dataverse security answers a different question which rows can this user see and why not? Which screens not which menus rows. Every access decision in Dataverse is a join. Three things meet who you are, where you sit, and who owns the row. That's the whole mechanism. Identity, position, ownership. Change any one of them and the answer changes. Now compare that to the older model most of us grew up with. In the old model, you had permission lists per object. This group can read this table, that group can edit that form. You maintain the lists, and when someone needed something new, you added a line simple, visible, and it scales exactly as far as your patience does. Dataverse works differently. It doesn't ask only can you touch this table. It asks two things what privilege do you hold and what scope does that privilege reach? Privilege plus scope. You might have read access on accounts. Fine, but read access to which accounts your own, your teams, your business units, everyone's. That second question is where the model lives and it's the one most designs never answer on purpose. So why do architects get this wrong? Because they start at the app screen. They open the model driven app. They look at the forms and the views. They ask who needs to see this? And they work backward from the screen to a role that feels natural. It's also backwards. The screen is the last thing in the chain. It's just a window. The data was already decided by the time it showed up there. So you design from the data outward. Who owns these rows? Which boundaries separate them? Which groups of people sit inside which boundary, then the roles and then the app almost as an afterthought when you flip the direction, a lot of problems just don't happen. Now, a note on how to listen to this because you can't see a diagram. Hold one mental flow in your head for the rest of the episode. Identity. First, who is this user, then privilege. What are they allowed to do to this kind of table? Then scope. How far does that reach then the record? Does this specific row fall inside that reach identity, privilege, scope, record. Every concept we cover plugs into one of those four steps. If you ever lose the thread, come back to that sequence. And scope is the step that causes most of the damage. So that's where we start. Because scope isn't a setting, it's defined by structure. The four layers of the model Dataverse security isn't one thing. It's four layers stacked on top of each other, and each one answers a different question. Layer one is the environment. This is the outer boundary. Before a user can touch a single row, they have to be allowed into the place where Dataverse lives. If they're outside that wall, nothing else. We talk about matters. Layer two is the organization structure, business units, teams, users. This is the shape of your company. As the platform sees it, it decides where a person sits and that position becomes the scope we talked about a minute ago. Layer three is privileges. These are the security roles. A role says what you can do to which kind of table and how far that reaches create, read, write, delete. And for each one, a depth layer, four is the refinement layer. Record level controls and column level controls a single row shared with a single person. A salary field that most users can't read. These don't build access from scratch. They adjust what the first three layers already produced environment structure, privileges, refinement. Now how do these layers combine? This is the part people get wrong. So listen closely. Access in Dataverse is the union of what each layer grants. It's additive. Your role gives you read at business unit depth. Your team gives you write on a different table. A colleague shares one row with you. You get all of it together. And here's the rule that follows. From that, no layer can take away what another layer gave. There's no deny in the privilege model. You can't write a role that says everything except this. You can only decide what to grant, and then keep the total grant small enough to live with. That changes how you think. You're not building walls inside a big open room. You're deciding one grant at a time. How much of the room anyone gets to see. There is one real exception, and it's worth naming now so it doesn't surprise you later. Column security works differently. It can mask a value even when everything above it says the row is visible. We'll get there. But notice what it is. It's a restriction on a field, not a revocation of a grant. The row access stays only the value is hidden. So what typically goes wrong? Someone gets a ticket. I can't see this record. And the first move is to open the user's security role and start adding privileges. But the role might be fine. The user might be in the wrong business unit. The row might be owned by a team outside their reach. The environment might not even have them in the right security group. When you debug at the wrong layer, you fix the symptom and break the model. You bump the depth to organization level, the ticket closes and now that person can read every row in the table. Nobody notices until audit time. So the habit to build is simple. Before you touch a role, ask which layer actually failed. Was it the wall, the structure, the privilege, the refinement? The answer is almost never the one you reach for first. And since every layer rests on the one before it, we start at the bottom with the foundation that decides who gets in at all the environment, boundary environments, and security groups. The outer wall. Let's start with what an environment actually is. An environment is a container one Dataverse database with its own tables, its own rows, its own users, its own security setup. Nothing inside it leaks into another one. Nothing from outside reaches in unless you build a bridge on purpose. So the first security decision isn't a role, it's which environment the data lives in. And that decision is easy to miss because it happens. Before anyone opens a designer, someone creates an environment to try something out. A month later, real customer data sits inside it. The wall was never designed. It just showed up. Now who gets past that wall? Here's where the security group comes in. When you create an environment, you can attach one Microsoft Entra security group to it. Only members of that group can become users. In that environment, everyone else is blocked at the door no matter what licenses they hold. Leave it blank and the door stays open. Any licensed user in the tenant can be added. That's a real choice, and for most production environments, it's the wrong one. Because an open door means your boundary is the license list. And the license list answers a billing question, not a security one. The group approach flips that access to the environment, becomes a membership decision owned by whoever manages the group. Add someone to the group and they can be provisioned. Remove them and they lose the ability to get in one detail worth knowing. The group controls who can be added as a user. It's not a replacement for roles. Being in the group gets you inside the wall. It grants nothing. Once you're there, that part belongs to the next three layers. Now think about separation. Most teams here dev, test and production and file it under application lifecycle management build here. Validate their release over there. That's true. But it's also a security strategy. Your developers need broad access in dev. They create tables, edit roles, break things on purpose in production. Almost none of them should hold more than read access. If that different environments let you give different people different reach without writing a single exception. And test data matters too. If testers work against a copy of production data, then your test environment is a production grade secret in a lower grade room. Separate environments let you control that mask the data, scope the group, or don't copy it at all. Next provisioning. Because this one feeds directly into ownership. Later, when a licensed user from the security group is added to the environment, Dataverse creates a row for them a system user row. It sits in a table with a business unit, a manager field, and an owner relationship to everything they'll ever create. So a user isn't just a login, a user is a record and records can own other records. Hold on to that. When we reach ownership, it explains why a departed employee can still hold thousands of rows. So where do teams skip this? Usually on the first environment someone is moving fast, the group field looks optional and they leave it empty. The environment grows, apps depend on it. Nobody wants to touch the setup now. Then audit shows up and asks a simple question who is able to get into this environment? And the honest answer is anyone with a license? There's no group to point to, no owner of the list, no evidence that someone approved it. That's what skipping the wall costs. Not a breach necessarily. Just an answer you can't give. So the wall is set. Now inside it we make the first structural choice that shapes everything else business units, business units explained. Here's the definition. And it's the one most people never hear. A business unit is a security boundary. It's also a data scope. It is not a box on an org chart. That difference matters. An org chart answers who reports to whom. A business unit answers a different question. Whose rows can this person reach and whose can't they? Every Dataverse environment starts with one business unit already in place, the root. It gets created with the environment. It carries the name of your organization and it sits at the top of the tree. You can't delete it. You can rename it, but it stays. Every other business unit you ever create hangs somewhere underneath it, directly or through other units. So the tree always has one trunk and everything else is a branch. Now why does the tree shape matter? Because the tree feeds access depth. In a moment we'll get to the depth levels in detail. But here's the link to hold on to. When a role grants access at business unit depth, the platform looks at the business unit where the user sits when it grants access. At parent child depth, the platform looks at that unit and every unit below it, below it. Not beside it, not above it. That direction is the whole mechanism. Access reaches down the tree, never up and never sideways. So a user at the root with parent child depth can reach everything, because every unit is a child or a child of a child. A user in a leaf unit with the same setting reaches only that leaf. Same role, same privilege, completely different reach decided by where the person sits. That brings us to the fact that drives most design trade offs. Every user belongs to exactly one business unit one, not two. Not primary and secondary one. You can move a user from one unit to another, but at any moment they sit in a single place. And that place is what the platform uses to calculate their scope. Think about what that forces you to decide. Say you have a person who supports two regions in a spreadsheet. You just list both. In Dataverse they sit in one unit so you have a choice. Put them in a higher unit that contains both regions, which widens their reach, or keep them in one region and cover the other by some other means, like a team or a share. Neither answer is free. That's the trade off. And you can't dodge it because the model has no way to put one person in two places. Most of the pain you'll see later in this episode traces back to this single constraint. Teams that don't respect it end up bending everything else to compensate. Now the second tie rolls. Every security role belongs to a business unit. Two when you create a role, it's created inside a specific unit and it flows down. Child units inherit a copy of the role, so the same role can be assigned to users anywhere beneath the unit where it was defined. That inheritance is useful. You define a role once near the top, and every unit below can use it. But notice what it means. A role has a home, and a user can only be assigned roles that exist in their own business unit. When you move a user to a different unit, their existing role assignments don't follow them. The roles are tied to the old unit, so the user ends up with none until you assign the matching ones in the new place. Plenty of admins learn that the hard way a person changes teams on Monday, and by Tuesday they are opening a ticket because the app looks empty. So pull the pieces together. One tree with a single root access that reaches down the branches. Users who sit in exactly one place, roles that live in a place and flow down from it. It's a clean model as long as the tree is shaped well. And that's the problem, because the shape of that tree is the first thing most teams get wrong. And it's the reason so many designs fall apart when the company grows. Why business unit trees break at scale? Here's what almost every team does. First, they get the requirements. They open the org chart and they rebuild it box by box. Inside Dataverse, sales becomes a business unit under it, North America and Europe under those inside sales and field sales, then finance, then HR, then each region gets its own copy of finance because someone might need to separate them later. It looks tidy. It matches the slide deck. Leadership already approved, and it feels like the safe choice because everyone recognizes the shape. But that's the org chart. Trap. The tree was copied from a document that answers a different question. The org chart says who reports to whom. The security tree has to say, whose data is walled off from whose. Those two things overlap only by accident, and the accident doesn't last. What typically happens is a reorg. Maybe it's a merger, maybe a new regional structure. Maybe someone decides inside sales and field sales should roll up together. In a normal company. That's a PowerPoint change and a few emails. In Dataverse, it's a rebuild because the business unit tree wasn't a picture of the company, it was wired into access. Every role sits inside a unit. Every user sits in one. Every row carries the unit of its owner. So when the org chart moves, the security structure has to move with it, and everything attached to it has to be checked. Start with the people. You move a user from one business unit to another. Simple enough in the admin center, but the rows that person owns carry an owning business unit, and that doesn't automatically stay in sync the way you'd hope, depending on how the move is done and how your relationships cascade, those rows can end up owned by someone in a different unit than the one the row claims to belong to. Or they stay behind with the old unit while the user sits somewhere new. Now picture what that does to access a manager in the old unit at business unit depth still sees those rows. The user who owns them has moved on. Their new colleagues at the same depth can't see them at all. The data is right where it was and the people are somewhere else. Meanwhile, the users roles don't follow them. We covered that. So on the day of the move, they arrive in the new unit with nothing assigned and a pile of rows they still own in the old one. Then there's reparenting. This is when you take an entire business unit and move it under a different parent. On paper, it's one drag in the tree. In practice, it changes the answer to every parent child question beneath it. Anyone with parent child depth above the new location now reaches this unit and everything under it. Anyone who had that reach from the old parent loses it. Rows haven't changed, owners haven't changed. But who can read them? Just did. For every user in two branches of the tree. And nobody tested that because nobody thought a drag in a diagram was a security change. That's the real cost, not the effort of the rebuild. It's the silent ripple access shifts in places no one is looking. And you only find out when someone sees a record they shouldn't or can't see when they need. So what's the rule? Business units model data boundaries, not people. Boundaries. People move. Departments get renamed. Reporting lines change every year or two. Data boundaries are different. They come from law, contracts and regulation, and they change rarely, usually with a lot of warning. If the tree follows the path that rarely changes. Reorg stop being security events. They become role assignments and team memberships, which are cheap to change and easy to review. That points to a different way to start before you create a single business unit, don't ask what the company looks like. Ask one question and the answer to it decides whether the unit should exist at all. Designing business units around data boundaries. Here's the question does this group of people need to be walled off from another group's data? That's the test, not do they work in a different department? Not do they have a different manager walled off? As in, if one group could read the other's rows, something bad would follow. If the answer is yes, you have a candidate for a business unit. If the answer is, well, they just do different work, you don't. That's a job for roles and teams and we'll get there. So what counts as a real wall? Three things come up again and again law, regulation and contracts. A legal wall is the cleanest example. Two companies under one umbrella each a separate legal entity, each with its own customers and its own obligations. Their data can't mix even by accident. A business unit per entity gives you a boundary the platform enforces for you. A regulatory wall looks similar. Maybe one division handles health records and the rest of the company doesn't. Maybe one handles payment data. The rule isn't. Most people shouldn't see this. The rule is only a defined group. May a unit around that group makes the boundary structural instead of a habit, and. Contracts matter more than people expect. A client says in writing that only their dedicated staff may touch their data. Now you've promised a wall to someone outside the company that's a boundary worth building into the tree, because you'll have to prove it later. Notice what these have in common. Each one comes from outside the org chart. Someone else set the rule and it won't change because of a reorg. Now, geography. Because this one trips people up. Regions feel like boundaries. They have names, budgets, leaders. But a region is only a business unit if the data has to stay apart, if a sales team in the North and a sales team in the South can legally and sensibly see the same accounts, then splitting them is just decoration. You've added a wall nobody asked for and you'll pay for it every time someone needs to work across it. Data residency changes the answer. If rules say one country's records can't be reachable by staff elsewhere, that's a wall built. The unit subsidiaries follow the same logic. A subsidiary that shares customers and systems with the parent probably doesn't need its own unit, a subsidiary that's legally separate with its own books and its own audit probably does. Then there's depth. Keep the tree shallow two levels three at most. That covers most real organizations, including large ones. Every extra level multiplies what parent child depth can reach, and every extra level is another place where a reorg can break something. A tree five levels deep is almost always an org chart in disguise. If you find yourself building one, stop and ask which of those levels is a real wall? Let's make this concrete picture. A company with three subsidiaries call them the Nordic Company, the German company, and the UK company. Each one is a separate legal entity with its own customers, its own contracts, its own auditors, and alongside them there's one shared service team, finance, operations, IT support and a customer care desk that handles requests for all three. How do you model that? Start with the walls. Three legal entities means three real boundaries. So the tree has the root and under it three business units, one per subsidiary. Each subsidiary staff sit in their own unit and their rows stay inside it. Now the shared service team. It's tempting to give it a fourth unit as a peer of the other three, but think about what it does. It needs to work across all three subsidiaries data. A unit beside them can't reach sideways. Access only flows down. So you place the shared service team at the root. From there, with parent child depth, it reaches every subsidiary beneath it one position, one role, full coverage, and no sharing or workarounds. Three subsidiary units, one team at the top. That's the entire tree. Two levels, and every box on it exists because a wall demanded it. Everything else the departments, the regions, the project groups gets handled somewhere else, not in the tree. The structure is set. Now comes the layer that runs through it and decides what each person can actually do inside their place. The security role. 00:18:22 Speaker: Security roles, privileges as a matrix. A security role looks like a list. It isn't. It's a grid. Down the side. You have tables across the top you have actions. And in every cell where those two meet, there's a setting that says how far the action reaches. That's the whole thing. A set of privileges, each one aimed at a table, each one carrying a depth. So when someone says, this user has the sales role, what they really mean is this for every table in the system, there's a row of settings and the user gets whatever those settings say. Let's name the actions because you need them in your head. Create the user can make a new row read. The user can see a row, right? The user can change a row delete, the user can remove one, then three. That sounds simple and aren't append, append to and assign and one more share. Assign is the easy one. It lets the user hand a row to a different owner. Share is also direct. It lets the user grant someone else access to a specific row, which will spend real time on later, but append and append to. Those are the ones that confuse people. Here's why most admins assume every privilege is about the row in front of them. Read means I can read this right means I can edit this. So append must mean I can add to this. Not quite. Append and append two are about relationships. Take a contact in an account. You want to link the contact to the account. That link is a connection between two rows and it touches both of them. So the platform checks two things. Does the user have append on the row being attached. And does the user have append to on the row it's being attached to? Append is the right to attach this row to something else. Append two is the right to have things attached to it. Both sides have to say yes. If either one is missing, the link fails and that explains a classic ticket. A user can open an account, they can edit it, they can open the contact too. But when they try to link the two, they get an error that says they lack permission. Nothing looks wrong with read or write. The missing piece sits on one side of the relationship in a privileged. Nobody thought about. So when you build a role, you aren't only deciding who sees what. You're deciding which rows can be connected to which. That's a different kind of access, and it's easy to forget until something breaks. Now there's a second group of privileges, and these don't belong to any table. They're called miscellaneous privileges, and they gate features. Export data to excel, run a bulk delete. Publish a customisation, use the mobile app. Create personal views. Go offline. Each of these is a switch and each one sits outside the table grid. They matter because they're quiet. A user might have perfect table access and still hit a wall, because a feature they need is turned off somewhere else in the role or the reverse. A user has a switch they shouldn't like export, and now every row they can read can also leave the building as a spreadsheet. Nobody reviews those cells as carefully as the table ones. They should. One more rule and it shapes everything about design roles are additive. A user can hold several roles at once. When they do, the platform doesn't pick one. It merges them for each privilege. On each table, the user gets the highest depth any of their roles grants. So if one role gives, read at user level on accounts and another gives read at business unit level, the user reads at business unit level, the wider grant wins always. That means you can't build a restrictive role and expect it to hold. If the same person also carries a broader role from somewhere else, the broader one takes over. There's no way to say except here, which is why role design is really about restraint. You decide what to give and you keep the total small. And once you see the grid, one question remains. Every cell has a setting. What are the settings? That's depth. And it's where a role gets its real power, access levels and depth explained. Every privilege in that grid carries a setting. Four of them are scopes. One of them means no access at all. None. Parent child business unit, business unit. User organization. Dataverse shows these as small circles in the role. Editor. Empty quarter. Half, three quarter full. People call them the Harvey Balls and the picture is accurate. The fuller the circle, the wider the reach. Let's go through them from the narrowest up. None means none. The privilege exists in the grid, but it's switched off. The user can't perform that action on that table no matter what else they hold. User depth is the narrow one. At user level, you reach rows you own, and you also reach rows that were shared with you directly or shared with a team. You belong to your own work, plus whatever someone handed you on purpose. That's it. A colleague in the same unit doing the same job owns rows you can't see at user depth. They might as well work in another company. Business unit depth widens the circle. At this level, you reach rows owned by anyone in your business unit, not just your rows. Everyone's. As long as they sit in the same unit you do. Notice what the platform checks here. It doesn't ask who you work with. It doesn't ask who you report to. It looks at the business unit on the row and compares it to the unit you sit in. Same unit you're in different unit, you're out. And this is where the earlier design work pays off or doesn't. If your units model real world business unit depth gives each world's occupants a shared view of their own data. If your units copy the org chart, you get arbitrary clusters of people who can see each other's rows for no reason anyone remembers. Then comes parent child depth. At this level, you reach rows in your own business unit and in every unit beneath it children and children of children all the way down. This is the setting that rides the tree. A person in the middle of a branch sees their own unit and everything below. A person in a leaf sees only that leaf. Same privilege, same role. Completely different reach. Because the position changed. And remember the direction down. Only a user with parent child depth can't see up to the parent and can't see across to a sibling. If a unit sits beside yours, it's invisible, even though you share a parent. Last is organization depth. Every row in the environment, regardless of owner, regardless of business unit, regardless of anything we've covered so far, everything we've built up to this point the tree, the walls, the units stops mattering. A privilege at organization depth ignores all of it. So here's why. It's the most dangerous setting in the product. Because it works. That's the problem. A ticket comes in. I can't see this record. The admin checks the role, finds read at business unit depth and bumps it to organization. The user refreshes the record appears. Ticket closed in under a minute, but the fix didn't solve the problem. It removed the structure that caused it. That person can now read every row in the table across every unit for every customer. And the same is true for right. If the admin bumped that to nobody sees the damage, there's no error and no alert. The user just quietly holds more than they should and the model has no record of why do that a few dozen times over a few years, and you end up with a tree that looks carefully designed and enforces almost nothing, walls drawn on a diagram, and a hundred people holding keys to every door. So treat organization depth the way you treat a master key. A small number of people need it admins, integration accounts, maybe a data steward or two. Everyone else should be able to explain in one sentence which wall they work inside. If the only explanation is it was faster, that's the answer you came to fix. Now step back because there's a pattern across all five levels. Look at what each one compares. User depth compares the owner on the row to you. Business unit depth compares the unit on the row to yours. Parent child compares that unit to yours and everything below. Organization compares nothing at all. Every one of those comparisons points at the same thing. The row itself. Specifically two facts stored on it, who owns it and which business unit that owner brought along. Depth is just a ruler. It only measures something because every row carries that information. Without an owner, there's nothing to measure against. So the next question isn't about roles at all. It's about the row record ownership, the quiet center of everything. Open any row in a user owned table, you'll find two columns. Most people never look at owner and owning business unit. The owner is either a user or a team. That's the full list. A row can't be owned by two people, and it can't be owned by nobody in a user owned table. Somebody always holds it and that somebody is the anchor for every depth calculation we just walked through. Think about what that means for user depth. Rows you own isn't a loose idea. It's a literal match. The platform compares the owner column to your identity or to the teams you belong to. And if they line up, you're in. Now, the second column owning business unit. Nobody types this one in. When a row gets an owner, it also picks up that owner's business unit. The two travel together as a pair stamped on the row. At the moment of ownership, if a user in the Nordic unit creates an account, the account carries the user as owner and the Nordic unit as owning unit, and that pairing is what business unit depth reads and never asks where the creator works. Today it reads the unit written on the row so the row carries its own scope. That's the quiet part. You can't see it on a form. It doesn't show in the app, but every access decision in the system runs through those two values. And when they're wrong, everything built on top of them is wrong too. There's a catch, though. Not every table works this way. Dataverse has a second table called Organization Owned. Here, rows have no owner at all. No user, no team, no owning unit. The row simply belongs to the organization as a whole. So how does security work there? At the table level, a user either holds the privilege on that table or doesn't. There's no depth to apply because there's nothing on the row to compare against. Read is read for everyone who has it across every row. That's a perfect fit for some data reference lists. Currency codes. Product categories, settings, tables, anything the whole company needs. And nobody should be fencing off row by row for that kind of data. Ownership is just overhead. But here's the part architects miss for anything sensitive. Anything tied to a customer or a case or a person. Organization owned means you've given up the entire scope model. No walls, no units. No depth. One switch for the whole table. Now, the part that raises the stakes. You choose the ownership type when you create the table, and after that you can't change it. There's no setting to flip, no migration button. If you build a table as organization owned, and a year later someone says, we need to restrict these rows by region. The answer is rebuild new table, new relationships. Data moved across every app and flow and report pointed at the replacement. The reverse isn't kinder. A user owned table you wanted to keep open will carry owner and unit values on every row, and every role that touches it needs a depth. Fine. You can set everything to organization level, but now you've built a model you never meant to use. So this is one of the few security decisions that's close to permanent, which is strange because it gets made in the first ten minutes of modeling. Someone clicks through the table creation form, picks the default, and moves on to adding columns. The default matters. Custom tables come in as user owned unless you say otherwise, and most teams never say otherwise. So what's the design habit? Decide ownership type deliberately and decide it with security in mind before you name a single column. Ask one question will anyone ever need to see some of these rows and not others? If the answer is yes, or even maybe make it user owned. Owners, units. Depth. All of it comes with the table. If the honest answer is never, this is shared reference data. Organization owned is cleaner and cheaper. Get that wrong in either direction and you'll find out later when it costs the most to fix. And once you accept that every row has an owner, a different question shows up. Owners aren't permanent. People leave roles change, work gets handed around. What happens to the row when its owner changes, assign, reassign and the ownership trail changing? An owner has a name in Dataverse. It's called assign. When someone assigns a row, the owner column changes and the owning business unit changes with it. Because the two travel as a pair, the row picks up the new owner's unit the moment the handover happens. That's a quiet security event. A row that sat inside one wall yesterday can sit inside another today, and every depth calculation follows it. Now. Rows rarely live alone. An account has contacts, opportunities, cases, notes. So the platform has to decide what happens to all of those when the parent row changes hands. That decision is the cascade behavior and it's set on each relationship. Five actions get a setting, assign, share, Unshare, Reparent, and delete. Each one can be set to cascade all, which means the action flows down to every related row active, which limits it to open rows. User owned which only touches Rose owned by the same person or none, which means the children stay exactly where they are. Most teams treat these as convenience settings pick the default, move on, but look at what they control. A sign with cascade all means one click hands over a whole tree of rows, including ones the new owner was never meant to hold. Share with cascade all means a single share grants access to every child quietly, and you won't see the extra grants anywhere obvious, and the reverse is just as risky. Set assigned to none and the account moves to a new owner while its cases stay behind with the old one. Now one customer's data sits across two people, possibly across two business units. Nobody planned that split. It's just what the setting did. So Cascade configuration is a security decision. It decides how far a single action reaches through your data. Make it on purpose, relationship by relationship, and write down why. Now, the problem that shows up every year in every environment. People leave. When a user is disabled, their system user row stays. So do all the rows they own. Nothing moves. Nothing gets reassigned. The owner column still points at someone who can't sign in. Call it the disabled user problem. Those rows sit in a unit nobody works in anymore. Owned by an account. Nobody watches. Managers at user depth can't see them because they don't own them. People at business unit depth might still reach them if the unit is still stuffed or nobody does. And customer records go dark. A deal with no owner, a case with no one working it and it compounds. Leave it a few years and the largest owner in your system is a pile of former employees. The fix isn't a cleanup project. It's a pattern. Reassign on Offboarding as a workflow, not a manual task. When someone leaves, the process triggers automatically. It finds every row the departing user owns, hands them to a defined owner, usually a team or a manager, and only then lets the account be disabled. The order matters reassign first, disable second. Once the account is off, finding and moving, everything gets harder and the destination matters too. If rows moved from one departing person to one remaining person, you've just built the same problem with a longer fuse and that person will leave eventually and the same rows go with them, which points at a different kind of owner. One that doesn't quit, doesn't change units, and doesn't take rows with it. When it goes the team owner teams and access teams, two different tools. Dataverse has more than one kind of team and they don't do the same job. Start with the owner team. It's the straightforward one. You create it, place it in a business unit and give it a security role. After that, it can do two things a user can do. It can hold privileges and it can own roles. That second part is what sets it apart. An owner team can sit in the owner column of a row. Its business unit becomes the owning business unit. So the team doesn't just have access to data, it holds the data in the same sense a person does. Now the access team, different tool, different purpose and access team doesn't own anything and doesn't carry a role. It exists for one reason to grant access to specific rows. You attach it to a single record, add some people and those people get the rights defined in a template. Read, write, append whatever the template says. The team lives only as long as that collaboration needs it. Think of the account team on a big customer for people need to work one account. They don't share a unit and their roles don't reach it, and access team gives them rights to that role and only that row without touching the model underneath. So the owner team is a container for data and And privileges. The access team is a lightweight way to open one door. Then there's a third option, and it solves a different problem entirely. Who maintains the membership with a regular owner team? Someone adds and removes members by hand in the admin center, one person at a time. That works for a dozen people. It stops working at a few hundred, and it quietly breaks when someone changes jobs and nobody updates the team. A Microsoft Entra Group team fixes that. You link the team to a group in your directory. Membership isn't managed in Dataverse anymore. It follows the group. Someone joins the group and they're in the team. The next time they sign in, they leave the group and they're out. That moves the decision to where identity is already governed. Access reviews, joiner and leaver processes, approvals. All of it lives in the directory. Dataverse just reads the result. Now there's a detail inside that option. You pick the group type, and there are two security groups and Microsoft three hundred sixty five groups. Security groups are the usual choice for access control. They exist to hold people for permissions. Microsoft three hundred sixty five groups come with a mailbox, a shared site, a team's workspace. They're built for collaboration, and they often grow on their own because people create them for projects. Using one as a security source works, but you inherit whatever its owners decide to do with it. You also choose a membership type members only, which means just the people directly in the group owners, which means the groups, owners, members and owners, which covers both or guests, which includes outside users who were invited into the directory. That last one deserves a pause. If you pick a membership type that includes guests, you've decided that outside identities can pick up data versus access through a group they may not know exists. Sometimes. That's right. Most of the time it should be a deliberate choice, not a default left on. So which tool do you reach for? Ask one question does this team own data or does it just need to see it? If rows should belong to the group and stay put when people come and go, you want an owner team usually backed by an intra group. If a handful of people need access to a specific row for a while and nothing about ownership should change, you want an access team? Get that question wrong, and you end up with owner teams that only exist to grant visibility or access teams quietly doing the job of an owner. But picking the team is only half of it. A team holds privileges differently than a user does, and that difference changes what the people inside it can actually do. Team roles and effective permissions. Assign a security role to a team, and every member of that team picks up those rights at forty people to the group, and forty people get the privileges. Remove one and that person loses them the next time the session refreshes. That's the appeal. You stop touching users one by one, and the role sits in one place where you can read it. But there's a setting on the user that decides how those team rights actually behave. It's called the member's privilege inheritance, and it has two options. The first is team privileges. Only under this one, the user holds whatever their own roles give them and also whatever the teams roles give them. But here's the catch the team privileges can only be used on rows the team owns or rows shared with the team. When this user creates something, it belongs to them and their own roles decide what they can do with it. The second option is direct user access level and team privileges. This one is the default. The users own roles apply, and on top of that, the teams roles apply to to the users own work as well. Team rights stretch over everything the person touches. Now watch what that does to creating an owning rose. Say a user has no create privilege on accounts in their own roles. Just read. But their team holds a role with create own accounts under the first setting, that user still can't create an account as themselves. The team's right to create only works for Rose. The team owns the user has to create them on behalf of the team. By setting the team as owner under the second setting, the same user can create an account and own it personally. The team's role filled the gap in their own. So one checkbox changes whether people can own Rose as individuals. And that matters because ownership is what depth reads. A row owned by a person follows that person. A row owned by the team follows the team. Which brings us to why an owner team works as a container when a team owns the row. Nothing about that row depends on anyone's employment. The owner column points at the team. The owning business unit is the team's unit. A person leaves, gets disabled, moves to another city. The row doesn't care. It stays where it was with the same scope visible to everyone who reaches that team's data. Compare that to what we covered earlier rose owned by a departed user. Go dark and you need a workflow to rescue them. Rose, owned by a team. Never leave in the first place. The off boarding job gets smaller because there's less to move. So there's a pattern here, and it's worth stating plainly teams own the data, users hold the work. The team is the permanent home of the record. The user is whoever is working on it. Today. That split matches how organizations actually behave. A customer relationship belongs to the company, not to the rep who happens to manage it this quarter. In practice, it means designing so that new rows land in a team's ownership, not a person's a form that sets the owner to the right team on creation, a flow that routes incoming cases to a queue team instead of whoever clicked first, and a user setting that lets people work on team data without needing to own it. Do that and reorg. Shrink. People move between teams and their access moves with them. The rows don't move at all, so the structure is doing its job. Business units wall off the data roles, decide the actions, depth sets the reach and teams hold the rows steady. All of that works at the level of the whole row, but some rows have one field in them that shouldn't travel with the rest a salary, a tax number, and for that scope control is too blunt. We need a finer grain. The column. 00:37:21 Speaker: Column level, security explained. Think about what's inside a single row, a contact, a name, an email or a phone number, and then three columns down a salary or a tax ID or a note about a medical condition. Everything we've built so far protects the row as a whole. If you can read the contact, you can read every column on it. The scope model has no way to say this row, but not that field. That's the gap column. Security fills. It protects the fields where one wrong viewer is a real problem. Compensation figures, national ID numbers, bank details, health data, anything where the row is fine to share but one value inside it isn't. Now how do you set it up? Three steps and the order matters. First, you mark the column as secured. It's a property on the column itself, a single switch in the column settings until you flip it. Nothing changes. The column behaves like every other one. Second you create a field security profile. A profile is a named set of rules, and for each secured column it holds, it says what that profile allows. Third, you assign people to the profile users or teams. Anyone who isn't assigned gets nothing on that column. Not a reduced version. Nothing. That last point trips people up. The moment you secure a column, you've closed it for everyone, even people who could read it yesterday, including admins who don't get a pass here. System administrators can see secured columns only if a profile gives them access, just like anyone else. You open it back up deliberately, one profile at a time. Each profile can grant three permissions on a secured column read. The person can see the value update. The person can change an existing value. Create. The person can set a value when the row is first made. They're separate and the split is useful. Picture an HR coordinator who enters a salary when a new hire record is created, but can't change it afterward. Create yes, update no or a manager who needs to see the number but never touch it. Read only you build the profile to match the job, not just a yes or no. So what does someone see when they don't have access? Not an empty field, not a missing one. The column is still there on the form in the view in the grid, but the value shows as a row of asterisks masked, not hidden. And that's deliberate. The user can tell there's something in the field they just can't read it that keeps forms from looking broken and it tells the person where to go if they need it. But it also means the column's existence isn't a secret. If the name of the field itself is sensitive, column security won't hide it. Now the limits because there are a few and they matter more than the setup. It doesn't apply to every column type. You can secure most single value fields text numbers, dates, currency lookups, but some types can't be secured, and the primary name column of a table is one of them. Check the specific column before you design around it. Don't assume the second limit is the one that makes column security worth trusting. It applies to every access path the form, the view a report and export the web API. A flow reading the data, a plugin, a power BI query running through Dataverse. The check happens at the platform level, so it doesn't matter which door someone uses to reach the value, the masking follows the data. That's what separates it from hiding a field on a form. Remove a column from a form, and anyone with a view or an API call can still read it. Secure the column and the form. The view and the API all return the same masked result. So on paper column security is clean. Three steps, three permissions one consistent behavior across every path. In practice, it fails in a different place, not in the platform in how people use it. Column security design patterns and pitfalls. Not in the platform. In the habits around it. The most common failure is profile sprawl. Here's how it starts. Someone in payroll needs to see salaries. A profile gets created for payroll a week later. A regional director needs the same field but only read new profile. Then an auditor needs read access for a month, new profile, then a project lead, a contractor. A team that merged with another team. A year on, you have forty profiles. Each one has a name that made sense to whoever typed it. Nobody knows which ones overlap, which are stale or who's in them. Ask who can read salary, and the honest answer takes an afternoon of clicking. That's one profile per request, and it's the road to a model nobody can explain. The fix is to change what a profile represents. Don't build profiles around teams or people. Build them around data sensitivity. Think of classes, a profile for confidential compensation data, a profile for personal identifiers, a profile for health information. Each one maps to a kind of data, and each one answers a single question who is allowed to touch this class? Then the work flips. When a new person needs access, you don't create anything. You add them or their team to a profile that already exists. When a new sensitive column appears, you decide which class it belongs to and add it to that profile. The list of profiles stays short because the list of sensitivity classes stays short. Three or four profiles can cover most organizations. If your past ten something is being modeled by request. Now a quieter risk. Secured columns don't stay on forms, views, reports, and exports all pull the same data. A masked value in a grid is fine, but think about an Excel export, a power BI data set, or a flow that copies the value into another system column. Security protects the Dataverse. Read once a person with access copies the number somewhere else, the mask is gone and your control ends at the border. So review what leaves not just what's displayed. The people holding read access on a secured column are the real perimeter. Keep that group small enough to name then testing. And here's a habit worth building. Don't assume the profile works, check it. The Check Access tool lets you pick a user and see what they can actually do, including which secured columns they can read, update, or create. It takes a minute. Skipping it means you find out from the user, usually at the worst moment. One last point and it matters for the whole design column. Security is a refinement. It's never a substitute for getting the table right. If half the columns on a table need locking, the table is probably two tables. If one team should never see another team's rows at all. That's a business unit and a depth question, not a field question. Column security narrows what's visible inside rows. You've already decided someone may reach it. Can't fix a model that let them reach the wrong rows. Use it for the few fields that truly need it. Keep the profiles few and keep them tied to the data, not the org chart. Which brings us back to the tree, because there's a second way to extend access along it, one that doesn't depend on where someone sits, but on who they report to. Column security, design patterns and pitfalls. The platform rarely fails here. The habits around it do. The most common one is profile sprawl. It starts. Small. Payroll needs to see salaries so someone creates a payroll profile. A week later. A regional director needs the same field read only. So there's a second profile. Then an auditor needs access for a month, then a contractor, then two teams merge and nobody cleans up the old ones. A year on, you have forty profiles, each named by whoever was in a hurry that day. Ask who can read salary. And the honest answer takes an afternoon of clicking. That's one profile per request. It's the road to a model nobody can explain. The fix is to change what a profile stands for. Don't build it around a team or a person. Build it around a class of data, a profile for confidential compensation, a profile for personal identifiers. A profile for health information. Each one answers a single question who may touch this kind of data? When a new person needs access, you don't create anything. You add them or their team to a profile that already exists. When a new sensitive column shows up, you decide which class it belongs to and place it there. The list of profiles stays short because the list of sensitivity classes stays short. Three or four cover most organizations. If your past ten. something is being modeled by request. Now a quieter risk. Secured columns don't stay inside. Forms, views, reports, and exports all pull from the same table. A masked value in a grid is fine, but picture an Excel export. A power BI dataset or a flow that copies the number into another system column security protects the read from Dataverse. Once someone with access moves the value somewhere else, the mask is gone and your control stops at the border. So the people holding read on a secured column are the real perimeter. Keep that group small enough that you can name every member and look at what leaves the platform, not only what shows on screen. Then there's testing. And here the habit is simple. Don't assume the profile works. Check it. The check access tool lets you pick a user and see what they can actually do. That includes which secured columns they can read, update, or create. It takes about a minute. Skip it and you'll hear about the mistake from the user, usually at the worst possible moment, and usually because the profile was assigned to the wrong team three months ago. One last point and it shapes the whole design column. Security is a refinement. It's never a substitute for getting the table right. If half the columns on a table need locking. The table is probably two tables. If one group should never see another groups rows at all, that's a business unit and a depth question, not a field question. Column security narrows what's visible inside rows. Someone is already allowed to reach it. Can't repair a model that let them reach the wrong rows in the first place. So use it for the few fields that truly need it. Keep the profiles few. Tie them to the data, not the org chart. Which brings us back to the tree, because there's a second way to extend access up it. And this one doesn't depend on where a person sits. It depends on who they report to. Hierarchy, security managers and positions. Business unit depth works by place where row sits in the tree decides who reaches it. Hierarchy security works by line. Who reports to whom, decides the reach, and the tree barely matters. Dataverse gives you two versions of this manager hierarchy and position hierarchy. They solve the same problem and they get there in different ways. Start with the manager hierarchy. Every user row has a field called manager. It's a lookup to another user. That's the whole data source. Fill it in for everyone and the platform can build a reporting chain out of those links. Now turn the feature on and here's what changes. A manager gets access to the rows their direct reports can reach. Not rows they own. Not rows in the same unit. Rows that belong to the people beneath them, and it flows up a person's access passes to their manager. If that manager has a manager, the next link in the chain can reach it to up to a limit you set. Reach goes up the line, never sideways to appear. That limit is the depth setting. It's a number and it says how many levels of reports a manager can see. Set it to one and managers see only their direct reports. Rows set it to three and they see reports, reports of reports and one level beyond that. Pick that number with care. A depth of two or three covers most real needs. Push it higher and a regional head can see rows owned by someone five layers down who they've never met. That's rarely what anyone intended. Now, the second model, the position hierarchy. This one doesn't read the manager field at all. Instead, you define positions regional sales lead sales manager, account executive. You arrange those positions in a tree with parents and children, and then you assign users to them. Access flows up through the positions. Someone sitting in a parent position reaches the rows of people sitting in the child positions beneath it. Same depth setting. Same upward direction. So why have both? Because the manager field describes individuals and individuals. Change. Someone leaves. Someone is acting in a role for three months. Someone reports to two people on paper and one in practice. The manager field has to be edited every time per person a position stays put, the sales manager position exists whether prior fills it or Marcus does move a person in or out, and you change one assignment, not a chain of reporting links for organizations where roles are stable, but people move around. Positions hold up better, but positions carry their own costs. Someone has to define the tree, keep it current, and assign every user to a spot. It's the second structure to maintain beside the first. Now the part that catches almost everyone hierarchy doesn't grant access on its own. It rides on top of a privilege. Here's what that means. The manager's security role needs read on the table, and it needs it at user depth at least y user depth. Because hierarchy extends what user depth already does. It takes rows I own and widens it to Rose. My reports own. So if the manager's role gives read at none. Hierarchy has nothing to extend, and if their role already gives business unit depth, hierarchy adds little because they already reach those rose by place. The classic ticket reads like this. The admin turns on manager hierarchy, sets the depth of two, and tells the managers to try it. Nothing changes. Every manager still sees only their own rose. The cause is almost always the same. The role has the privilege set wrong, or the table was never switched on for the feature. Because hierarchy is enabled per table, not globally. So three conditions have to line up. The hierarchy model is on the role, grants user level access on that table, and the manager data or the position assignments are actually filled in. Miss anyone and you get silence. No error, no warning, just access that never arrives. That fragility is worth keeping in mind because hierarchy isn't always the right tool. Sometimes it's exactly what a sales organization needs, and sometimes it quietly pulls in the wrong people. So when does it earn its place and when is it the wrong answer? When to use hierarchy security. Start with where it fits. Sales organizations. Service organizations, anywhere. The work is owned by individuals, and the people above them need to see the role up. A sales manager wants the pipeline of their team. Not by place, not by unit, but by who works for them. A service lead wants the open cases of the agents they supervise. The question those managers ask is, what are my people working on? And that's a question about a line, not a location. In that kind of organization, hierarchy does something business units can't. You don't have to build a unit per team. You don't have to hand out wider roles. You fill in who reports to whom. Set a depth and the reach follows the chain. When a rep changes, managers one field changes and the access moves with it. Now the poor fit matrix organizations, the ones where a person reports to one manager for their career and works for someone else on a project, or two people with the same title in the same unit who need completely different data. Here, the reporting line says nothing about the data. A person should reach the manager field records who approves vacation. It doesn't record who should see a customer's contract, turn on hierarchy in that setting, and managers pick up rows because of an HR relationship, not because of any real data need. It's access. By coincidence, you'll recognize the symptom. Someone asks why a director can read a deal in a division they've never touched, and the answer is that three levels down someone's manager field points the wrong way. Then there's cost. Hierarchy isn't free for every query. The platform has to work out who sits beneath the user, and that gets heavier as the chain gets deeper. A shallow hierarchy is cheap, a deep one with thousands of users and wide depth settings adds work to retrieval, and you feel it in views and reports that used to load quickly. That's one more reason to keep the depth number small. Two or three levels answers most real questions. Anything beyond that is usually buying visibility. Nobody asked for and paying for it in speed. And here's the dependency that gets less attention than it should. The whole feature runs on data quality. Manager hierarchy reads one field on the user row. If that field is wrong, the access is wrong. A person who left a team six months ago still shows under the old manager, and that manager still sees their rows. A new hire with no manager entered, sits outside the chain and nobody above them sees anything. So who owns that data? That's the question to settle before you switch the feature on. In many companies, the manager field gets filled once during provisioning and then nobody touches it. HR changes the reporting line in one system. Dataverse never hears about it. If you go this route, tie the field to a source that's actually maintained, sync it from the directory or from HR and give someone a name next to it. Otherwise, your security model quietly depends on a field that no one is responsible for. Now put the two side by side because the contrast is useful. Business unit depth is by place. It asks where the row sits in the tree and where you sit and compares the two hierarchy. Depth is by line. It asks who reports to whom and follows that chain up. One reads structure, the other reads relationships and they answer different questions. So a design can use both as long as you know which question each one is solving. But even a good tree and a good chain leave gaps. Sometimes one row needs to reach one person, and nothing about their unit, their role or their manager would ever put it there. For that. There's a different mechanism. You don't change the structure, you make an exception and you share the row sharing the exception mechanism. Sharing is simple to describe. You pick one row, you pick a person or a team and you give them specific rights on that row and nothing else. No unit changes, no new role, no depth setting touched. The structure stays exactly as it was, and one row gets a door cut into it. The rights you can grant are the same ones from the privileged grid. Read, write, delete, append, append to assign and share, which is the right to pass the row along to someone else. You choose them one at a time. Someone gets read and write but not delete. Someone else gets read. Only a third person gets read plus the right to share it onward, which is where things start to slip out of hand. Because now the person you trusted can open the same door for anyone they like. And here's the part that makes sharing different from everything else we've covered. It's additive and it's quiet. A share can only add. It never takes anything away. A user who already reaches the row through depth, through hierarchy, through a team keeps all of that. The share just stacks one more path on top. Nothing marks it either. No change shows up on the form. The role doesn't change. The business unit tree looks the same if you open the security model in the admin center, the share isn't there. It lives on the row and you only find it if you go looking at that one row. That's why it accumulates at design time. You can't see a share because no share exists yet. Six months later, a rep shares an account with a colleague. A manager shares a case with a specialist. Someone shares a whole set of opportunities before going on leave. Each one takes seconds. Each one solves a real problem for a real person, and none of them leaves any trace in the model you designed, so the design stays clean on paper while the data fills up with exceptions. Now add cascading. Remember the cascade settings on relationships. The ones that decide how far an action travels share is one of the five. Set it to cascade. All and one share on an account reaches every related row the contacts, the opportunities, the cases, the notes. A single click on the parent hands, access to a whole tree of children and the person doing the sharing may not know how many rows that is, and it multiplies the other way to one share on one account. With cascade, all through three levels of relationships can create hundreds of access entries. Nobody asked for hundreds. The setting did that quietly on every share made from then on. So what does sharing look like when it's healthy? Rare. It's the exception. It was named for a one off request, a temporary handover. A specialist pulled in for a single deal, a few shares here and there, each with a reason you could explain in a sentence. And what does it look like when it isn't? You ask how people get access to customer rows and the answer is they get shared. You ask what happens when a new person joins the team and the answer is someone shares everything with them. You ask how a user lost access last month, and nobody can say because the share was removed by hand and nothing recorded it. That's the warning sign. If sharing is your main access path, the model is wrong, not the sharing the model underneath it. People are sharing because the structure doesn't put the right rows in front of the right people. The unit is drawn in the wrong place or the depth is too narrow, or the data belongs with a team and sits with the person. Sharing is just the symptom people reach for to get the work done. But sharing has a second problem, and this one has nothing to do with design. It has to do with what each share costs to store, and what a few million of them do to the platform. 00:54:52 Speaker: The cost of sharing at scale. Every share is a row, not a flag on the record, not a setting on the form. A real row in a real table. The principal object access table platform people shorten it to POA. Each entry holds a few things. Which row is shared, who it's shared with a user or a team, and which rights were granted. One share. One entry. Share an account with a team that's one entry. Cascade all pushes that share down to forty related rows. That's forty more. So the count doesn't track how many times people clicked share. It tracks how many rows each click ended up touching, and those numbers drift apart fast. Now why does that matter? Because the platform reads that table constantly. When a user opens a view, the platform has to decide which rows they can see. Depth and hierarchy are cheap to check. They compare values already on the row, but shares live somewhere else to include them. The query has to look into the POA table, match entries to the user and to every team they belong to, and fold the result into the answer. With a few thousand entries, you'll never notice. With millions you will. Views that used to open in a second. Start to drag reports. Time out. A security check that was trivial becomes the slowest part of the request. And it hits everyone, not just the people who hold shares, because the lookup happens on every read against that table. Teams usually meet this problem the same way. Nobody warns them. A system that ran fine for two years starts feeling heavy. Support tickets say the app is slow and the performance work finds nothing wrong with the app. The cause is sitting in a table nobody designed. So where's the line? There isn't one clean number, and anyone who gives you one is guessing. Microsoft's guidance is to keep the number of shares per row small, and to avoid sharing a single row with a large number of principals, teams tend to feel real pain once a single row has hundreds of shares or. Once the whole table passes into the millions. Treat those as warning lights, not limits you plan around if you're getting close. You've already waited too long. The better question is how to avoid needing shares in the first place. And there are three answers. The first is to let a team own the row. If a group of people always works the same set of records, make that group an owner team and make the team the owner. Access comes from one place and the team's role and its depth. No per row entries add a person to the team and they reach everything the team holds without a single share being written. The second is the access team, and this one fits when the group is small and the collaboration is tied to one record the account team on a big customer. A deal with a specialist pulled in access teams are built for exactly this, and they grant access through a template so the pattern stays consistent instead of being rebuilt by hand each time. They do write entries to. But they're bounded and they're managed as a team, not as scattered one offs. The third is the hardest. Restructure the business units. If people keep sharing across a boundary, the boundary may be in the wrong place. Sometimes the right fix is moving the wall, not cutting more doors in it. Now, if you already have the problem, here's how to dig out. Start with an audit. Find which tables hold the most shares, and which users or teams show up most often as the recipient patterns appear quickly, the same two teams receiving the same kind of row over and over. That's a team that should own the data or a unit boundary that's drawn wrong. Then replace the pattern with structure. Build the owner team, move the rows into it and remove the shares it makes redundant. Don't delete shares first, fix the structure, confirm access still works and then clear the entries. Do that table by table and the table shrinks while the model gets easier to read. That covers each tool on its own, but Access and Dataverse isn't decided by any one of them. The platform checks them in a fixed order, and once you can walk that order, you can explain almost any result. How Dataverse evaluates an access request. Every access decision runs the same sequence, same order every time for every row. Once you know it, you stop guessing. A user says, I can't see the account, and instead of opening five screens at random, you walk five steps. Step one is the privilege. Does this user hold read on this table at all? Not read on some rows. Read in any of their roles on the table itself. If no role gives it, the walk ends here. Depth. Hierarchy shares. None of it. Gets a vote. A share. Can't rescue someone who has no privilege on the table to begin with, because the platform checks the privilege first and the row second. This is the cheapest step to check, and the one people skip. Most often they jump straight to the row and wonder why nothing explains the result. Step two is depth. The privilege exists now at what level? Here the platform compares the row to the user. It reads the owner on the row and the owning business unit that travels with it. Then it asks whether that owner or that unit falls inside the user's reach. User depth means the user owns it. Business unit depth means the owning unit matches theirs. Parent child means the owning unit is theirs or sits below it. Organization means everything passes. If the row lands inside the reach, the user gets in and the platform has its answer. If it doesn't. The walk goes on. Step three is hierarchy. Did the users reach just get longer? This only applies if the feature is on for that table, and only if the role gives at least user depth. The platform then looks down the reporting chain or down the position tree up to the depth limit. Does the rows owner sit beneath this user in that chain? If yes, access. If the chain is empty because the manager field was never filled in. Nothing extends and nothing tells you why. Step four is sharing everything structural has failed to let the user in. Is there an exception? Now the platform goes into the principal object access table. It looks for an entry on this row that names the user. Then it looks for entries that name any team the user belongs to. One match is enough, and the rights on that entry decide what the user can do or share with Reed gives Reed. It doesn't give right. Even if the role says right is allowed at some other depth, that last point matters. A share grants only what it lists. Step five is the last one, and it doesn't decide whether the user reaches the row. It decides what they see once they're in column security for every secured column on the row. The platform checks the users profiles. No profile or no read permission, and that value comes back. Masked. The row opens one field shows asterisks. So the full order is this privilege depth hierarchy shares columns. The first four are gates and passing any one is enough. The fifth is a filter gates grant and the filter trims. That distinction clears up a lot of confusion because people try to explain a masked field by looking at depth, and depth has nothing to do with it. Now why does the order matter so much? Because each step answers a different question and a failure looks the same. From the outside. The user sees an empty grid or a missing row, or an error. They can't tell you which step failed. Neither can the screen. So you walk it. Does the role hold the privilege? If yes, at what depth does the rose owner fall inside that depth? If not, is hierarchy on and does the chain reach it? If not, is there a share? And if there in is a column masked five questions in order. And the answer is almost always in the first three guessing skips that people add a role, widen the depth, or share the row just to make the ticket go away. And each of those fixes quietly bends the model to cure a false nobody located. The walk only works if you can see each step. And for that, the platform ships a tool. Diagnosing access problems. The tool is called Check Access. You'll find it on the user's record or on a specific row. Pick a user, pick a table and the platform tells you what that person can do. Read, write, delete, append, assign, share. Each one comes back as a yes or a no. But the useful part is the reason for each write. It shows where the access came from. A security role, a team, a share, a hierarchy. So you don't just learn that someone can read the row, you learn which gate, let them in, and when the answer is no, the same screen tells you there's nothing behind that. Write no role grants it, or the role grants it at a depth that doesn't reach this row. Read it the way you'd walk the five steps privilege first, then depth, then everything that came after. Most problems show up in the first screen and they fall into four patterns. The first is the most common. The privilege exists, but the depth is too low. The user holds read on accounts. The role looks fine, but the depth is set to user, so they see only rows they own and the account they need belongs to someone else. Check access shows, read granted at user level and the rows owner isn't them. The fix isn't to add a role, it's to ask whether this person should reach by unit and widen the depth to match on that one role. The second failure shows up after a move. Someone changes departments and their user record gets a new business unit. Suddenly they can't see the rows they worked on yesterday. Or worse, they can see rows from the old team that they shouldn't. Why? Their roles were tied to the old unit and business unit depth now compares against a different place. Their old rows still carry the old owning unit, so depth no longer reaches them. Check the user's unit first and then the unit on the rows. Those two values rarely match by accident. The third failure lives on the user, not the role. It's the privilege. Inheritance setting. A person is in a team and the teams role gives create on cases, but they still can't create one in their own name. The cause is the member's privilege inheritance set to team privileges only under that setting, the team's rights apply to rows the team owns, not to rows the user owns. Check access will show the privilege coming from the team. The setting is what limits it, and you only see that by opening the user. The fourth failure surprises people because the main table looks perfect. A user can read the account, they can write to it, but when they try to add a contact to it or link a note, the platform says no. The reason is append and append two. To attach one row to another, you need append on the row you're attaching and append to on the row receiving it. Miss either one on either table and the relationship fails. Even though both tables look healthy on their own, check access shows each. Write separately. Look at both sides of the link for failures and notice what they share. None of them needed a new role, a new unit, or a new share. Each one had a cause you could find in under a minute, and each fix was small once the cause was visible. That's the value of diagnosing before changing anything. Otherwise, every ticket becomes a patch and the patch has become the model. But walking the steps only repairs what's already built to stop the same tickets from coming back. You design the pieces so they don't rot. And that starts with the part. Everything else depends on the role's role design. That doesn't rot. Most role problems start on day one. Someone opens the system administrator role or salesperson or basic user and starts changing privileges to fit the project. Don't the out of box roles belong to Microsoft? They get updated when the platform adds tables, and your edits sit on top of changes you didn't make and can't predict worse. Nobody can tell later which privileges were original and which were yours. The role looks standard. It isn't so you copy first. Every role you use is a clean copy of something or built from nothing, and it carries your name from the start. The originals stay untouched and they stay useful as a reference. When you need to know what standard looks like. Now, what should the copy look like? Small. Here's the instinct that ruins most models. A role gets built per job sales rep, senior sales rep, sales rep, Europe sales rep. Europe with quotes. Each one repeats ninety percent of the last, and each one has to be updated every time a new table appears. The alternative is composition one base roll held by everyone. It carries what every user needs. Read on the shared reference tables, the privileges to use the app itself, the miscellaneous rights that keep forms from throwing errors. Nothing about any single function. Then add on rolls. One per function. A sales role that holds the sales tables. A service role that holds cases and queues. A reporting role for the people who export a user gets the base plus whichever add ons their work needs. And remember, roles are additive. The user ends up with the union, so you never rebuild the base inside an add on each privilege lives in exactly one place. When something changes, you change it once, and everyone who holds that role gets the update on their next session. That also settles a question that otherwise causes arguments. Where does a new privilege go if everyone needs it? Base. If one function needs it, that functions role if it's for one person, stop because that's a different problem. Naming comes next and it matters more than it looks. A role name is the only documentation most admins will ever read. Name by function and scope. Sales, regional service agent finance. Read only each name says what role does and how far it reaches. Don't name roles after people because people leave and the role stays. Don't name them after projects because the project ends and the role out lives it with a name nobody can decode. Two years later, a role called Phoenix Access tells a new architect nothing. A good convention is short and boring. Prefix for the app or area, then the function, then the depth. If depth is the point. Anyone can read a list of fifteen roles like that and guess correctly what each one does. Now watch for the warning sign. Because roles drift even with good rules, it's role explosion and you can spot it with one test. Put two roles side by side. If they differ by a single privilege or by one depth setting on one table, they shouldn't be two roles. Maybe the difference belongs in an add on that only some people hold. Maybe it belongs in a column profile or in a team. Maybe it's a one off and the one off is the real issue. But two near-identical roles are a symptom. Each one will drift on its own, and in a year, nobody will remember which one was supposed to be right. If your role count climbs past what the functions in your business can explain, something is being modeled by request. Again, one more thing and it protects everything. Above roles are configuration and configuration needs a path. Put roles in a solution. Build them in the development environment, export the solution and import it into test, then production. Never hand edit a role in production because the next deployment overrides your change. Or worse, nobody notices that production no longer matches anything and keep the solution in source control. Unpack it. Committed and now a role change is a diff. Someone can review who added delete on contracts and when and why the history answers. Without it, you have a table of privileges and a shrug. That's the role layer, a small base, a few add ons, names a stranger can read, and a path that keeps environments honest. But roles only matter once somebody holds them, and the cleanest way to hold them in bulk is through teams. So the next design question is how to build those teams without creating a second mess? Team design for maintainability. Start with one question where does membership come from? If the answer is someone adds people in the admin center, you've built a second directory. It sits inside Dataverse and nobody upstream knows it exists. The alternative is the Microsoft Entra Group team. The team is tied to a directory group and membership follows the group. Someone joins the group, they land in the team. The next time they sign in, they leave the group. They drop out access stops being a Dataverse chore and becomes a side effect of identity governance, which already has approvals, reviews and expiry dates attached. That's why intra group teams should be your default owner. Teams and access teams still have their jobs, but for any team that holds roles for a stable group of people, let the directory drive it. Next. How many teams? Fewer than you think. The pattern that holds up is one team per business function and business unit combination sales in the Europe unit service in the Europe unit sales in North America. Each team says two things what work these people do and where their data lives. That pairing is what a role needs to make sense, because the role gives the privilege and the unit gives the reach, but only where needed. If sales in two units share the same role and the same reach, you don't need two teams. If a function only exists in one unit, one team covers it. Don't build the full grid because the grid looks tidy. Build the cells that carry real access and leave the rest empty. Now, the question that decides whether any of this survives who owns membership, not the platform team. They don't know who works in which function this quarter. They'll get a request, add the person and never think about it again. Six months on, nobody can say why half the members are there. The business owns it. The head of sales or someone they name decides who belongs in the sales group. The platform team owns the teams design, what roles it holds, what unit it sits in, what it's allowed to own. Two jobs, two owners. When those blur, you end up with a platform team approving access requests they can't judge. Put a name next to every group, not a mailbox, a person. If you can't find the owner of a group in 30s, that group is already drifting. Then there's the life cycle. Three moments and each one touches access differently. The joiner a new hire, gets added to the right directory group. On day one, the team picks them up, the roles come with it, and they see their work without a single ticket. If joiners still need a manual Dataverse step, the model isn't finished. The mover. This is where most models leak. Someone changes functions and gets added to the new group. Nobody removes them from the old one. Now they hold both, and since roles are additive, they hold the union. A year of moves and a person carries access from every job they've ever had. So the move has to be two actions, one in and one out. Tied to the same event, the lever. Remove them from the groups and the access ends. But remember what we covered earlier. The rows they own stay behind. A clean exit takes the access away and reassigns the work. Three events. One source of truth. That's what makes teams maintainable. They stop being something you edit and become something you inherit. All of that is pattern patterns are easy to nod along to. So let's put every piece in one company and watch where the design holds and where it strains a worked design. Putting the model together. Take a company with about two thousand people three regions North America, Europe and Asia Pacific, a finance team that spans all of them, and one shared service desk that handles internal requests for everyone. It's ordinary, which is the point. Nothing here is exotic. Start with the tree because everything hangs from it. The root business unit, then three children, one per region. That's the whole tree. Two levels. Now notice what's missing. There's no unit for sales. None for service, none for finance. Why? Because the test is simple. Does this group need to be walled off from another group's data regions do contracts, customers and local regulations split along those lines. Finance doesn't finance. People need to work across regions. So a unit would create a boundary the business doesn't want. Finance gets handled with roles and one column profile, which we'll get to. So the tree models three data boundaries, not four departments, not a reporting structure. Next roles three layers built the way we described. First, a base role. Everyone holds it. Read on the shared reference tables, the privileges to run the apps, and the miscellaneous rights that stop forms from erroring out. Nothing about sales. Nothing about service. Second, a regional sales role. Read and write on accounts, contacts and opportunities set at business unit depth. A rep in Europe reaches every sales row owned inside the Europe unit and nothing owned in North America. The wall is drawn by the tree and the role just says how far to reach. Third, a sales manager role. This one sits at user depth on those same tables, and that's deliberate. Hierarchy rides on user depth. So the manager role gives the floor and the hierarchy setting extends it down the reporting chain depth of two. A manager sees their reps and their reps, reps and no further. Managers hold the bass role, the regional sales role and the manager role reps hold the first two. Each privilege lives in exactly one place. Now the service desk where teams do the heavy lifting. The desk works, shared queues, tickets arrive, anyone on the desk picks them up and the work needs to outlast any one person's employment. So the queues are owned by owner teams. One per desk function rows belong to the team. A person works them through the teams role and their membership and membership comes from intra. Each owner team is an intra group team tied to a directory group. HR changes someone's job, the directory group updates and the team follows. When an agent leaves, their tickets stay exactly where they were because the team owned them all along. No reassignment run. No orphaned rows. Remember the setting on the member's privilege inheritance for agents, it's team privileges only. They don't create rows in their own name. The team owns the work and the user holds the task. Last piece finance compensation data lives on a few columns salary bonus and a pay grade. Those three get marked as secured and they all go into one field security profile named for the class of data they hold, not for a team. An intra group team of payroll staff gets read an update. A smaller group gets read only. Everyone else opens the same row and sees masked values. One profile, two assignments. If an auditor needs access for a month, you add them to the read group and you remove them in a month. That's the whole design. Three units, three layers of roles, a few teams driven by the directory, one profile. Now look at what it avoids. There's no business unit per department, so a reorg of sales doesn't touch the tree. No organization depth handed out to quiet the tickets. No sharing as the main path. No role per person. But don't mistake that for a design with no weak spots. Every model carries pressure and this one has three places where it strains. The first is the manager. Field hierarchy works only as long as that data is current. The second is the regional wall. The day a global account team needs to work one customer across all three regions, somebody will reach for a share. And the third is finance itself. It works today because the people are few. Grow that group tenfold and the model starts asking harder questions. Those pressure points are worth naming because they show up in almost every organization, and so do the mistakes that make them worse. 01:13:59 Speaker: Then anti-patterns that make security unmanageable. Five mistakes show up again and again. Different companies, different industries, same shapes. The first is the org chart tree. Someone opens a diagram of the company and builds it into the tree. Sales gets a business unit, marketing gets one legal HR, finance each with its own box. Then regions go underneath each department and teams go underneath the regions. It looks organized. It even feels like diligence. But a business unit is a data boundary and departments rarely are. So the tree fills up with walls nobody asked for. And then people spent their days cutting doors through them. Every cross-department task needs a share or a wider role, and the day the company reorganizes the tree has to be rebuilt with every user and every row moving along with it. The second pattern starts with a ticket. A user can't see a record. The admin checks the role, finds read at business unit depth, and does what's fastest organization depth. The ticket closes in a minute and the user is happy. Then it happens again and again. Within a year, half the sales team holds organization depth on accounts because that's the setting that never produces a ticket. Nobody decided that everyone should see every customer. It just happened. One quick fix at a time. The wall between regions is still on the diagram, but it no longer stops anyone. Third, sharing becomes the default. Remember what we said about sharing being quiet? This is where it bites. A team skips the structural questions because sharing is right there on the toolbar and it works instantly. Need a colleague on the deal? Share it. Someone's on leave. Share the whole list. New hire. Share everything they'll need. Six months later, the table behind it holds a few million entries, and nobody can say who has access to what or why. There's no model left. There's only a pile of exceptions, and the pile is the model. The fourth pattern is quieter, and it grows from the same instinct role per person profile per request. A manager asks for access like her colleague has, so the admin copies the colleague's role and changes one privilege. A contractor needs a field, so a new field security profile appears with his name on it. Each request gets its own artifact because that's the smallest change that ends the conversation. After two years, you have two hundred roles. Forty of them differ from another by a single privilege, and nobody remembers which is the original. Ask who should have access to salary data, and the answer lives in the head of one admin who's about to change jobs. And the fifth is about timing, not configuration. Security design done. After the app is built, the team builds the tables first, forms, flows, views a demo for the stakeholders. Security is the last item on the plan, so it gets the last week. By then ownership types are fixed and you can't change them. Relationships already cascade in ways nobody examined. Rows are being created under whatever access the developers happen to have, usually system administrator. So the model gets bent around decisions that were made. Without it, the tree gets drawn to fit the data when it should have been the other way around. Security becomes a repair job on a system that's already live. Look at what the five share. Everyone is a shortcut and each one is rational. At the moment it's taken. Nobody sets out to build something unmanageable. They solve today's problem with the nearest tool, and the cost arrives later in a form. Nobody connects to the original choice. So the useful question isn't whether these patterns exist in your environment. They almost certainly do. It's how bad they've gotten and how you'd know that takes numbers, how many rolls, how many shares, how many people hold organization depth, and it takes a habit that stops the drift from starting again. That habit is governance, and it's less glamorous than it sounds. Governance, auditing and change control. Most security models don't fail on launch day. They fail slowly through changes nobody reviewed. So start with a different way of looking at what you've built. Roles, field security profiles and team definitions aren't settings, they're artifacts. They have a shape and author and a history the same as code does. That means they live in solutions. You build them in development, export them, and the solution file is the thing you review. Someone opens a pull request that adds delete on contracts to the sales role. A second person reads it. Maybe they ask why. Maybe they ask whether the finance add on should hold it instead. That conversation takes two minutes and it's the cheapest control you'll ever have. Compare that to the usual path an admin opens a role in production, ticks a box and saves no review, no record of why, and the next deployment either overwrites the change or leaves production quietly different from everything upstream. Now review catches changes you plan auditing catches the ones you don't turn it on. Dataverse can record changes to users roles and team memberships, and it's off until someone enables it at the environment level first, then on the tables that matter. You want to know when a role gets a new privilege. When a user is added to a role. When a team changes hands without that log, the question who gave this person access has no answer, and keep the retention long enough to be useful. An access question from an auditor usually arrives months after the change, not days, but a log only records the past. It doesn't tell you whether today's access still makes sense. For that, you run access reviews on a schedule. Quarterly works for most organizations, and monthly for the sensitive roles. The review has two questions who holds each role right now and who approved it? A role with a holder and no approver is a finding. So is a holder who changed jobs in March and still carries the old functions access. Pull the list from the platform and each role to its business owner and ask them to confirm or remove. And that's the same ownership split we covered for team membership. The business knows who should have access. The platform team knows what the access means. Neither can do the other's job well. Then there's the path changes. Take dev test production in that order. Every time a security change is built in development, deployed to test as a managed solution and checked there with real test users, not an admin account. Then it moves to production through the same package. Untouched testing with an admin account is the classic mistake. Admins pass every check so the test proves nothing. Use accounts that hold only the roles a real person would hold, and walk the five steps for each one. The same rule protects you from drift. If production ever differs from what source control says, that's an incident, not a convenience. Last, make it readable because the person who inherits this model won't have your context. A new architect should be able to understand it in an hour. That takes a short document kept next to the solution, with a few plain things in it the business unit tree and why each unit exists. The list of roles, what each one grants and who owns it. The teams, their intra groups and their owners, the secured columns and their profiles, and the naming convention written down in a paragraph. If any of those takes a conversation with the previous admin to explain, the documentation isn't finished. That's governance at the level of the platform. Team roles reviewed, changes logged, access confirmed environments in step. But the people who fund this work don't read audit logs. They ask different questions and security design answers them in ways that rarely get said out loud. The executive layer what security design decides. Leaders don't ask about depth settings. They ask how long it takes to get a new team working, what a reorg will cost, whether they can pass the audit. Security design answers all three. It just doesn't say so. Start with onboarding. A new sales team arrives after an acquisition or a new region opens. In a model built on structure, the work is small a business unit. If the data needs a wall, a directory group, a team, roles that already exist days, not months. In a model built on exceptions, every new team means a round of shares, copied roles and tickets. The speed isn't set by how fast people work. It's set by how much of the access was designed before they showed up. Now the reorg. Every company has one, usually sooner than planned. Sales mergers with marketing, two regions become one. A division gets sold. A model built around data boundaries absorbs that. People move between directory groups and the access follows. The tree doesn't change because the tree never described the org chart, but a tree that mirrors the org chart breaks on contact units get reparented users move owned rows change hands. Somebody spends a quarter fixing access. That worked fine before the announcement, then audit. An auditor asks who can see salary data and why. A structural model answers in minutes one profile, two teams, each tied to a directory group with a named owner. You show the design and the design is the evidence. Without it, the answer is a spreadsheet. Someone builds by hand, which is out of date the moment it's finished, and it's usually missing the shares. Here's the newer pressure. Co-Pilot and agents don't bring their own security. They read through the same model as the user who asked the question. If a user can reach a row, so can the assistant working for them. It will find it, summarize it, and put it in an answer that changes the cost of a mistake. An organization depth role used to be a quiet risk because people had to know where to look. Now a plain language question surfaces everything. That role can reach. Access errors that sat unnoticed for years get found in an afternoon, and agents ask more questions faster than any person ever did. So the decision for leaders is about timing. The model costs the least before the apps multiply, each new app flow and agent leans on what's already there. Build on a clean model and each one inherits clean access. Build on exceptions and each one multiplies them. Fixing it later is possible. It just means untangling shares, roles, and units while the business keeps running on top of them. Which brings the argument back to where it started. Conclusion. From permissions to structure, a Dataverse security model scales when it's built on ownership scope and hierarchy, not on exceptions. Every share is an entry somebody has to explain. Every business unit copied from the org chart is a wall in the wrong place. Every one off role is a copy that will drift. None of them hurts today. All of them come due at the next reorg with interest. If this changed how you read Dataverse security, leave a review. It helps more architects find it. And if you want to keep going, follow me, Mirko Peeters on LinkedIn and send me your next topic. Because in the end, access in Dataverse is never granted by a screen. It's decided by the model behind it.

← Windows 365 or Azure Virtual Desktop: Choosing the Right CloMicrosoft Fabric Direct Lake - Simply Explained →