← Back to Blog

MIGRATION · April 14, 2026 · 10 min read

Cross-tenant SharePoint permissions without user-mapping hell

Everything else in a SharePoint site migration is solvable. Content types, list views, site pages, version history — there is a right answer for each, and with the right tool you get there. Permissions in a cross-tenant move are different. They are the one part where a naive tool will silently produce a site that looks fine and is actually half-broken, because the failure mode is “nobody can get in to check”.

This post is the honest playbook: why cross-tenant permissions are hard, how MigrationFox’s CSV mapping handles UPN translation, and what to do for the users who do not have a clean mapping. At the end we walk through a full example so you can see what the job actually produces.

Why cross-tenant permissions are hard

A permission in SharePoint is a triple: (principal, scope, role). For example: alice@acme.com has Full Control on /sites/marketing. For the destination tenant to reproduce that triple after migration, it needs all three parts to still be valid on the other side of the move.

The scope is usually fine — the site or library or item exists on the destination because we just migrated it. The role is fine — Full Control is Full Control. What breaks is the principal. In a same-tenant migration the principal is still the same directory object and Graph resolves it natively. In a cross-tenant migration the source principal is alice@acme.com, living in Acme’s Entra ID, and the destination site lives in New Company’s Entra ID where that principal simply does not exist.

There are four categories of principal you have to translate, and they each have different failure modes:

Any one of these categories, handled naively, can silently drop a quarter of your site’s effective permissions. The users who had access through the missing principals will call the helpdesk on Monday morning saying “I cannot open the shared drive”.

The worst permissions-migration outcome is not failure. It is silent success. A migration that reports “100% complete” with 30% of the source permissions quietly discarded is worse than a migration that explicitly fails, because the problem surfaces only when users try to work.

The CSV mapping model

MigrationFox’s cross-tenant permissions flow is anchored on two CSV files that you maintain by hand, because no tool can guess the right mappings for you. They are small, they are obvious, and they are the place where the human judgement lives.

user-map.csv

One row per source user, with the destination UPN they map to:

source_upn,destination_upn
alice@acme.com,alice.smith@newcompany.com
bob@acme.com,bob.jones@newcompany.com
carol@acme.com,carol.davis@newcompany.com
dan@acme.com,dan.miller@newcompany.com

A user in the source with no row in the mapping is “unmapped” and lands on the exceptions list. We do not silently guess based on fuzzy name matching, because the cost of getting that wrong is worse than the cost of surfacing it.

group-map.csv

Groups get the same treatment:

source_group,destination_group
marketing-team@acme.com,marketing@newcompany.com
finance-leads@acme.com,finance-leadership@newcompany.com
engineering@acme.com,engineering@newcompany.com

For groups, the destination membership is populated by whoever maintains your Entra ID — usually HR or IT via AD Connect, Okta, or an HRIS integration. The migration does not write group memberships. It only points the site’s permissions at the correct destination group; whether Alice is a member of marketing@newcompany.com is a directory question, not a migration question.

How the replay phase runs

Permissions are the last phase of a SharePoint site migration, after every other piece of content and schema is in place. The sequence for each site is:

  1. Enumerate source permissions. Walk the site’s role assignments, then every scope with unique permissions (libraries, folders, items) and capture the (principal, scope, role) triples.
  2. Resolve against the mapping. For each source principal, look it up in user-map.csv or group-map.csv. Mapped principals go to the replay list; unmapped ones go to the exceptions list.
  3. Pre-flight the exceptions list. Before any writes happen, the UI shows you every unmapped principal with the scopes they had access to and the role they had. You decide, per principal: drop it, map it inline, or invite as guest.
  4. Replay mapped permissions. Each triple is written to the destination against the mapped principal. 429s back off, transient errors retry, terminal errors land in the exceptions report.
  5. Emit the audit report. A CSV/JSON record of every permission written, every permission skipped, and the reason for each skip.

The pre-flight in step 3 is the part that keeps silent failures from happening. You cannot get to step 4 without making an explicit choice about every unmapped principal. The tool does not quietly decide on your behalf.

