A Dataverse instance rolling out CRM for a single country with 50 sales reps is a three-week project on security configuration. Two or three business units, four security roles, done.
A Dataverse instance supporting a multi-country business - six countries, 1,200 reps, regulators in three of them who require data residency proof - is a different problem. The security model has to enforce country-level data isolation, allow cross-country collaboration for global accounts, support regulators pulling audit logs, and still let a reseller from the central marketing team generate a campaign list across regions without a support ticket per request.
This is the model we ship for multi-country deployments, the decision framework that produced it, and the gotchas that nearly took it down in production.
The constraints
Before choosing an architecture, we enumerate:
- Data residency. Which countries have laws requiring customer data to stay within national borders? These countries may need entirely separate Dataverse environments, not just separate business units.
- Data isolation. Are there countries where sales reps should not see accounts from other countries at all? Who decides what counts as "their country" - account-country, rep-country, or deal-country?
- Cross-country collaboration. Which accounts are global and need to be accessible from any country? Think multinational customers served from multiple regional teams.
- Compliance and audit. Who needs to pull an audit trail, and across what scope? A country regulator usually wants their country only; a global compliance officer wants everything.
- Admin boundaries. Can a local admin in Country A modify Country B's data? Usually no.
The combinatorial answer is not "more business units." It is often "two environments plus a careful BU design in the main one."
The two-environment pattern for data residency
For any country with strict residency requirements (some EU member states, specific financial regulators), the cleanest pattern is a separate Dataverse environment hosted in the required region. Microsoft's multi-geo Power Platform and Azure compliance surface make this viable without multiplying engineering work excessively.
Within each environment, the CRM schema is the same. Deployments from the same Azure DevOps pipeline target both environments. Users in residency-scoped countries only ever touch their environment; users with cross-country responsibilities have accounts in both.
The cost is real: two environments mean two licenses, two deploys per release, two audit trails to ingest centrally. The benefit is absolute: data in the restricted country literally cannot traverse to the other environment without an explicit integration you control.
Within a shared environment: the business unit tree
For the rest of the countries (where data residency is not a legal constraint), a single Dataverse environment with a business unit tree works well. Our pattern:
Three levels: Global HQ → Region → Country. Users sit in the country BU that matches their work location. Records are owned by users (via their country BU) or by teams (we will get to those).
Why three levels and not flat?
- Regional rollups. A regional VP has read access across their region's countries. One security role, BU-scope at the regional level, covers this.
- Organizational clarity. The BU tree matches how the business thinks about its own structure. Users intuit access from org chart.
Why not deeper than three levels?
- Security role scopes multiply. Every added level adds reasoning complexity to "can user X see record Y?"
- Reorg cost. When the business merges the Middle East into EMEA central, you restructure the BU tree. Fewer levels means less to restructure.
Cross-country shared accounts: owning teams
The trap: "global accounts that multiple countries share" modeled as BU ownership forces an arbitrary choice ("which country owns this global account?") that breaks on first disagreement.
The pattern: model a global account as owned by a team that has members from each serving country. Teams are cross-BU; they can own records; their members inherit the team's roles.
An example: Acme Corp is a global customer served from US, UK, and Japan. Create a team acme_corp_global_team at the root BU. Add the US, UK, and Japan account managers as team members. Assign the team read/write privileges (via a role at organization scope). Transfer ownership of the Acme Corp account row to the team.
Result: all three account managers see and can edit the account; none of them "own" it individually; the organization is reflected accurately; a new country joining later just adds a member.
Row-level security for edge cases
Business units and teams handle 95% of access. The remaining 5% is things like:
- "Deals above $500K are visible only to the sales VP regardless of BU"
- "Accounts flagged confidential are hidden from standard roles"
Dataverse's answer is positional filters via security role scopes combined with row-level filter expressions. In practice, we push these edge cases out of the security role model and into an application-level rule via Power Fx or plugin logic - "on form load, check flag; if confidential and user does not have role X, redirect."
The reason: complex row-level filters in security roles are hard to reason about and harder to audit. Application-level rules are explicit, testable, and live in source control.
The roles we ship
Regardless of country count, we use the same canonical role set:
RoleScopePurposeacme_read_only_countryBUReader access to country's recordsacme_standard_user_countryUserCreate/edit own records in countryacme_power_user_countryBUEdit/assign any country recordacme_read_only_regionParent:ChildRegional reader across countriesacme_power_user_regionParent:ChildRegional edit across countriesacme_organization_adminOrganizationFull access, for sys adminsacme_integrationOrganizationFor service principals, not humans
Seven roles. They compose via team membership or direct assignment. A country sales rep gets acme_standard_user_country; a regional VP gets acme_power_user_region; a global exec gets acme_organization_admin (minus delete privileges on key tables).
We resist the temptation to create "Sales Rep - Finance" or "Sales Rep - Customer Service" role variants. Those are application-level concerns and belong in the application's logic, not in the security role.
Audit and compliance
Dataverse's audit log captures field-level changes on opted-in tables. For multi-country:
- Enable audit on every business-critical table from day one. Retrofitting is painful.
- Configure retention per country's compliance requirement. Dataverse's built-in audit retention is configurable to 6+ months; beyond that, export to an external archive.
- Role-scope audit read access. A country compliance officer has a role that grants audit read on that country's BU only, not globally.
The common ask is "pull the audit log for all changes by user X in 2025." Dataverse's audit API supports this filter; the performance at scale (millions of audit rows) is the gating factor. For volume audit queries, we export to Azure Data Lake or similar and query with SQL/Synapse rather than hammering the Web API.
Implementation phases
A six-country rollout we shipped this year ran:
- Week 1-2: constraints interviews with each country's stakeholders, legal review for residency, decision on two-environment vs single-environment.
- Week 3: BU tree design, role matrix, team design for cross-country accounts.
- Week 4: pipeline configuration, integration testing of deploys across both environments (residency + main).
- Week 5-6: user provisioning, migration from legacy system, UAT per country.
- Week 7: go-live, country-by-country with a one-week gap between each.
- Post-launch: monthly access review for the first quarter.
The thing that pulls this into focus: don't design in isolation. A country lead catching "this BU structure won't work for our account merges" in Week 2 costs hours to fix. In Week 7, it costs weeks.
What we would still change
After running this model for eighteen months, the one thing we would revisit: the team-based ownership for global accounts works, but the team membership maintenance (adding/removing reps as they change territories) is ongoing overhead. The newer Dataverse feature for dynamic team membership via Azure AD groups is something we are piloting now; if the pilot holds, it collapses that maintenance to AD group changes.
The security model is never "done." It evolves with the business. The goal of the initial design is to produce a model that bends cleanly when the business changes, instead of breaking. That is the real deliverable.