A fresh Microsoft Entra tenant ships with the most permissive external-collaboration posture the product allows: any member can invite any external user, and every external Entra organization on the planet is trusted equally. A guest from a company you have never heard of can be invited by a summer intern, redeem the invitation, and land in your directory with a standing credential that nobody reviews. That is fine for a five-person startup and a genuine liability for a regulated enterprise. The control plane that fixes it — cross-tenant access settings — is powerful, sparsely documented in the places people actually look, and full of traps: a default policy that silently trusts the world, an MFA-trust toggle that is a per-partner bet on their factor strength rather than a friction slider, and two completely different products that both answer to the name “External ID.”
This is the practitioner’s guide to governing external access in Entra. We treat B2B not as one feature but as a stack of independent controls you compose: who in your tenant may invite (the authorization policy), which domains may be invited (the B2B management policy), which tenants you trust and how (cross-tenant access settings — inbound and outbound, per-partner), what a guest can reach once redeemed (entitlement management and app assignment), how strongly they must authenticate (Conditional Access, plus inbound MFA/device trust), and how long they stay (access reviews and access-package expiry). You will learn the exact difference between B2B collaboration (a guest object in your tenant) and B2B direct connect (no guest object, trust at the tenant boundary, used by Teams shared channels), when to reach for the separate External ID for customers (CIAM) tenant instead of bolting consumer sign-up onto your workforce directory, and how to wire every one of these with real Microsoft Graph and az calls.
By the end you will stop treating “add a guest” as a one-click favor and start treating external identity as a governed lifecycle with a locked default, named-partner allowlists, trust decisions made per organization on the strength of their controls, and automatic offboarding. The recurring lesson: external MFA trust is not a convenience toggle — it is you accepting a partner’s assertion that their user did MFA, so you turn it on only for partners whose bar you actually trust, and you keep the default restrictive so per-partner policy is the thing that opens each door.
What problem this solves
External collaboration is where identity governance quietly falls apart. The pain is rarely a dramatic breach; it is slow accretion. A partner engagement that needed three people gets ten guests because the invite went to a distribution list. A vendor’s contractor left their firm eighteen months ago but their guest object in your tenant still works because nobody reviews guests. A CAD-review app that should be reachable by one external team is reachable by anyone the default cross-tenant policy trusts — which, out of the box, is every Entra organization in existence. Meanwhile your security team has spent a quarter rolling out phishing-resistant MFA for employees, and every one of those guests sails past it because a partner tenant asserted “yes, they did MFA” with an SMS code you would never accept from your own staff.
What breaks without deliberate cross-tenant governance: guest sprawl (thousands of stale external objects nobody can attribute to a live engagement), trust leakage (accepting weak MFA from tenants you never vetted), over-broad reach (guests can be invited to any app, from any organization, by any member), audit gaps (no owner, no expiry, no review trail on external access — a finding in every ISO 27001 and SOC 2 audit), and the expensive mistake of standing up the wrong product (using a customer-identity CIAM tenant for partner B2B, or letting consumer sign-ups pollute your employee directory and land in the scope of your workforce Conditional Access).
Who hits this: every organization that collaborates with partners, vendors, contractors, auditors, or acquired companies through Microsoft 365, SharePoint, Teams, or line-of-business apps — which is nearly all of them. It bites hardest on enterprises with regulatory obligations (external access is an explicit control in SOC 2 CC6, ISO 27001 A.9, and most data-residency regimes), on companies going through M&A (where “get their team into our systems this week without merging directories” is a standing request), and on ISVs who confuse “let partners into my corporate tenant” with “let customers sign up for my SaaS.” The fix is not a single setting; it is a composed posture — a locked default, per-partner allowlists, trust decisions grounded in each partner’s actual controls, entitlement-managed onboarding, guest-scoped Conditional Access, and recurring reviews with teeth.
To frame the whole field before the deep dive, here is every layer of external-access control this article covers, what it governs, and where it lives:
| Control layer | What it governs | Where it lives | Failure if you skip it |
|---|---|---|---|
| Authorization policy | Who in your tenant may invite guests | policies/authorizationPolicy (allowInvitesFrom) |
Any member sprinkles guests across the directory |
| B2B management policy | Which email domains may be invited at all | Legacy B2B policy (invitationsAllowedAndBlocked…) |
Consumer/competitor domains get invited |
| Cross-tenant access — default | Baseline inbound/outbound trust for every external Entra tenant | crossTenantAccessPolicy/default |
The world is trusted equally |
| Cross-tenant access — per partner | Inbound/outbound access + MFA/device trust for one tenant | crossTenantAccessPolicy/partners/{id} |
No way to differentiate a vetted partner from a stranger |
| B2B direct connect | Tenant-boundary trust with no guest object (Teams shared channels) | XTAP b2bDirectConnect* blocks |
Either no shared-channel collaboration or guest sprawl |
| Conditional Access (guest-scoped) | How a guest must authenticate to reach your apps | CA policies targeting Guest or external users |
Guests bypass MFA / device requirements |
| Entitlement management | What a guest can reach, with an owner and an expiry | Access packages + policies | Manual onboarding/offboarding, no expiry |
| Access reviews | Periodic recertification / removal of guests | Identity Governance access reviews | Stale guests accumulate forever |
| External ID for customers (CIAM) | Consumer/customer sign-up in a separate tenant | Dedicated external tenant + user flows | Customers pollute the employee directory |
Learning objectives
By the end of this article you can:
- Choose correctly between B2B collaboration, B2B direct connect, and a separate External ID for customers (CIAM) tenant for a given collaboration or customer scenario, and explain why mixing them up is expensive.
- Read and reason about cross-tenant access settings end to end — the default policy versus per-organization overrides, the inbound versus outbound sides, and how targeting groups and applications narrows the blast radius.
- Configure inbound trust for external MFA, compliant device, and Hybrid-joined device claims, and articulate exactly what you are accepting when you set
isMfaAccepted: truefor a partner. - Enable automatic redemption in both directions so a guest lands ready-to-work without a redemption email, and know when not to.
- Drive the invite → redeem → reset-redemption lifecycle via Microsoft Graph, including the IdP-migration case where you reset redemption instead of deleting and re-inviting.
- Onboard guests through entitlement management access packages with connected-organization scoping, approvals, and time-boxed expiry so offboarding is automatic.
- Author Conditional Access policies scoped to guests (require MFA or compliant device, block legacy auth) and explain how inbound trust lets a guest satisfy CA with a home-tenant claim.
- Govern the guest lifecycle with recurring access reviews and auto-apply removal, and map the whole posture to the audit controls it satisfies.
Prerequisites & where this fits
You should already understand core Entra concepts: a tenant is your directory instance; a member is an internal user (userType: Member); a guest is an external user object (userType: Guest) that authenticates in their home directory but is represented in yours; and Conditional Access is the policy engine that decides, per sign-in, whether to grant, block, or challenge. You should know how to run Microsoft Graph calls (via az rest, the Graph Explorer, or the Microsoft Graph PowerShell SDK) and read JSON. Familiarity with Microsoft 365 surfaces (Teams, SharePoint, OneDrive) helps because they are the most common places guests actually land, and awareness of Entra ID P1/P2 licensing matters because several controls here (entitlement management, access reviews, Identity Protection risk in CA) require P2 / Entra ID Governance.
This sits in the Identity & Access Governance track. It assumes the tenant fundamentals and pairs tightly with several adjacent articles. Guest sign-ins are only as safe as the persona-based Conditional Access framework you point at them, so that decision is upstream of this one. Onboarding at scale belongs in entitlement management access packages, and the recurring cleanup belongs in an access reviews program. When you need to merge directories rather than federate as guests, that is cross-tenant synchronization, a different mechanism with different trade-offs. And when the external users are customers rather than partners, you cross into customer identity (CIAM) with Entra External ID — a separate tenant and a separate model this article is careful to keep distinct.
A quick map of who owns which control during a real deployment, so you route decisions to the right team:
| Control | Typical owner | Approval needed from | License floor |
|---|---|---|---|
| Default cross-tenant access policy | Identity platform team | Security / CISO (it is a trust posture) | Entra ID Free (basic B2B) |
| Per-partner XTAP + MFA trust | Identity engineering | Security, per partner (it is a trust bet) | Entra ID Free |
| Who may invite (authorization policy) | Identity platform team | IT governance | Entra ID Free |
| Allowed/blocked invite domains | Identity platform team | Security / legal | Entra ID Free |
| Guest Conditional Access | Identity engineering | Security | Entra ID P1 (per active guest via MAU) |
| Entitlement management access packages | Governance / IGA team | Resource / app owners | Entra ID P2 / Governance |
| Access reviews of guests | Governance / IGA team | Resource owners as reviewers | Entra ID P2 / Governance |
| External ID (CIAM) tenant | Application / product team | Security architecture | External ID MAU billing |
Core concepts
Six mental models make every later decision obvious.
“External ID” is two products wearing one name. Microsoft consolidated its external-identity story under the umbrella brand Entra External ID, but it still covers two structurally different scenarios. The first is workforce-tenant B2B: partners, vendors, and contractors become guest (or direct-connect) objects in your existing employee tenant, and everything in this article’s sections 2 through 9 lives here. The second is External ID for customers (CIAM): consumers and customers of an app you build get local accounts in a separate, dedicated external tenant (the successor to Azure AD B2C), self-registering through user flows. The settings, APIs, and even the admin surfaces differ. Applying one model’s tooling to the other — using CIAM user flows to onboard partners, or letting customer sign-ups into your workforce directory — is the single most expensive category error in this space.
A guest is a foreign passport stamped for entry, not a new citizen. In B2B collaboration, the guest authenticates in their home directory (or via an external IdP), and your tenant holds a guest object that carries their group memberships, app assignments, and access-package grants. You never manage their password or their MFA registration — their home tenant does. What you control is what the guest can reach in your tenant and how strongly they must prove themselves at your border (via Conditional Access and inbound trust). Delete the guest object and you orphan every permission attached to it; this is why the correct response to a partner changing their IdP is reset redemption, not delete-and-re-invite.
B2B direct connect trusts the tenant, not the user. B2B direct connect is a different beast: there is no guest object at all. Instead, two organizations establish mutual trust in their cross-tenant access settings, and users from the other tenant access a specific, narrow surface — today that surface is Teams shared channels (Teams Connect). A user from the partner never appears in your directory; they authenticate entirely in their home tenant and are admitted to the shared channel because the tenant boundary trusts them. This eliminates guest sprawl for deep Teams collaboration but is unavailable for SharePoint sites, general apps, or anything outside shared channels. Choosing between collaboration and direct connect is choosing between “a managed foreign object in my directory” and “a trusted boundary with no object.”
Cross-tenant access settings are a two-by-two grid. The control plane for both collaboration and direct connect is cross-tenant access settings (XTAP). Picture a grid. One axis is default versus per-organization: there is exactly one default policy that applies to every external Entra tenant, plus optional per-organization overrides keyed by the partner’s tenant ID. The other axis is inbound versus outbound: inbound governs external users coming into your tenant; outbound governs your users going into theirs. Every cell can independently allow or block B2B collaboration, B2B direct connect, target specific users/groups and applications, set trust for inbound MFA/device claims, and toggle automatic redemption. Almost every real design is: lock the default restrictive, then open specific cells per named partner.
Trust is you outsourcing an authentication check to another tenant. Inbound trust settings (isMfaAccepted, isCompliantDeviceAccepted, isHybridAzureADJoinedDeviceAccepted) let a guest satisfy your Conditional Access requirement using a claim from their home tenant instead of re-proving it in yours. If your CA policy requires MFA and you trust the partner’s MFA, a guest who did MFA at home is honored as MFA-satisfied here — no re-registration, no second prompt. This is strictly better UX and, for a partner running phishing-resistant methods, better security. But it is binary per partner: you cannot say “accept their MFA but only the strong factors.” Trusting a partner whose MFA is SMS-only means accepting SMS as sufficient for your resources. Trust never weakens CA — it only lets a claim flow through; if you don’t trust the partner, your CA simply re-challenges the guest.
Governance is a lifecycle, not an event. Inviting a guest is the easy 5%. The 95% is the lifecycle: who approved the access, what it is scoped to, when it expires, who recertifies it, and how it is removed. Entitlement management access packages turn onboarding and offboarding into a single policy (assign a bundle of resources with an approval and an expiry; when it lapses, access — and optionally the guest object — is removed). Access reviews recertify guests on a recurring schedule and auto-remove anyone a reviewer doesn’t approve. Without these, every guest you invite becomes a standing, unowned, unreviewed credential — exactly the finding auditors write up.
The vocabulary in one table
Before the deep sections, pin down every moving part. The glossary at the end repeats these for lookup; this table is the mental model side by side:
| Concept | One-line definition | Where it lives | Why it matters |
|---|---|---|---|
| B2B collaboration | Guest object in your tenant; authenticates at home | Guest user in your directory | The default partner model; carries permissions |
| B2B direct connect | Tenant-boundary trust, no guest object | XTAP b2bDirectConnect* |
Teams shared channels; zero guest sprawl |
| External ID (CIAM) | Customer sign-up in a separate external tenant | Dedicated external tenant | Customers, not partners; keeps directories clean |
| Cross-tenant access (XTAP) | Control plane: inbound/outbound, default/per-org | crossTenantAccessPolicy |
Decides who and what is trusted |
| Default policy | Baseline for every external Entra tenant | crossTenantAccessPolicy/default |
Lock this restrictive first |
| Per-organization policy | Override for one partner tenant ID | crossTenantAccessPolicy/partners/{id} |
Where you open named partners |
| Inbound trust | Accept partner’s MFA/device claim | inboundTrust in a policy |
Removes guest re-registration friction |
| Automatic redemption | Skip the consent/redemption prompt | automatic in trust settings |
Guest lands ready, no email step |
| Redemption | Guest accepts the invitation, links their IdP | externalUserState on the object |
PendingAcceptance → Accepted |
| Reset redemption | Re-issue invite keeping the same object | resetRedemption: true on invitation |
Correct fix for IdP migration |
| Guest Inviter | Directory role that may invite guests | Role assignment | Constrains who can invite |
| Connected organization | An external org registered for entitlement mgmt | Identity Governance | Scopes access-package eligibility |
| Access package | A bundle of resources with policy + expiry | Entitlement management | Time-boxed guest onboarding |
| Access review | Recurring recertification of access | Identity Governance | Removes stale guests |
Choosing the right External ID model
Get this decision right first; it dictates everything downstream. The three models are not interchangeable, and the cost of choosing wrong is measured in months of rework.
| Model | Where the external identity lives | Auth surface | Object in your directory? | Use it for |
|---|---|---|---|---|
| B2B collaboration | Guest object in your workforce tenant | Guest authenticates in their home tenant (or external IdP), redeems in yours | Yes — a guest user | Partners, vendors, contractors using Teams, SharePoint, your LOB apps |
| B2B direct connect | No object; trust at the tenant boundary | Shared channels / Teams Connect only, fully home-tenant auth | No | Deep Teams shared-channel collaboration with a trusted org, zero guest sprawl |
| External ID (CIAM) tenant | Local consumer/customer accounts in a separate external tenant | Email OTP, social, or federated IdPs via user flows | Separate tenant entirely | Customer- and consumer-facing SaaS apps you build |
The big trap has two shapes. First, people reach for a CIAM external tenant to handle partner B2B — but partner collaboration belongs in your workforce tenant via B2B, because partners need to reach Teams, SharePoint, and internal apps that live there. Second, and worse, people bolt customer sign-up onto their corporate workforce tenant — so consumer accounts land in the employee directory, appear in your GAL, fall into scope of workforce Conditional Access and licensing, and generally contaminate the identity boundary you spent years hardening. Customer-facing apps belong in a dedicated external tenant so consumer accounts never touch your employee directory.
Here is the decision as a flow you can hold in your head: is the external person a customer of an app you built? → CIAM external tenant. Is the collaboration purely Teams shared channels with a trusted, reciprocating org? → B2B direct connect. Everything else — partners, vendors, contractors, auditors, acquired-company staff needing SharePoint/Teams/apps → B2B collaboration. And a distinct fourth case that people confuse with all three: are you merging two directories so users become members of a single tenant? That is cross-tenant synchronization, not B2B — a provisioning mechanism, not a federation one.
A decision table for the ambiguous cases that actually come up:
| Scenario | Right model | Why not the others |
|---|---|---|
| Vendor’s 5-person team needs your SharePoint + a LOB app | B2B collaboration | Direct connect can’t reach SharePoint/LOB; CIAM is for customers |
| Two partner orgs running Teams-only, want shared channels | B2B direct connect | Collaboration works but creates guest objects you don’t need |
| You sell a SaaS product; end users self-register | External ID (CIAM) | Guests/direct-connect are for orgs you know, not the public |
| Acquired company, keep them as guests short-term | B2B collaboration + entitlement mgmt expiry | Directory merge (XT sync) is heavier; do it later |
| Acquired company, permanently merge into one directory | Cross-tenant synchronization | B2B leaves them as guests forever; you want members |
| Auditor needs read access to one app for 3 weeks | B2B collaboration via access package (expiry) | Manual invite has no expiry/owner; auditors are the audit finding |
| Partner uses Okta for their whole company (no Entra) | B2B collaboration + SAML/OIDC direct federation | Their users still become guests; you federate at the domain |
Everything in sections that follow is workforce-tenant B2B unless the heading explicitly says external (CIAM) tenant.
B2B collaboration vs B2B direct connect
These two are the fork inside workforce collaboration, and the difference is not cosmetic. B2B collaboration creates a guest object; B2B direct connect does not. That single distinction cascades into what surfaces are reachable, what appears in your directory, how Conditional Access applies, and how you offboard.
| Dimension | B2B collaboration | B2B direct connect |
|---|---|---|
| Object in your directory | Guest user (userType: Guest) |
None |
| Where the user authenticates | Home tenant or external IdP, then redeems in yours | Entirely in their home tenant |
| Reachable surfaces | Teams (as guest), SharePoint, OneDrive, LOB/enterprise apps, Azure resources | Only Teams shared channels (Teams Connect) today |
| Redemption / invitation | Yes — invitation, redemption, externalUserState |
No invitation; trust configured tenant-to-tenant |
| Conditional Access target | Guest or external users → the guest object |
CA still applies (as external user), evaluated via trust |
| Licensing footprint | Counts toward guest MAU; may consume licenses for some workloads | No guest object; billed via Teams collaboration model |
| Offboarding | Remove guest / expire access package | Remove the direct-connect trust or the shared channel |
| Guest sprawl risk | Real — every collaborator is an object | None — no objects created |
| Sharing files outside the channel | Yes (SharePoint/OneDrive as guest) | No — confined to the shared channel |
When to pick which: reach for direct connect when the collaboration is genuinely Teams-shared-channel-shaped — two engineering teams co-working in a channel, no need to share SharePoint sites or grant app access, and both organizations are willing to configure mutual trust. It is the cleanest possible posture because there is nothing to review, nothing to expire, and no object to go stale; you tear down the trust or the channel and the access is gone. Reach for collaboration for everything broader: the guest needs a SharePoint site, needs to be assigned to an enterprise app, needs an Azure RBAC role, or the collaboration isn’t neatly contained in one shared channel. Most real partner relationships end up on collaboration precisely because they span more than one Teams channel.
Both are configured through the same cross-tenant access settings, just different blocks. In a per-organization policy, b2bCollaboration* governs collaboration and b2bDirectConnect* governs direct connect — inbound and outbound for each. A crucial asymmetry: direct connect must be enabled on both sides. Because there is no invitation flow, the partner tenant must also allow direct connect outbound to you (or inbound from you, depending on direction) — a shared channel only forms when both organizations’ cross-tenant settings agree. Collaboration is more forgiving: you can invite a guest even if the partner has done nothing, because the guest redeems using their own credentials.
The direct-connect blocks in a per-organization policy look structurally identical to the collaboration ones — allow/block, targeting users/groups and applications:
{
"b2bDirectConnectInbound": {
"usersAndGroups": {
"accessType": "allowed",
"targets": [
{ "target": "AllUsers", "targetType": "user" }
]
},
"applications": {
"accessType": "allowed",
"targets": [
{ "target": "Office365", "targetType": "application" }
]
}
},
"b2bDirectConnectOutbound": {
"usersAndGroups": {
"accessType": "allowed",
"targets": [ { "target": "AllUsers", "targetType": "user" } ]
},
"applications": {
"accessType": "allowed",
"targets": [ { "target": "Office365", "targetType": "application" } ]
}
}
}
Note the application target Office365 — direct connect’s reachable app surface is the Office 365 suite (specifically the shared-channels experience), which is why you don’t enumerate arbitrary app IDs here the way you do for collaboration. And note that for direct connect, inbound trust matters even more: because there is no guest object and no redemption in your tenant, honoring the partner’s MFA/compliant-device claims is often the only practical way to satisfy your Conditional Access for these users — which is why direct connect and inbound trust are almost always configured together.
Cross-tenant access settings: default vs per-organization
Cross-tenant access settings are the control plane for both collaboration and direct connect. There is one default policy that applies to every external Entra tenant you have not explicitly configured, plus organizational overrides keyed by partner tenant ID. Each policy has an inbound side (external users coming into your tenant) and an outbound side (your users going to theirs). Understanding the precedence and the shape of these objects is the foundation for everything else.
Inspect the current default before you touch anything:
# Default inbound/outbound posture for ALL external tenants (Graph, beta or v1.0)
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default"
The default object has these top-level blocks, and knowing what each governs — and its out-of-the-box value — is essential because the shipped defaults are permissive:
| Block | Governs | Default (out of the box) | Recommended baseline |
|---|---|---|---|
b2bCollaborationInbound |
External users invited into your tenant | Allowed (all users, all apps) | Keep allowed or lock to allowlisted partners |
b2bCollaborationOutbound |
Your users invited into external tenants | Allowed (all users, all apps) | Usually allowed; tighten if exfil-sensitive |
b2bDirectConnectInbound |
External users into your shared channels | Blocked | Enable per trusted partner only |
b2bDirectConnectOutbound |
Your users into external shared channels | Blocked | Enable per trusted partner only |
inboundTrust |
Accept external MFA/device claims | All false (don’t trust) |
Keep false on default; trust per partner |
automaticUserConsentSettings |
Auto-redeem inbound/outbound | Both false |
Enable per trusted partner if desired |
invitationRedemptionIdentityProviderConfiguration |
Fallback order (MSA, OTP, Google, etc.) | Default order | Rarely changed |
Notice the shape of the shipped default: collaboration is wide open, direct connect is closed, and no trust is extended. That means, straight out of the box, any external Entra user can be invited as a guest by anyone allowed to invite, but nobody gets shared-channel access and no MFA claims are trusted. The most common enterprise hardening is to narrow the collaboration default (either lock it to an allowlist or leave it open but rely on the authorization/domain policies to constrain it) while opening direct connect and trust only per named partner.
The precedence rule is simple but easy to get backwards: a per-organization policy for a partner tenant completely overrides the default for that tenant — it does not merge. If you set a per-org policy for fabrikam that allows collaboration inbound for one group, that is the entire inbound collaboration posture for Fabrikam; the default’s settings for Fabrikam no longer apply. This is why the pattern is “lock the default, then author per-partner policies that open exactly what each partner needs.”
I keep the default restrictive on the axes that leak trust and open access per partner. A common hardened default blocks inbound collaboration entirely, then explicitly allows named tenants via per-org policies:
{
"inboundTrust": {
"isMfaAccepted": false,
"isCompliantDeviceAccepted": false,
"isHybridAzureADJoinedDeviceAccepted": false
},
"b2bCollaborationInbound": {
"usersAndGroups": {
"accessType": "blocked",
"targets": [{ "target": "AllUsers", "targetType": "user" }]
},
"applications": {
"accessType": "blocked",
"targets": [{ "target": "AllApplications", "targetType": "application" }]
}
}
}
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default" \
--headers "Content-Type=application/json" \
--body @default-xtap.json
A blocked-inbound default does not break existing redeemed guests immediately, but it stops new inbound B2B from any tenant you have not allowlisted. This is a significant posture change — communicate it, stage it with a single pilot partner, and be ready to add per-org policies quickly as legitimate collaboration needs surface. A less disruptive alternative is to leave collaboration open on the default but constrain it with the authorization policy (who may invite) and the B2B management policy (which domains) — belt-and-suspenders without the hard block. Which you choose depends on how tightly regulated you are: financial and healthcare tenants usually block by default; most others constrain by domain and inviter.
The two hardening philosophies, side by side:
| Approach | Default b2bCollaborationInbound |
How you open partners | Pros | Cons |
|---|---|---|---|---|
| Allowlist (block default) | blocked for all |
Per-org policy allowed per partner |
Zero trust by default; explicit audit of every partner | Every new partner needs a policy; onboarding friction |
| Constrain (open default) | allowed for all |
Domain allowlist + inviter role limit | Low friction; partners onboard without a per-org policy | Any allowed domain can be invited; less explicit |
| Hybrid | allowed, but trust/direct-connect off |
Per-org for trust, domain policy for reach | Frictionless collaboration, deliberate trust | Trust and reach governed in two places |
Authoring inbound and outbound policies for a named partner
Create a per-organization policy for the partner’s tenant ID, then set its inbound and outbound sides. It is two logical steps: register the partner org (which creates a policy object seeded from the default), then PATCH the specific blocks you want to change.
PARTNER_TENANT="22223333-cccc-4444-dddd-5555eeee6666"
# 1. Register the partner org (creates a per-org policy you then tighten/open)
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/partners" \
--headers "Content-Type=application/json" \
--body "{\"tenantId\": \"$PARTNER_TENANT\"}"
Now scope inbound access to a specific group in the partner tenant (their object IDs) and to specific applications in yours. Targeting a group instead of AllUsers is the single highest-leverage control here — it means only the partner’s project team can be invited and admitted, not their entire company. Likewise targeting specific app IDs instead of AllApplications means guests reach only the apps the engagement requires.
{
"b2bCollaborationInbound": {
"usersAndGroups": {
"accessType": "allowed",
"targets": [
{ "target": "9a9a8b8b-7c7c-6d6d-5e5e-4f4f3a3a2b2b", "targetType": "group" }
]
},
"applications": {
"accessType": "allowed",
"targets": [
{ "target": "11112222-aaaa-bbbb-cccc-333344445555", "targetType": "application" }
]
}
}
}
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/partners/$PARTNER_TENANT" \
--headers "Content-Type=application/json" \
--body @partner-inbound.json
The targeting options and their blast radius, from widest to narrowest:
| Target type | targetType value |
Blast radius | When to use |
|---|---|---|---|
| All external users | user with AllUsers |
Every user in the partner tenant | Rarely — only for a fully trusted, small partner |
| A group | group with the group object ID |
Only members of that partner-side group | Default — scope to the project team |
| A specific user | user with a user object ID |
One named individual | Highly sensitive, single-collaborator cases |
| All applications | application with AllApplications |
Every app in your tenant | Broad partner access; avoid where possible |
| A specific application | application with an app ID |
One app in your tenant | Default — scope to the engagement’s apps |
| Office 365 suite | application with Office365 |
The M365 workloads collectively | Teams/SharePoint collaboration |
For the outbound direction — your employees being invited into the partner tenant — set b2bCollaborationOutbound on the same partner object. Outbound rarely needs to be as tight as inbound (you are trusting your own users to behave), but if you have data-exfiltration concerns, restrict which of your users may be guests elsewhere and pair it with the broader outbound posture. A common outbound restriction is to allow only a specific internal group (say, your partner-management team) to be provisioned as guests into external tenants:
{
"b2bCollaborationOutbound": {
"usersAndGroups": {
"accessType": "allowed",
"targets": [
{ "target": "5b5b4c4c-3d3d-2e2e-1f1f-0a0a9b9b8c8c", "targetType": "group" }
]
},
"applications": {
"accessType": "allowed",
"targets": [ { "target": "AllApplications", "targetType": "application" } ]
}
}
}
Inbound versus outbound decisions differ because the risk differs. Inbound is “who can get into my stuff” — high scrutiny, tight scoping. Outbound is “where can my people go” — usually a data-governance concern (are my users creating accounts in tenants I don’t control?) rather than an access concern:
| Direction | The risk it manages | Typical scoping | Common mistake |
|---|---|---|---|
| Inbound collaboration | External users reaching your resources | Group + specific apps, per partner | Leaving it AllUsers/AllApplications |
| Outbound collaboration | Your users creating identities elsewhere | Often all users; restrict if exfil-sensitive | Blocking it and breaking legitimate partner work |
| Inbound direct connect | External users in your shared channels | Group + Office365, per trusted partner | Enabling without matching trust settings |
| Outbound direct connect | Your users in partner shared channels | Matches partner’s inbound expectation | Forgetting the partner must also enable it |
Trust settings: external MFA, compliant device, and automatic redemption
This is the setting cluster that removes the most guest friction — and the one most often misunderstood. By default, when a guest accesses your resources under a Conditional Access policy that requires MFA, Entra forces them to register for and satisfy MFA in your tenant, even though they already did MFA in their home tenant. Trusting their inbound claims means a partner who authenticated with a strong method at home is honored as MFA-satisfied here — no re-registration, no double prompt.
{
"inboundTrust": {
"isMfaAccepted": true,
"isCompliantDeviceAccepted": true,
"isHybridAzureADJoinedDeviceAccepted": false
}
}
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/partners/$PARTNER_TENANT" \
--headers "Content-Type=application/json" \
--body @partner-trust.json
Each trust flag, precisely — what it accepts and the decision behind it:
| Trust flag | What you are accepting | Turn on when | Leave off when |
|---|---|---|---|
isMfaAccepted |
The partner’s assertion that the user completed MFA | You trust the partner’s MFA strength (ideally phishing-resistant) | Partner’s MFA is weak (SMS-only) or unvetted |
isCompliantDeviceAccepted |
The partner’s Intune/MDM compliance signal for the device | Your CA requires compliant device and you trust their MDM | You can’t vouch for the partner’s device management |
isHybridAzureADJoinedDeviceAccepted |
The partner’s Hybrid-join device claim | Your CA requires Hybrid-join and the partner is Hybrid-joined | You don’t gate on Hybrid-join, or don’t trust it |
The decision guidance that matters most:
isMfaAccepted— turn on only for partners whose MFA bar you actually trust. You are accepting their assertion that the user did MFA. For a partner running phishing-resistant methods (FIDO2, Windows Hello, certificate-based), this is strictly better security and far less friction than making the guest re-register in your tenant. For an unknown or weakly-secured org, leave itfalseand let your Conditional Access challenge the guest with a factor you control. The critical nuance: trust is binary per partner — you cannot accept only their strong factors. If their MFA includes SMS and you setisMfaAccepted: true, you are accepting SMS.isCompliantDeviceAccepted/isHybridAzureADJoinedDeviceAccepted— only meaningful if your Conditional Access requires compliant or Hybrid-joined devices for the app. Most organizations cannot enforce device compliance on a partner’s hardware (you don’t manage their laptops), so trusting the partner’s compliance signal is the only way to let a device-gated app work for guests at all — the alternative is that guests simply can’t access those apps. Turn it on only if you both require device compliance and trust the partner’s MDM to enforce it honestly.
A frequent source of confusion, stated plainly: trust settings are honored only when your Conditional Access policy actually requires MFA or device compliance. Trust does not weaken CA. It lets a guest satisfy a CA requirement using a claim from their home tenant instead of re-proving it in yours. If you have no CA requiring MFA for guests, isMfaAccepted changes nothing. This is why the correct architecture is both: a guest-scoped CA policy that requires MFA, plus inbound trust set per partner so trusted partners satisfy it from home while untrusted ones get re-challenged.
Automatic redemption is the other friction-remover in this block. Normally a guest must click a redemption link and consent before their object activates. With automatic redemption enabled (in the relevant direction), the consent/redemption prompt is skipped and the guest can access resources immediately — useful for tightly-integrated partners or automated onboarding where the email round-trip is pointless. It is configured under automaticUserConsentSettings:
{
"automaticUserConsentSettings": {
"inboundAllowed": true,
"outboundAllowed": true
}
}
Here inboundAllowed: true lets external users into your tenant skip the redemption prompt (useful for a trusted partner or automated onboarding where the consent step adds nothing), while outboundAllowed: true lets your users skip the partner’s consent prompt when they reach into the partner tenant — meaning you consent on your users’ behalf, so confirm the partner trust first.
A subtlety worth internalizing: automatic redemption removes the user-facing consent moment, but it does not remove your controls — the per-org collaboration targeting, inbound trust, and Conditional Access all still apply. It is a UX optimization for trusted partners, not a security bypass. Pair it with tight group/app targeting so “no consent prompt” doesn’t mean “broad silent access.”
Invitation, redemption, and reset redemption
With the policy plane configured, the actual per-guest lifecycle runs through the invitation and user APIs. Send an invitation via Graph; setting sendInvitationMessage: true emails the guest a redemption link:
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/invitations" \
--headers "Content-Type=application/json" \
--body '{
"invitedUserEmailAddress": "anil@fabrikam.com",
"inviteRedirectUrl": "https://myapplications.microsoft.com",
"sendInvitationMessage": true,
"invitedUserType": "Guest"
}'
The invitation object’s key fields and what they control:
| Field | Purpose | Values / notes |
|---|---|---|
invitedUserEmailAddress |
Who to invite | The external email; becomes the guest’s UPN-ish identity |
inviteRedirectUrl |
Where the guest lands after redemption | Your app, or the My Apps portal |
sendInvitationMessage |
Whether Entra emails the redemption link | true for a normal invite; false if you send your own comms |
invitedUserType |
Guest or Member semantics | Guest (default) — Member grants member-like privileges (rare) |
invitedUserMessageInfo |
Custom message / CC recipients | Personalize the invite email |
resetRedemption |
Re-issue for an existing object | See below — for IdP migration |
invitedUser |
Reference an existing user object | Required with resetRedemption |
The guest’s externalUserState is the field you query to distinguish pending from redeemed guests: PendingAcceptance means invited-but-not-yet-redeemed (chase the invite or let it lapse), Accepted means redeemed and active (the normal working state), and a null value means the object wasn’t B2B-invited at all (e.g. created directly), so its handling depends on how it was created.
Reset redemption is the maneuver that separates people who understand B2B from people who don’t. When a partner changes their identity provider — say they migrate from Google Workspace to their own Entra tenant, or from an on-prem AD FS to Okta — the right move is not to delete and re-invite the guest. Deleting the guest object orphans every group membership, app assignment, and access-package grant attached to it, and you’d have to rebuild all of it. Instead, reset redemption status so the guest re-redeems against the new IdP while keeping the same object ID, group memberships, and app assignments:
GUEST_ID="<guest-object-id>"
# Optional: invalidate existing tokens so the old IdP session can't be reused
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/users/$GUEST_ID/invalidateAllRefreshTokens"
# Reset redemption — re-issues the invite, keeps the object and all its permissions
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/invitations" \
--headers "Content-Type=application/json" \
--body "{
\"invitedUserEmailAddress\": \"anil@fabrikam.com\",
\"inviteRedirectUrl\": \"https://myapplications.microsoft.com\",
\"sendInvitationMessage\": true,
\"resetRedemption\": true,
\"invitedUser\": { \"id\": \"$GUEST_ID\" }
}"
When to reset redemption versus other operations:
| Situation | Do this | Not this |
|---|---|---|
| Partner migrated Google → Entra tenant | Reset redemption (same object, new IdP) | Delete + re-invite (orphans permissions) |
| Partner migrated AD FS → Okta | Reset redemption | Delete + re-invite |
| Guest’s email address changed | Reset redemption with the new address | Editing the object’s UPN by hand |
| Guest left the partner entirely | Remove the guest / let access package expire | Reset redemption (they shouldn’t return) |
| Redemption link expired unredeemed | Re-send invitation (no reset needed if never redeemed) | Delete the pending object |
Conditional Access for guests
Cross-tenant trust settings and Conditional Access are two halves of the same guest-authentication story, and they must be designed together. Trust settings decide whether a home-tenant claim is accepted; Conditional Access decides what is required in the first place. If you have no CA policy requiring MFA for guests, then a guest with no MFA at home walks in with a password — the trust settings never come into play because nothing was required. The correct posture is a dedicated guest-scoped CA policy that requires strong authentication, combined with per-partner trust so vetted partners satisfy it from home while everyone else is challenged with a factor you control.
Conditional Access targets external users through the Guest or external users assignment, which — unlike the old “All guest and external users” lump — lets you distinguish types of external user:
| Guest/external user type in CA | Who it matches | Typical policy |
|---|---|---|
| B2B collaboration guest users | Redeemed guests in your tenant | Require MFA (or trusted MFA), block legacy auth |
| B2B collaboration member users | Rare Member-type invited users |
Same as guests, usually |
| B2B direct connect users | Shared-channel users, no object | Rely on trust; require MFA-equivalent claim |
| Local guest users | Guests created without a home IdP (legacy) | Require MFA registered in your tenant |
| Service provider users | GDAP/partner-admin (CSP) users | Tight — often require MFA + specific controls |
| Other external users | Users not matching the above | Usually block or scrutinize heavily |
A baseline guest CA policy requires MFA for all guest sign-ins and blocks legacy authentication (which can’t do MFA and is a common bypass). Expressed as a Graph Conditional Access policy:
{
"displayName": "CA - Guests require MFA, block legacy auth",
"state": "enabledForReportingButNotEnforced",
"conditions": {
"users": {
"includeGuestsOrExternalUsers": {
"guestOrExternalUserTypes": "b2bCollaborationGuest,b2bDirectConnectUser",
"externalTenants": { "membershipKind": "all" }
}
},
"applications": { "includeApplications": ["All"] },
"clientAppTypes": ["all"]
},
"grantControls": {
"operator": "OR",
"builtInControls": ["mfa"]
}
}
Note the policy starts in enabledForReportingButNotEnforced — report-only mode. Never enforce a guest CA policy blind; run it report-only first, inspect the sign-in logs’ policy-evaluation results to confirm you’re not about to lock out a legitimate partner, then flip to enabled. This report-only discipline is the same one covered in depth in deploying Conditional Access safely, and it matters doubly for guests because you can’t easily test as a partner user.
How trust and CA interact, case by case — this table is the whole mental model of guest authentication:
| Guest’s home MFA | Your isMfaAccepted for their tenant |
Your guest CA requires MFA? | Result for the guest |
|---|---|---|---|
| Did strong MFA at home | true |
Yes | Satisfied by home claim — no re-prompt |
| Did strong MFA at home | false |
Yes | Re-challenged in your tenant (registers/does MFA) |
| No MFA at home | true |
Yes | No MFA claim to trust → challenged in your tenant |
| No MFA at home | false |
Yes | Challenged in your tenant |
| (any) | (any) | No CA requires MFA | No MFA required at all — trust is moot |
Beyond MFA, guest-scoped CA commonly layers: session controls (shorter sign-in frequency for guests than employees, since you don’t manage their devices), device requirements (require compliant device only if you trust the partner’s compliance signal, else you lock them out), and authentication context to gate especially sensitive apps behind step-up authentication. Guests are also prime candidates for risk-based policies via Identity Protection, and for the diagnostic reality that when a guest is blocked, the answer is in the sign-in logs — see diagnosing Conditional Access from sign-in logs.
The knobs worth setting specifically for guests:
| CA control for guests | Why guests specifically | Setting |
|---|---|---|
| Require MFA | You can’t assume their home did it | builtInControls: ["mfa"] |
| Block legacy authentication | Legacy can’t MFA; common bypass | Separate policy targeting legacy client app types |
| Shorter sign-in frequency | Unmanaged devices; limit token lifetime | Session control signInFrequency |
| Require compliant device (conditional) | Only if you trust their MDM via inbound trust | builtInControls: ["compliantDevice"] |
| Authentication context step-up | Gate sensitive apps behind extra proof | Auth context + app mapping |
| Terms of use | Legal acceptance for external parties | Grant control termsOfUse |
External collaboration restrictions: who invites, which domains
Two independent knobs govern invitations at the tenant level, upstream of any per-partner policy. Both are cheap to set and stop entire classes of sprawl.
First, who in your tenant can invite guests — the authorization policy’s allowInvitesFrom. Lock this down so random employees cannot sprinkle guests across the directory:
# allowInvitesFrom: everyone | adminsAndGuestInviters
# | adminsGuestInvitersAndAllMembers | none
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" \
--headers "Content-Type=application/json" \
--body '{"allowInvitesFrom": "adminsAndGuestInviters"}'
The four settings and what each means:
allowInvitesFrom value |
Who may invite guests | Posture |
|---|---|---|
everyone |
Any user, including guests themselves | Most permissive (default) — guests can invite guests |
adminsGuestInvitersAndAllMembers |
Admins, Guest Inviters, all members (not guests) | Members can invite; guests can’t |
adminsAndGuestInviters |
Admins + users with the Guest Inviter role only | Recommended — deliberate, role-gated |
none |
Nobody can invite (admin provisioning only) | Locked down; invites only via automation/admin |
adminsAndGuestInviters is the default I recommend: assign the Guest Inviter role to the specific people who legitimately onboard partners (project managers, vendor coordinators, procurement) and nobody else can invite. It scales better than none (which forces every invite through an admin or automation) while being far tighter than everyone (which lets guests invite more guests — a genuine sprawl vector).
Second, which email domains may be invited at all — the B2B management policy. Use an allowlist of trusted partner domains, or a denylist to block consumer/competitor domains:
{
"invitationsAllowedAndBlockedDomainsPolicy": {
"allowedDomains": ["fabrikam.com", "contoso-partner.com"]
}
}
Allowlist versus denylist is a mutually exclusive choice: allowedDomains means only the listed domains can be invited (use it when you know your partners and want tight control), while blockedDomains means everything except the listed domains can be invited (use it when you want broad collaboration but block known-bad consumer or competitor domains).
allowedDomains and blockedDomains are mutually exclusive — you configure one list or the other, never both. An allowlist is the safer posture; pair it with the per-partner XTAP policies so domain and tenant are both constrained. Note the interaction with cross-tenant settings: the domain policy governs invitation (can this email be invited?), while XTAP governs tenant trust (is this partner tenant allowed, and how?). A guest must pass both — the domain must be permitted to invite and their home tenant must be allowed inbound.
Onboarding guests with entitlement management
Manual invitation works for a handful of guests. At any scale, guest onboarding belongs in entitlement management (part of Entra ID Governance, P2), which turns “invite a guest and assign them to things” into a governed, self-service, time-boxed workflow. The building blocks: an access package bundles resources (groups, apps, SharePoint sites); a policy on the package defines who is eligible, what approval is required, and when access expires; and a connected organization scopes which external orgs’ users may even request the package.
The connected-organization concept is what makes this safe for external users: you register the partner’s tenant (or domain) as a connected organization, then a package policy can say “external users from these connected organizations may request this package.” A guest from a random tenant can’t request it; only users from orgs you’ve explicitly connected can.
# Register a connected organization (scopes who can request external access packages)
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/identityGovernance/entitlementManagement/connectedOrganizations" \
--headers "Content-Type=application/json" \
--body '{
"displayName": "Fabrikam (engineering partner)",
"identitySources": [
{
"@odata.type": "#microsoft.graph.azureActiveDirectoryTenant",
"tenantId": "22223333-cccc-4444-dddd-5555eeee6666",
"displayName": "fabrikam.onmicrosoft.com"
}
],
"state": "configured"
}'
The access-package machinery, piece by piece:
| Object | Role | Key settings |
|---|---|---|
| Catalog | A container of resources + packages, with delegated admins | Enable “for external users” |
| Access package | A bundle of resources users request as a unit | Grouped resources; one or more policies |
| Package policy | Who can request, approvals, lifecycle | Requestors, approval stages, expiry |
| Connected organization | An external org whose users may request | Tenant ID or domain identity source |
| Assignment | A user’s active grant of a package | Created on approval; removed on expiry |
The single biggest reason to use entitlement management for guests is automatic lifecycle: a package policy with an expiry means onboarding and offboarding are one policy, not a manual checklist. When the assignment expires, the guest’s access to every resource in the package is removed — and you can configure the guest object itself to be removed when they have no remaining access-package assignments:
{
"expiration": {
"type": "afterDuration",
"duration": "P90D"
}
}
For the guest-object cleanup, entitlement management has a catalog/setting that governs what happens to external users when their last assignment ends:
| Guest-lifecycle option | Effect when last package assignment ends | Use when |
|---|---|---|
| Block from signing in | Guest object remains but can’t sign in | You may re-engage them soon |
| Remove the guest object (after N days) | Guest object deleted after a grace period | Time-boxed engagements; clean offboarding |
| Do nothing | Object and access reviews handle cleanup separately | You govern guests via access reviews instead |
This is the cleanest pattern for time-boxed partner engagements: an auditor needs access for three weeks, a contractor for a six-month project, an acquired company’s team until a directory merge — set the access package to auto-expire, and offboarding happens on schedule with no human in the loop. The full design of multi-stage approvals, separation-of-duties, and catalog delegation is covered in entitlement management access packages; here the point is that guests are the highest-value population to run through it, because they are the ones most likely to be forgotten.
Comparing the onboarding approaches:
| Onboarding method | Approval | Expiry | Offboarding | Best for |
|---|---|---|---|---|
| Manual Graph invitation | None (inviter decides) | None | Manual (someone must remember) | One-off, urgent, small |
| Invitation + access review | None at invite | None | Review removes stale ones | Existing guests you’re cleaning up |
| Entitlement management package | Multi-stage, per policy | Built-in (afterDuration) |
Automatic on expiry | Scaled, governed, time-boxed |
| Cross-tenant synchronization | N/A (provisioning) | N/A | De-provision on scope change | Directory merge, not collaboration |
Governing the guest lifecycle with access reviews
A guest you invited two years ago for a one-week project is now an unmonitored standing credential — the classic audit finding. Two mechanisms keep the population honest: entitlement-management expiry (covered above, best for new guests you onboard through packages) and access reviews (best for the existing guest population and for recurring recertification).
Access reviews (Entra ID Governance / P2) schedule a recurring review of guests — all of them, or guests in specific groups or apps — and can auto-remove anyone whose reviewer doesn’t approve, or who doesn’t self-attest. The teeth are in autoApplyDecisionsEnabled plus a default decision of deny/remove for non-responders:
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/identityGovernance/accessReviews/definitions" \
--headers "Content-Type=application/json" \
--body '{
"displayName": "Quarterly guest access review",
"scope": {
"@odata.type": "#microsoft.graph.accessReviewQueryScope",
"query": "/users?$filter=(userType eq '\''Guest'\'')",
"queryType": "MicrosoftGraph"
},
"settings": {
"recurrence": { "pattern": { "type": "absoluteMonthly", "interval": 3 } },
"autoApplyDecisionsEnabled": true,
"defaultDecisionEnabled": true,
"defaultDecision": "Deny",
"instanceDurationInDays": 14,
"reminderNotificationsEnabled": true
}
}'
The access-review settings that determine whether a review has teeth or is theater:
| Setting | What it does | Recommended for guests |
|---|---|---|
scope.query |
Which population is reviewed | userType eq 'Guest', or a specific group |
recurrence.pattern |
How often it runs | Quarterly (absoluteMonthly, interval 3) |
autoApplyDecisionsEnabled |
Apply the decisions automatically | true — otherwise nothing is removed |
defaultDecisionEnabled + defaultDecision |
What happens to non-responders | Deny — remove guests nobody vouches for |
instanceDurationInDays |
How long reviewers have | 14 days |
| Reviewers | Who decides | Resource/group owners, or self-review + fallback |
recommendationsEnabled |
Show sign-in-based recommendations | true — reviewers see who’s inactive |
The two governance mechanisms are complementary, not redundant — use both. Entitlement-management expiry covers guests you onboard through packages and works continuously (access, and optionally the guest object, is removed on the assignment’s scheduled expiry). Access reviews cover the existing guest population on a periodic cadence (e.g. quarterly), removing access nobody re-approves and guests who don’t respond. New guests get the first; the standing population gets the second.
A subtle but important design choice: who reviews. Self-review (“guest, do you still need this?”) scales but is weak (people always say yes). Owner-review (the internal sponsor or resource owner decides) is stronger but requires that every guest have an identifiable owner — which, conveniently, entitlement-management packages give you for free (the requestor/approver chain). The best programs review guests by the resource they access, with the resource owner as reviewer, and a default-deny for anything unowned. This whole program design — cadence, reviewer selection, escalation, and reporting — is the subject of building an access reviews program; for guests specifically, the non-negotiables are autoApplyDecisionsEnabled: true and defaultDecision: Deny, because a review that removes nothing removes nothing.
Identity providers for guests
What a guest authenticates with depends on the email domain and which IdPs you have configured. This determines the redemption experience and, for domains not backed by Entra, whether you need to set up direct federation.
| Guest’s email | IdP used | Setup required |
|---|---|---|
| Belongs to an Entra tenant | That home Entra tenant | None — B2B uses their tenant automatically |
| Gmail / Google Workspace | Google federation | Register an OAuth client in Google Cloud, add to Entra external IdPs |
| Any other email | Email one-time passcode (OTP) | Enabled by default for B2B; verify it’s on |
| Microsoft personal account | Microsoft account (MSA) | Default |
| Federated domain (partner runs AD FS / Okta / Ping) | SAML/OIDC direct federation | Exchange metadata, configure per domain |
Email one-time passcode (OTP) is the universal fallback: for any email not backed by an Entra tenant, a Microsoft account, or a configured federation, the guest receives a one-time code by email to authenticate. It’s on by default and worth confirming, because if you disable it, guests with arbitrary email domains can’t redeem at all.
SAML/OIDC direct federation is what you set up when a partner uses a non-Entra IdP for their whole company. You federate at the domain level so every @partner.com guest authenticates at the partner’s IdP. The partner gives you their SAML metadata (issuer URI, passive endpoint, signing cert) or OIDC issuer plus client credentials, and you register it:
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/directory/federationConfigurations/graph.microsoft.com%2FsamlOrWsFedExternalDomainFederation" \
--headers "Content-Type=application/json" \
--body '{
"domains": [{ "@odata.type": "microsoft.graph.externalDomainName", "id": "partner.com" }],
"issuerUri": "https://idp.partner.com/saml",
"passiveSignInUri": "https://idp.partner.com/saml/login",
"signingCertificate": "<base64-der-cert>",
"metadataExchangeUri": "https://idp.partner.com/saml/metadata"
}'
The IdP-selection logic and its gotchas:
| Situation | What happens | Gotcha |
|---|---|---|
| Partner has an Entra tenant verifying the domain | B2B uses their Entra tenant | Direct federation is ignored — Entra wins |
| Partner has no Entra tenant, you set up direct federation | Guests auth at the partner’s IdP | Must federate at the domain, exchange metadata |
| Partner has neither, no federation | Email OTP | Works, but weaker; verify OTP is enabled |
| Guest uses a Gmail address | Google federation (if configured) else OTP | Google IdP requires an OAuth client you register |
The most common direct-federation mistake: setting it up for a domain the partner does back with an Entra tenant. If they have an Entra tenant verifying partner.com, B2B uses their Entra tenant and your direct-federation config is simply ignored. Direct federation is specifically for domains not backed by an Entra tenant. Confirm which before you spend an afternoon exchanging SAML metadata that will never be used.
B2B vs External ID for customers (CIAM)
This section keeps the two worlds distinct because conflating them is the expensive error named in the intro. Everything above is workforce-tenant B2B for partners. External ID for customers (CIAM) — the successor to Azure AD B2C — is a different tenant type for customers of an app you build. You don’t invite CIAM users; they self-register through a user flow you attach to your app.
The two models compared across every dimension that matters:
| Dimension | B2B collaboration (workforce) | External ID for customers (CIAM) |
|---|---|---|
| Who the external user is | A partner/vendor/contractor you know | A customer/consumer of your app (the public) |
| Tenant | Your existing workforce tenant | A separate, dedicated external tenant |
| Object type | Guest in your directory | Local account in the external tenant |
| How they get in | You invite; they redeem | They self-register via a user flow |
| Primary auth | Home tenant / external IdP | Email + password, email OTP, social IdPs |
| Reaches your internal apps? | Yes (Teams, SharePoint, LOB) | No — only the customer-facing apps in that tenant |
| Conditional Access | Your workforce CA (scoped to guests) | Separate CA in the external tenant |
| Scale | Hundreds to thousands of partners | Thousands to millions of consumers |
| Branding / sign-up UX | Minimal (redemption page) | Fully custom (company-branded sign-up/sign-in) |
| Billing | Guest MAU + workload licensing | MAU (monthly active users) in the external tenant |
When each is correct, as a decision table:
| If the external users are… | Use | Because |
|---|---|---|
| Employees of partner companies you collaborate with | B2B collaboration | They need your Teams/SharePoint/apps; you know the orgs |
| A trusted org’s team, Teams shared channels only | B2B direct connect | No guest objects needed; boundary trust suffices |
| Customers/consumers signing up for your product | External ID (CIAM) | Public self-registration; keep them out of the employee directory |
| Users of two directories being merged into one | Cross-tenant synchronization | You want members in one tenant, not federation |
Self-service sign-up in a workforce tenant exists too — it lets unmanaged partners onboard themselves into specific apps, and it reuses user-flow-like machinery — but the resulting object is a guest, not a local customer account. It’s a niche middle ground; most designs use B2B invitation/entitlement-management for partners and a proper CIAM tenant for customers. The full CIAM design — user flows, social IdPs, custom attributes, API connectors, and token customization — is a separate topic covered in building customer identity (CIAM) with Entra External ID. The one thing to carry away here: never bolt customer sign-up onto your workforce tenant, and never use a CIAM tenant for partner B2B. Different products, different tenants, different tooling.
Architecture at a glance
Picture your workforce tenant as a walled campus with a single, well-staffed gate. The cross-tenant access policy is the gate’s rulebook. It has a default page that applies to every visitor from an Entra-run organization — and out of the box that page reads “admit anyone for collaboration, refuse everyone for shared channels, trust no one’s MFA.” Behind the default sits a stack of per-organization pages, one per named partner, each of which completely replaces the default page for that partner: it can say “admit only these five people from Fabrikam’s engineering group, only to these two apps, and trust their FIDO2 MFA so they don’t re-prove it at our gate.”
Trace an inbound guest through the gate. Anil from Fabrikam clicks a redemption link (or, if automatic redemption is on, skips even that). The gate first checks the authorization policy — was he invited by someone allowed to invite? Then the B2B management policy — is fabrikam.com an allowed domain? Then the cross-tenant access policy for Fabrikam’s tenant — is Anil in the allowed group, and are the apps he wants in the allowed set? If yes, a guest object is minted (or reactivated) in your directory carrying whatever group memberships and access-package grants he’s entitled to. On every subsequent sign-in, Conditional Access evaluates him under the Guest or external users assignment: it requires MFA, and because your per-org policy set isMfaAccepted: true for Fabrikam, Anil’s home-tenant MFA claim satisfies it — no second prompt. Contrast the B2B direct connect path, which skips the guest object entirely: a user from a trusted partner reaches a Teams shared channel purely on the strength of the tenant boundary trusting them, authenticating wholly in their home tenant, appearing nowhere in your directory.
Now the governance overlay. Anil didn’t get his access by a manual favor; he requested an entitlement-management access package scoped to Fabrikam as a connected organization, an approval stage granted it, and the assignment carries a 90-day expiry — so when the engagement ends, his access (and optionally his guest object) is removed automatically. In parallel, a quarterly access review sweeps every guest in the tenant; anyone whose owner doesn’t re-approve is auto-removed. And entirely off to the side, in a separate external tenant, live your customers — self-registering through user flows, never touching this campus, never in scope of the workforce Conditional Access that governs Anil. That separation is deliberate: partners come through the gate as governed guests; customers live in a different building altogether. Read the whole system as three concentric rings — the trust plane (cross-tenant access settings deciding who and what is trusted), the access plane (entitlement management and Conditional Access deciding what a trusted guest reaches and how they prove themselves), and the lifecycle plane (access reviews and expiry deciding how long they stay).
Real-world scenario
Meridian Manufacturing, a mid-sized industrial group running Microsoft 365 E5 with Entra ID P2, acquired a smaller competitor, Halvorsen Components, and needed Halvorsen’s 40-person engineering team into Meridian’s SharePoint and a custom CAD-review app within one week. Two hard constraints landed on day one. Legal forbade adding Halvorsen’s people to Meridian’s employee directory as full members — the acquisition wasn’t legally closed, and the two payrolls, GALs, and licensing pools had to stay separate. And Meridian’s security team, mid-way through a phishing-resistant MFA rollout, flagged that Halvorsen’s tenant was still SMS-only for MFA. Meridian’s identity architect chose B2B collaboration, not a tenant merge (cross-tenant sync would come later, if the merge legally completed) and not direct connect (they needed SharePoint and a LOB app, not just Teams channels).
The first gotcha surfaced within hours. Following a “reduce friction” instinct, someone set isMfaAccepted: true on Halvorsen’s per-organization policy. Guests immediately sailed past Meridian’s guest Conditional Access policy — the one that required strong MFA — on the strength of an SMS code Meridian did not trust. Trust is binary per partner: you cannot say “accept their MFA but only the strong factors.” Meridian was, in effect, accepting SMS as sufficient for its CAD IP, exactly what the phishing-resistant rollout was meant to eliminate. The sign-in logs made it obvious — the Authentication requirement column showed “singleFactorAuthentication” satisfying a policy that demanded multifactor, because the home-tenant claim flowed through.
The fix was to not trust Halvorsen’s MFA and instead let Meridian’s own Conditional Access re-challenge the guests with a factor Meridian controlled — an authenticator app registered in Meridian’s tenant at first redemption — while keeping inbound collaboration tightly scoped to a single Halvorsen security group and Meridian’s two apps:
{
"inboundTrust": {
"isMfaAccepted": false,
"isCompliantDeviceAccepted": false
},
"b2bCollaborationInbound": {
"usersAndGroups": {
"accessType": "allowed",
"targets": [
{ "target": "4d4d3e3e-2f2f-1a1a-0b0b-9c9c8d8d7e7e", "targetType": "group" }
]
},
"applications": {
"accessType": "allowed",
"targets": [
{ "target": "aaaa1111-2222-3333-4444-bbbbccccdddd", "targetType": "application" },
{ "target": "eeee5555-6666-7777-8888-ffff00001111", "targetType": "application" }
]
}
}
}
The onboarding itself ran through entitlement management: Meridian registered Halvorsen as a connected organization, built an access package bundling the SharePoint site, the CAD app, and a “Halvorsen-Engineering” security group, and set the package policy to auto-expire at the integration deadline (P120D). Halvorsen’s team members requested the package; a Meridian engineering manager approved in a single stage; guests were minted with exactly the right access and a hard expiry. When the directory merge finally completed four months later, offboarding was automatic — the packages expired, the guest access evaporated, and the team was re-provisioned as members via cross-tenant synchronization. No manual offboarding checklist, no orphaned guests, no audit finding.
The lesson written on the whiteboard afterward: “External MFA trust is a per-partner bet on their factor strength, not a friction toggle. When in doubt, keep it false and own the challenge yourself.” The incident and its fix, as a timeline:
| Time | Situation | Action | Effect | What it should have been |
|---|---|---|---|---|
| Day 1, 09:00 | Need Halvorsen eng in SharePoint + CAD app in a week | Chose B2B collaboration | Correct model | — |
| Day 1, 11:00 | Reduce guest friction | Set isMfaAccepted: true for Halvorsen |
Guests bypass strong-MFA CA with SMS | Leave trust false; own the challenge |
| Day 1, 14:00 | Security flags weak auth | Read sign-in logs (authn requirement) | Confirmed SMS claim flowing through | This was the catch |
| Day 1, 16:00 | Fix trust | isMfaAccepted: false + scope to one group + two apps |
Meridian’s CA re-challenges guests | Correct posture |
| Day 2 | Onboard at scale | Connected org + access package (P120D expiry) | Governed onboarding, hard expiry | — |
| Month 4 | Merge completes | Packages expire; re-provision as members via XT sync | Automatic offboarding, clean cutover | The whole point of the expiry |
Advantages and disadvantages
B2B and cross-tenant access give you enormous control, but the model has sharp edges that only reveal themselves in production. Weigh it honestly:
| Advantages (why this model helps you) | Disadvantages (why it bites) |
|---|---|
| Guests authenticate in their home tenant — you never manage their passwords or MFA registration | The default posture is permissive: collaboration open, world trusted equally until you lock it |
| Cross-tenant access settings let you trust partners individually — a vetted partner and a stranger get different rules | Trust is binary per partner — you can’t accept only a partner’s strong MFA factors |
| Inbound MFA trust removes re-registration friction for partners you actually trust | Enabling trust for a weak-MFA partner silently downgrades your own guest CA |
| B2B direct connect enables deep Teams collaboration with zero guest objects to govern | Direct connect only covers Teams shared channels; needs both tenants to agree |
| Entitlement management makes onboarding + offboarding a single, time-boxed policy | Requires Entra ID P2/Governance; more setup than a manual invite |
| Access reviews recertify guests automatically with auto-remove | A review without autoApplyDecisions + default-deny removes nothing — theater |
| Reset-redemption preserves an object’s permissions across a partner’s IdP migration | Deleting a guest to “fix” auth orphans every assignment — a tempting wrong move |
| A separate CIAM tenant keeps customers cleanly out of the employee directory | Mixing the two models (CIAM for partners, or customers in the workforce tenant) is costly rework |
The model is right for essentially all partner collaboration — you want partners to bring their own identities, you want to trust them differently based on their controls, and you want governed, time-boxed access. It bites hardest on organizations that deploy with defaults and never lock them (the world trusted equally), on those who treat MFA trust as a convenience slider rather than a per-partner security bet, and on anyone who confuses the customer (CIAM) and partner (B2B) worlds. Every disadvantage is manageable — but only if you know it exists, which is the point of this article.
Hands-on lab
Configure a hardened cross-tenant access posture end to end using Microsoft Graph, then verify it — all against your own tenant (you’ll use a placeholder partner tenant ID; the reads work regardless, and the writes are reversible). Run in Cloud Shell (Bash) or any shell with the Azure CLI signed in as a Global Administrator or a role with the cross-tenant/policy permissions.
Step 1 — Confirm your context and current default.
az account show --query "{tenant:tenantId, user:user.name}" -o table
# Read the current default cross-tenant posture (note it before changing)
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default" \
--query "{collabIn:b2bCollaborationInbound.usersAndGroups.accessType, directIn:b2bDirectConnectInbound.usersAndGroups.accessType, mfaTrust:inboundTrust.isMfaAccepted}"
Expected: collabIn is allowed (the permissive default), directIn is blocked, mfaTrust is false. This is your baseline; write it down so you can restore it.
Step 2 — Lock down who can invite guests.
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" \
--query "allowInvitesFrom"
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" \
--headers "Content-Type=application/json" \
--body '{"allowInvitesFrom": "adminsAndGuestInviters"}'
Expected: the GET first shows everyone; after the PATCH, a re-read shows adminsAndGuestInviters. Now only admins and Guest Inviters can invite.
Step 3 — Set a domain allowlist for invitations. (Adjust the domains to real partners; this is reversible.)
# The legacy B2B management policy id is well-known; fetch it if unsure
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/legacy/policies" \
--query "value[?displayName=='B2BManagementPolicy'].id" -o tsv
Then patch the allowlist (substitute the returned policy id):
POLICY_ID="<b2b-management-policy-id>"
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/legacy/policies/$POLICY_ID" \
--headers "Content-Type=application/json" \
--body '{"definition":["{\"B2BManagementPolicy\":{\"InvitationsAllowedAndBlockedDomainsPolicy\":{\"AllowedDomains\":[\"fabrikam.com\"]}}}"]}'
Expected: invitations to @fabrikam.com succeed; invitations to any other domain are rejected.
Step 4 — Register a partner organization and scope its inbound access. Use a placeholder partner tenant ID (a real GUID format; the POST creates a policy object regardless).
PARTNER_TENANT="22223333-cccc-4444-dddd-5555eeee6666"
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/partners" \
--headers "Content-Type=application/json" \
--body "{\"tenantId\": \"$PARTNER_TENANT\"}"
# Read it back — it's seeded from the default
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/partners/$PARTNER_TENANT" \
--query "{collabIn:b2bCollaborationInbound.usersAndGroups.accessType, mfaTrust:inboundTrust.isMfaAccepted}"
Expected: a per-organization policy now exists for that tenant, initially inheriting the default’s values.
Step 5 — Configure inbound trust and automatic redemption for the partner. (Only do this if you’d genuinely trust the partner — here it’s a lab exercise on a placeholder.)
cat > partner-trust.json <<'JSON'
{
"inboundTrust": {
"isMfaAccepted": true,
"isCompliantDeviceAccepted": false,
"isHybridAzureADJoinedDeviceAccepted": false
},
"automaticUserConsentSettings": {
"inboundAllowed": true,
"outboundAllowed": false
}
}
JSON
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/partners/$PARTNER_TENANT" \
--headers "Content-Type=application/json" \
--body @partner-trust.json
Expected: a re-read shows mfaTrust: true and automaticUserConsentSettings.inboundAllowed: true.
Step 6 — Send a test invitation (use a real address you control at an allowed domain, or expect a domain-block error to prove Step 3 works).
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/invitations" \
--headers "Content-Type=application/json" \
--body '{
"invitedUserEmailAddress": "you@fabrikam.com",
"inviteRedirectUrl": "https://myapplications.microsoft.com",
"sendInvitationMessage": false,
"invitedUserType": "Guest"
}'
Expected: with an allowed domain, a guest object is created (invitedUser.id returned) in PendingAcceptance. Try a disallowed domain and confirm the invitation is rejected — that proves the allowlist.
Validation checklist. You’ve locked invitations to a role, constrained invitable domains, registered a partner org, scoped and trusted it deliberately, and confirmed both a successful and a blocked invitation. Map each step to what it proves:
| Step | What you did | What it proves | Real-world analogue |
|---|---|---|---|
| 2 | allowInvitesFrom = adminsAndGuestInviters |
Only deliberate inviters can create guests | Stopping guest sprawl at the source |
| 3 | Domain allowlist | Which external domains can be invited at all | Blocking consumer/competitor domains |
| 4 | Register partner org | Per-org policy overrides the default for that tenant | Onboarding a named partner |
| 5 | Trust + auto-redemption | Deliberate per-partner trust decision | The core B2B security judgment |
| 6 | Invite (allowed vs blocked) | The controls actually enforce | Verifying before declaring victory |
Teardown (restore the baseline).
# Remove the placeholder partner policy
az rest --method DELETE \
--uri "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/partners/$PARTNER_TENANT"
# Restore permissive invite setting if that was your original (or leave it locked — it's safer)
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" \
--headers "Content-Type=application/json" \
--body '{"allowInvitesFrom": "everyone"}'
# Delete any test guest you created (get the id from Step 6)
# az rest --method DELETE --uri "https://graph.microsoft.com/v1.0/users/<guest-id>"
Cost note. None of this incurs Azure resource cost — it’s directory configuration. The only “cost” is licensing: guest MAU and any P1/P2 features (entitlement management, access reviews) you exercise, billed under your Entra plan. Leaving the invite restriction locked (adminsAndGuestInviters) is the safer end-state, so consider not reverting Step 2.
Common mistakes & troubleshooting
External access fails in specific, recognizable ways. First the scannable table you can pull up mid-incident, then the reasoning for the ones that bite hardest.
| # | Symptom | Root cause | Confirm (exact cmd / portal path) | Fix |
|---|---|---|---|---|
| 1 | Guest can’t be invited at all | Domain blocked by B2B management policy, or inviter lacks rights | Check allowInvitesFrom; check allowed/blocked domains |
Add domain to allowlist; grant Guest Inviter role |
| 2 | Guest invited but can’t access anything | Default XTAP blocks inbound, or no per-org policy | GET crossTenantAccessPolicy/default and /partners/{id} |
Add/allow per-org b2bCollaborationInbound |
| 3 | Guest re-prompted for MFA every session | isMfaAccepted false for their tenant (or no home MFA) |
GET /partners/{id} → inboundTrust.isMfaAccepted; sign-in log authn requirement |
Set trust true if you trust their MFA; else expected |
| 4 | Weak MFA guests satisfy strong-MFA CA | isMfaAccepted: true for a weak-MFA partner |
Sign-in logs: authn requirement satisfied by single factor | Set isMfaAccepted: false; let your CA challenge |
| 5 | Guest blocked by Conditional Access unexpectedly | Guest-scoped CA requires compliant device you can’t trust | Sign-in logs → CA policy → failure reason | Trust device claim, or exclude/adjust the CA policy |
| 6 | Shared-channel (direct connect) doesn’t work | Direct connect blocked on one side, or trust not set | Both tenants’ b2bDirectConnect* + inboundTrust |
Enable direct connect + trust on both tenants |
| 7 | Partner changed IdP; guest can’t sign in | Object still bound to old IdP; deleted-and-reinvited would orphan | externalUserState; sign-in error at old IdP |
Reset redemption (keep object), don’t delete |
| 8 | Stale guests everywhere; audit finding | No access reviews / no expiry on invites | Count userType eq 'Guest'; check review definitions |
Access reviews (auto-apply, default-deny) + package expiry |
| 9 | Direct federation set up but ignored | Partner’s domain is backed by an Entra tenant | Check if partner has an Entra tenant verifying the domain | Remove federation; B2B uses their Entra tenant |
| 10 | Guest lands with far more access than intended | XTAP targeting left at AllUsers/AllApplications |
GET /partners/{id} targets |
Scope to a group + specific app IDs |
| 11 | Customers ended up in the employee directory | Customer sign-up bolted onto the workforce tenant | Look for consumer accounts as members/guests in workforce | Move customers to a separate External ID (CIAM) tenant |
| 12 | Automatic redemption on, but guest still prompted | Auto-redemption set on wrong direction, or not on both policies | GET /partners/{id} and /default automaticUserConsentSettings |
Set inboundAllowed/outboundAllowed on the right side |
| 13 | Guest can’t reach a specific app | App not in the per-org policy’s allowed applications | GET /partners/{id} → b2bCollaborationInbound.applications |
Add the app ID (or AllApplications) to the target |
| 14 | Access package request denied for a partner | Partner not registered as a connected organization | GET connectedOrganizations; package policy requestors |
Register connected org; add to package policy scope |
The expanded reasoning for the entries that cause the most wasted hours:
3 & 4. MFA behavior for guests — the two sides of the same coin. If a guest is re-prompted for MFA every session (entry 3), the usual cause is that isMfaAccepted is false for their tenant, so your CA re-challenges them — which is correct if you don’t trust their MFA. But if weak-MFA guests are satisfying your strong-MFA CA (entry 4), someone set isMfaAccepted: true for a partner whose MFA you shouldn’t trust, and their SMS/weak claim now flows through. Confirm both from the sign-in logs: Entra admin center → Identity → Monitoring & health → Sign-in logs, filter User type = Guest, and read the Authentication requirement and Cross-tenant access type columns — they show whether a home-tenant claim satisfied the requirement. Fix: set trust to match reality — true only for partners whose factor strength you’d accept for your own staff.
6. Direct connect requires both sides. Unlike collaboration (where a guest redeems using their own credentials, so the partner needn’t configure anything), B2B direct connect only forms when both organizations’ cross-tenant settings agree. If a shared channel won’t work, check both tenants: your b2bDirectConnectInbound/Outbound and the partner’s matching outbound/inbound, plus inboundTrust (direct connect almost always needs MFA trust because there’s no guest object to re-challenge in your tenant). Fix: coordinate with the partner so both enable direct connect for each other and set the trust the shared-channel experience requires.
7. Reset redemption, never delete. When a partner migrates their IdP (Google → Entra, AD FS → Okta), the guest can no longer authenticate against the old provider. The wrong instinct is to delete the guest and re-invite — which orphans every group membership, app assignment, and access-package grant. Confirm: the guest’s externalUserState and a sign-in failure at the old IdP. Fix: reset redemption (resetRedemption: true with invitedUser.id), which re-issues the invitation while preserving the object and all its permissions; the guest re-redeems against the new IdP and keeps everything.
8. Reviews without teeth remove nothing. A guest population with no access reviews and no invite expiry accretes stale credentials — the most common external-access audit finding. But a review configured without autoApplyDecisionsEnabled: true and a defaultDecision of Deny for non-responders is theater: reviewers ignore it, nothing is removed, and the finding stands. Confirm: count guests (/users?$filter=userType eq 'Guest') and inspect your review definitions’ settings. Fix: recurring reviews with auto-apply and default-deny, plus entitlement-management expiry on new guests so cleanup is continuous rather than only periodic.
11. Customers in the employee directory. If consumer accounts have appeared as members or guests in your workforce tenant, someone bolted customer sign-up onto the wrong tenant. This contaminates your GAL, licensing, and Conditional Access scope. Confirm: look for consumer-shaped accounts (gmail/outlook addresses, no organizational affiliation) in the workforce directory. Fix: stand up a separate External ID (CIAM) tenant and migrate customer identities there; the workforce tenant is for employees and partner guests only.
Best practices
- Lock the default cross-tenant policy first. Decide your baseline posture (block-and-allowlist, or open-and-constrain) deliberately, and set the default before you author any per-partner policy. The default trusts every Entra tenant equally out of the box — that is the thing to fix on day one.
- Make trust a per-partner security decision, never a convenience toggle. Set
isMfaAcceptedonly for partners whose MFA strength you’d accept for your own staff. Trust is binary per partner — you can’t accept only their strong factors, so a weak-MFA partner means accepting weak MFA. - Scope every per-org policy to a group and specific apps.
AllUsers/AllApplicationsis the lazy default that grants a partner’s whole company access to everything. Target the project team’s group and the engagement’s app IDs. - Restrict who can invite. Set
allowInvitesFromtoadminsAndGuestInvitersand assign the Guest Inviter role deliberately. Leaving iteveryonelets guests invite more guests — a real sprawl vector. - Constrain invitable domains with an allowlist. Pair the domain allowlist with per-partner XTAP so both domain and tenant are constrained; an allowlist beats a denylist for a known partner set.
- Onboard guests through entitlement management, not manual invites. Access packages give every guest an owner, an approval trail, and — crucially — an expiry, so offboarding is automatic. Register partners as connected organizations to scope who can request.
- Run guest access reviews with teeth.
autoApplyDecisionsEnabled: trueand adefaultDecisionofDenyfor non-responders. A review that removes nothing is theater and fails audit. - Design guest Conditional Access explicitly. A dedicated policy targeting Guest or external users: require MFA, block legacy auth, shorter sign-in frequency. Roll it out report-only first — you can’t easily test as a partner.
- Reset redemption for IdP migrations; never delete-and-re-invite. Deleting a guest orphans every permission.
resetRedemptionpreserves the object and all its assignments across a partner’s provider change. - Keep the two External ID worlds separate. Partners are B2B guests in your workforce tenant; customers are local accounts in a separate CIAM external tenant. Never bolt customer sign-up onto the workforce tenant, and never use CIAM for partner B2B.
- Prefer direct connect for pure Teams shared-channel collaboration. It creates zero guest objects to govern — nothing to review, expire, or go stale. Just remember both tenants must enable it and the trust it needs.
- Instrument guest sign-ins. Ship sign-in and audit logs to Log Analytics so guest activity is queryable in KQL, retained beyond the default window, and alertable — external access is exactly what auditors and incident responders ask about first.
Security notes
- Treat inbound MFA trust as delegated authentication. When you set
isMfaAccepted: true, you are trusting the partner’s identity system to have genuinely performed MFA. That trust is only as good as their controls, their monitoring, and their willingness to tell you the truth. Extend it to partners whose security posture you’d stake your own access on, and keep the default at no-trust so it’s always a deliberate per-partner grant. - Never set trust on the default policy.
inboundTrustoncrossTenantAccessPolicy/defaultwould trust every external Entra tenant’s MFA/device claims — an enormous, invisible attack surface. Trust belongs exclusively on per-organization policies for named, vetted partners. - Scope tightly to limit blast radius. A guest constrained to one group and two apps who is later compromised can reach only those two apps. A guest granted
AllApplicationsis a lateral-movement path into your whole app estate. Least privilege applies to external identities more than internal ones. - Guest Conditional Access is not optional. Guests arrive from devices and networks you don’t control. A guest-scoped CA policy (require MFA, block legacy auth, short session lifetime) is the border control that trust settings feed into — without it, trust is moot and unmanaged guests walk in with a password.
- Lock down the invitation surface.
allowInvitesFrom = everyoneis a social-engineering vector — an attacker who compromises one guest can invite more guests. Restrict toadminsAndGuestInvitersand monitor invitation audit events. - Enforce lifecycle to close standing credentials. Every stale guest is an unmonitored credential and a phishing target. Entitlement-management expiry and default-deny access reviews turn “standing external access” into “time-boxed, recertified external access” — the difference between passing and failing an access-governance audit.
- Keep customers out of the workforce tenant. Consumer accounts in your employee directory fall into scope of workforce Conditional Access, licensing, and the GAL, and blur the identity boundary. A separate CIAM tenant is a security boundary, not just an organizational nicety.
The security controls mapped to the audit obligations they satisfy — because “we govern external access” is a claim you have to evidence:
| Control | Mechanism | Mitigates | Audit control it evidences |
|---|---|---|---|
| Locked default XTAP | crossTenantAccessPolicy/default restrictive |
Trusting unvetted tenants | Access control policy (ISO A.9 / SOC 2 CC6.1) |
| Per-partner trust decisions | inboundTrust per organization |
Accepting weak external MFA | Authentication strength (NIST 800-63) |
| Guest Conditional Access | CA targeting external users | Unmanaged-device/network access | Logical access controls (CC6.1/6.6) |
| Invite restriction | allowInvitesFrom + Guest Inviter role |
Guest sprawl / social engineering | Least-privilege provisioning (CC6.3) |
| Entitlement-management expiry | Access packages, afterDuration |
Standing external credentials | Timely de-provisioning (CC6.3 / A.9.2.6) |
| Guest access reviews | Recurring, auto-apply, default-deny | Stale unowned guest access | Periodic access recertification (CC6.2) |
| Separate CIAM tenant | External ID for customers | Directory contamination | Segregation / data boundary |
Cost & sizing
External identity in Entra is priced by activity and by feature tier, not by object count, and the drivers are worth understanding before you scale:
- B2B guest MAU (monthly active users). External identities are billed on a monthly-active-user basis under the Entra External ID / External Identities model — you pay for guests who actually sign in, not for every guest object sitting idle. A generous free allowance covers small collaboration footprints; beyond it, per-MAU pricing applies. The practical implication: stale, never-signing-in guests don’t cost money directly, but they are the audit risk, which is why you offboard them regardless.
- P1/P2 feature dependencies. Guest Conditional Access requires Entra ID P1-equivalent licensing (billed via the external-identities MAU model for guests). Entitlement management and access reviews require Entra ID P2 / Entra ID Governance — these are the features that make guest onboarding and lifecycle governable, and they’re the ones with a real licensing floor. If your compliance regime mandates access recertification (most do), the Governance license is effectively non-optional.
- CIAM (External ID for customers) is separately billed. A customer tenant is its own resource with its own MAU-based billing, sized to your consumer population (thousands to millions), entirely separate from your workforce Entra plan. Don’t conflate its cost with B2B.
- The hidden cost is the un-governed state. The expensive outcome isn’t the license — it’s the incident or the audit finding from thousands of stale guests, over-broad trust, or customers in the wrong tenant. Governance features cost money; not having them costs more.
A rough sizing picture for a mid-sized enterprise with active partner collaboration:
| Scenario | Guest MAU | License tier needed | Rough monthly driver | Notes |
|---|---|---|---|---|
| Small partner collaboration | Within free MAU allowance | Entra ID Free (basic B2B) | ~₹0 for B2B activity | CA for guests still wants P1 |
| Regulated enterprise, governed guests | Hundreds–low thousands active | Entra ID P2 / Governance | P2/Governance licensing + MAU beyond free tier | Entitlement mgmt + reviews are the value |
| Guest Conditional Access at scale | Any active guests | P1-equivalent (via external MAU) | Per-MAU beyond free allowance | Required for guest CA |
| Customer-facing app (CIAM) | Thousands–millions | External ID customer tenant | Separate MAU billing, sized to consumers | Entirely separate from workforce plan |
Sizing guidance in prose: if you only collaborate lightly with a few partners, the built-in B2B on Entra ID Free plus a guest CA policy may suffice within the free MAU allowance. The moment you need governed external access — approvals, expiry, recertification — you need Entra ID P2 / Governance, and that’s the right investment because it’s the same feature set your auditors will ask you to evidence. Customer identity is a separate budget line in a separate tenant. And the cheapest thing you can do is offboard aggressively: fewer active guests means lower MAU and less audit surface.
Interview & exam questions
1. What is the difference between B2B collaboration and B2B direct connect? B2B collaboration creates a guest object in your tenant; the guest authenticates in their home directory and can reach Teams, SharePoint, and your apps. B2B direct connect creates no object — trust is established at the tenant boundary and users reach only Teams shared channels, authenticating entirely in their home tenant. Collaboration is broader but creates objects to govern; direct connect eliminates guest sprawl but is limited to shared channels and must be enabled on both sides.
2. Explain the structure of cross-tenant access settings. There is one default policy applying to every external Entra tenant, plus per-organization overrides keyed by partner tenant ID. Each policy has an inbound side (external users into your tenant) and an outbound side (your users into theirs), and each can allow/block collaboration and direct connect, target specific users/groups and apps, set MFA/device trust, and toggle automatic redemption. A per-org policy completely overrides the default for that tenant — it does not merge.
3. What exactly are you accepting when you set isMfaAccepted: true for a partner? You’re accepting the partner tenant’s assertion that the user completed MFA in their home tenant, so their home-tenant MFA claim satisfies your Conditional Access without re-prompting. It’s a per-partner trust decision on the strength of their MFA — and it’s binary: you can’t accept only their strong factors, so if their MFA includes SMS, you’re accepting SMS. Set it only for partners whose MFA you’d accept for your own staff.
4. A guest with SMS-only MFA is satisfying your policy that requires phishing-resistant MFA. Why, and how do you fix it? You set isMfaAccepted: true for that partner, so their weak home-tenant MFA claim flows through and satisfies your CA. Trust is binary — you can’t filter to strong factors. Fix by setting isMfaAccepted: false for that partner and letting your own Conditional Access re-challenge the guest with a factor you control (e.g., an authenticator registered in your tenant). Confirm the issue in the sign-in logs’ Authentication requirement column.
5. A partner is migrating from Google Workspace to their own Entra tenant. What do you do with the existing guests? Reset redemption — re-issue the invitation with resetRedemption: true and the existing invitedUser.id, which keeps the same object ID, group memberships, and app assignments while forcing the guest to re-redeem against the new IdP. Do not delete and re-invite, which would orphan every permission attached to the object.
6. When would you use a separate External ID (CIAM) tenant instead of B2B? When the external users are customers/consumers of an app you build, not partners. CIAM users get local accounts in a dedicated external tenant and self-register through user flows; they never enter your employee directory or fall into your workforce Conditional Access. B2B is for partners you collaborate with, who become guests in your workforce tenant. Mixing them (customers in the workforce tenant, or CIAM for partners) is a costly architecture error.
7. How do you ensure guests are offboarded automatically? Onboard them through entitlement management access packages with an expiry (afterDuration), so access — and optionally the guest object — is removed when the assignment lapses. Layer access reviews with autoApplyDecisionsEnabled: true and a default decision of Deny for non-responders to recertify the existing population. Together they turn standing external access into time-boxed, recertified access.
8. What does the allowInvitesFrom setting control, and what’s a safe value? It controls who in your tenant may invite guests: everyone (any user, including guests), adminsGuestInvitersAndAllMembers, adminsAndGuestInviters, or none. A safe production value is adminsAndGuestInviters — assign the Guest Inviter role to the specific people who legitimately onboard partners. everyone is risky because guests can invite more guests.
9. Does inbound MFA trust weaken your Conditional Access? No. Trust doesn’t weaken CA — it lets a guest satisfy a CA requirement using a claim from their home tenant instead of re-proving it in yours. If no CA policy requires MFA for guests, isMfaAccepted changes nothing. The correct architecture is both: a guest-scoped CA policy that requires MFA, plus per-partner trust so vetted partners satisfy it from home and untrusted ones get re-challenged.
10. Why must B2B direct connect be configured on both tenants, but B2B collaboration only on yours? Direct connect has no invitation flow — a shared channel only forms when both organizations’ cross-tenant settings mutually agree to allow it, so both sides must enable direct connect for each other. Collaboration uses an invitation and redemption flow where the guest redeems with their own credentials, so you can invite a guest even if the partner has configured nothing on their side.
11. What is a connected organization in entitlement management, and why does it matter for guests? A connected organization registers an external org (by tenant ID or domain) so that a package policy can allow its users to request access packages. It scopes external eligibility: a guest from a random tenant can’t request the package, only users from orgs you’ve explicitly connected. It’s what makes self-service external onboarding safe.
12. How do you configure automatic redemption, and what does it change? Set automaticUserConsentSettings.inboundAllowed (and/or outboundAllowed) to true on the relevant cross-tenant policy. It skips the guest’s redemption/consent prompt so they can access resources immediately — a UX optimization for trusted partners. It does not bypass your other controls: per-org targeting, inbound trust, and Conditional Access all still apply.
These map primarily to SC-300 (Identity and Access Administrator) — implement and manage external identities, cross-tenant access settings, B2B/B2B direct connect, entitlement management, and access reviews — and touch SC-900 at a conceptual level. A compact cert mapping for revision:
| Question theme | Primary cert | Objective area |
|---|---|---|
| B2B vs direct connect; XTAP structure | SC-300 | Implement & manage external identities |
| MFA/device trust settings | SC-300 | Manage cross-tenant access settings |
| Reset redemption / guest lifecycle | SC-300 | Manage the identity lifecycle of external users |
| Guest Conditional Access | SC-300 | Plan & implement Conditional Access |
| Entitlement management + access reviews | SC-300 | Implement access governance (Entra ID Governance) |
| B2B vs CIAM decision | SC-300 / SC-900 | External identities; identity concepts |
Quick check
- A guest is re-prompted for MFA every time they access your app, even though they did MFA in their home tenant. What single setting controls whether their home MFA is accepted, and where do you set it?
- True or false: setting
inboundTrust.isMfaAccepted: trueon the default cross-tenant access policy is a reasonable way to reduce friction for all partners. - A partner is switching their identity provider from AD FS to Okta and their guests can no longer sign in. What operation preserves the guests’ permissions while letting them re-authenticate against the new IdP?
- You need two engineering teams to co-work in a Teams shared channel with no objects created in either directory. Which model, and what’s the one configuration gotcha?
- Your compliance team asks how you ensure external access is time-boxed and recertified. Name the two Entra governance mechanisms and the one setting on each that gives it teeth.
Answers
isMfaAcceptedin the inbound trust settings, set on the per-organization cross-tenant access policy for that partner’s tenant (crossTenantAccessPolicy/partners/{tenantId}). Setting ittruemakes the guest’s home-tenant MFA claim satisfy your Conditional Access without a re-prompt — but only do so if you trust that partner’s MFA strength.- False. Trust on the default policy would accept the MFA/device claims of every external Entra tenant — an enormous, invisible attack surface. Trust belongs only on per-organization policies for named, vetted partners; the default should extend no trust.
- Reset redemption — re-issue the invitation with
resetRedemption: trueand the existinginvitedUser.id. This keeps the same object ID, group memberships, and app assignments while forcing re-redemption against the new IdP. Deleting and re-inviting would orphan every permission. - B2B direct connect (used by Teams shared channels / Teams Connect) — it creates no guest object in either directory. The gotcha: it must be enabled on both tenants (each allowing direct connect to the other), and typically needs inbound MFA trust because there’s no guest object to re-challenge in your tenant.
- Entitlement management (access packages) with an expiry (
expiration.type: afterDuration) so access is removed automatically, and access reviews withautoApplyDecisionsEnabled: trueplus adefaultDecision: Denyfor non-responders so stale guests are actually removed. Both together cover new onboarding and existing-population recertification.
Glossary
- Entra External ID — the umbrella brand covering both workforce-tenant B2B (partner collaboration) and the separate External ID for customers (CIAM); the same name spans two structurally different products.
- B2B collaboration — external users represented as guest objects in your tenant, authenticating in their home directory or via an external IdP and redeeming an invitation; carries group memberships and app assignments.
- B2B direct connect — mutual tenant-boundary trust with no guest object, admitting external users to Teams shared channels (Teams Connect) only; must be configured on both tenants.
- Cross-tenant access settings (XTAP) — the control plane governing B2B: one default policy for all external Entra tenants plus per-organization overrides, each with inbound and outbound sides.
- Inbound trust — settings (
isMfaAccepted,isCompliantDeviceAccepted,isHybridAzureADJoinedDeviceAccepted) that let a guest satisfy your Conditional Access using a claim from their home tenant instead of re-proving it in yours. - Automatic redemption —
automaticUserConsentSettings.inboundAllowed/outboundAllowed; skips the guest’s redemption/consent prompt so they access resources immediately. - Redemption /
externalUserState— the process (and the object field tracking it) by which a guest accepts an invitation and links their IdP;PendingAcceptance→Accepted. - Reset redemption — re-issuing an invitation (
resetRedemption: truewithinvitedUser.id) to preserve an existing guest object and all its permissions while forcing re-authentication against a changed IdP. - Authorization policy (
allowInvitesFrom) — the tenant setting controlling who may invite guests:everyone,adminsGuestInvitersAndAllMembers,adminsAndGuestInviters, ornone. - B2B management policy — the legacy policy holding the invitation domain allowlist or denylist (
invitationsAllowedAndBlockedDomainsPolicy); the two lists are mutually exclusive. - Guest Inviter — the directory role that grants a user the ability to invite guests when
allowInvitesFromisadminsAndGuestInviters. - Connected organization — an external organization (by tenant ID or domain) registered in entitlement management so that its users may request access packages.
- Access package — an entitlement-management bundle of resources (groups, apps, sites) that users request as a unit, governed by a policy defining requestors, approvals, and expiry.
- Access review — an Identity Governance construct that recertifies access on a recurring schedule and can auto-remove non-approved or non-responding users (
autoApplyDecisionsEnabled,defaultDecision). - Direct federation (SAML/OIDC) — domain-level federation for a partner whose whole company uses a non-Entra IdP (AD FS, Okta, Ping); ignored if the partner’s domain is backed by an Entra tenant.
- Email one-time passcode (OTP) — the universal B2B fallback authentication for emails not backed by an Entra tenant, a Microsoft account, or a configured federation.
- External ID for customers (CIAM) — the successor to Azure AD B2C: a separate external tenant for consumers/customers of an app you build, who self-register through user flows; kept distinct from workforce B2B.
- Cross-tenant synchronization — a provisioning mechanism that makes users from another tenant members of yours (directory merge), distinct from B2B federation where they remain guests.
Next steps
You can now govern external access as a composed posture — locked default, per-partner trust, entitlement-managed onboarding, guest Conditional Access, and recurring reviews. Build outward:
- Next: Designing Conditional Access at Scale: A Persona-Based Policy Framework — build the guest-scoped policies that trust settings feed into, as part of a coherent persona framework.
- Related: Entra ID Governance: Designing Entitlement Management Access Packages — the multi-stage approvals, separation-of-duties, and expiry that make guest onboarding governable.
- Related: Building an Access Reviews Program in Entra ID — the recurring recertification that keeps the guest population honest.
- Related: Building Customer Identity (CIAM) with Entra External ID — the separate-tenant customer model this article deliberately kept distinct from partner B2B.
- Related: Implementing Entra ID Cross-Tenant Synchronization for Multi-Tenant Organizations — when you want to merge directories into members rather than federate as guests.
- Related: Deploying Conditional Access Safely: Report-Only Rollout to Enforcement — the report-only discipline for rolling out guest CA without locking out partners.
- Related: Why Was I Blocked? Diagnosing Conditional Access from Entra Sign-In Logs — reading the sign-in logs when a guest is blocked or a trust claim doesn’t flow.