What to do for unmapped users

Three reasonable responses to an unmapped source principal, each appropriate in different situations.

Option A: Drop the permission

If the unmapped user is a leaver — they no longer work at the company and have no destination account — dropping the permission is correct. Their access was going to be revoked anyway. The exceptions report still records that they had access on the source, which you may want for compliance/audit reasons, but the destination permission simply is not written.

This is the default for anyone without a destination UPN. We do not invent an account, we do not leave a ghost assignment pointing at a deleted UPN. We record the fact and move on.

Option B: Map inline

If the unmapped user should have been in the CSV but got missed — a contractor whose UPN changed form, a married name that flipped between directories, a recent hire not yet in the mapping — the pre-flight UI lets you add the mapping without re-running the migration. Type the destination UPN, press apply, that principal moves from exceptions to replay.

This happens often enough that we designed the workflow around it. Expect to adjust the mapping 5–10% during pre-flight on a typical site; it is not a failure of preparation, it is just that some users have unusual cases.

Option C: Invite as guest

If the unmapped user is a real external collaborator — a partner at another firm, an agency, a customer — you do not want to drop their access, and they do not have an internal UPN. The right move is to invite them as a B2B guest on the destination tenant.

In the pre-flight, flagging a user as “invite as guest” causes the replay phase to:

The guest workflow is bounded by your destination tenant’s external collaboration settings. If the destination tenant is locked down to no guest invitations, the “invite as guest” option is simply disabled in the UI. If guest invitations are allowed only to certain domains, the UI warns you when the guest’s email is outside the allow list.

Groups: the ordering problem

There is one ordering trap specific to group-based permissions worth calling out. If the source has a permission like marketing-team@acme.com has Edit on /sites/marketing and you have mapped that group to marketing@newcompany.com, the replay is:

POST /sites/dest-site/items/{id}/invite
{
  "recipients": [{ "email": "marketing@newcompany.com" }],
  "roles": ["write"]
}

That API call assumes marketing@newcompany.com exists and has a directory object the SharePoint site can resolve. It does not verify that Alice is a member of that group. Membership is a directory-side concern and if the destination group is empty when the migration runs, the replay succeeds but nobody has effective access.

The right sequence is:

  1. Provision destination groups first (either manually or via AD Connect / HR sync)
  2. Populate group memberships so the right people are in the right groups
  3. Run the migration, which maps permissions to the populated groups

The migration tool cannot do step 1 and 2 for you — they are directory-maintenance tasks, not migration tasks. What we can do is flag when a mapped destination group has zero members at replay time, because that is almost always a sign that steps 1/2 were not completed correctly.

A worked example

Acme is acquired by New Company. The SharePoint site /sites/acme-marketing needs to move to newcompany.sharepoint.com/sites/marketing. The site has these permission assignments:

PrincipalScopeRole
alice@acme.comSiteOwner
bob@acme.comSiteMember
marketing-team@acme.comSiteMember
partner@external.com/Shared DocumentsContributor
ex-employee@acme.comSiteMember
anyone-with-the-link/Shared Documents/Q1-Report.pdfView

Mappings:

# user-map.csv
source_upn,destination_upn
alice@acme.com,alice.smith@newcompany.com
bob@acme.com,bob.jones@newcompany.com

# group-map.csv
source_group,destination_group
marketing-team@acme.com,marketing@newcompany.com

Pre-flight output:

After human decisions (invite partner as guest, drop ex-employee, drop Anyone-link with an explicit note), the replay phase runs and emits a CSV of every permission written. The Monday-morning experience is that marketing works immediately, the partner gets an invitation email, and the Anyone-link is gone (as intended). No mystery access gaps, no silent data loss.

What we do not do

A few explicit non-goals, because we get asked:

Related reading

Get started

A free workspace at app.migrationfox.com/register lets you upload a user map and a group map and run the pre-flight for free. You will see the exceptions list before you spend a byte.

See your permission exceptions before you migrate

Free pre-flight. No credit card required.

Start Free →