Skip to main content

Cleaning Up Test Tenants and Entra Identities

Status: Living reference, started 2026-08-09. Feeds TRA-377 (the not-yet-built automated stale-tenant cleanup process) — this document covers the manual version of that same problem.

The core thing to understand​

Deleting a Tenants/TenantUsers row in the database and deleting the corresponding Entra identity are two completely separate actions. A database delete does nothing to Entra. An orphaned Entra identity left behind after a database cleanup is exactly the vulnerability TRA-375 was built to catch — the database says "nothing here," but Entra still remembers the account, and a fresh registration attempt with that same email can either get silently attached to it (the original bug) or correctly rejected (the fix, confirmed working 2026-08-08).

Why an email can't be reused even after the database row is gone​

InviteUserAsync (the guest-creation path registration uses) checks Entra's own directory, not RoundTrip's database, before inviting someone. If that email already has any Entra identity — guest or member — the invite fails or silently resolves to the existing one, regardless of whether any TenantUsers row exists anywhere. Confirmed real-world cases: support@traxsgroup.com, pete.carroll@traxsgroup.com, hello@traxsgroup.com — all pre-existing Microsoft 365 member accounts, all blocked from registration for this reason (see TRA-384).

Actually freeing up an email — the real, confirmed steps​

Deleting a user in the Entra admin center is a soft delete by default — the account moves to a suspended "Deleted users" state for 30 days before Microsoft automatically purges it. Whether the email is immediately reusable during that window is genuinely unclear from Microsoft's own documentation — don't assume it is.

The reliable path:

  1. Entra admin center → Users → delete the account normally
  2. Users → Deleted users → find the same account → Delete permanently

Step 2 is the one that actually guarantees the identifier is free — a real, documented hard-delete action, not a workaround.

Why this is safe to do manually, but wasn't built into automated cleanup​

TRA-375/377's DeactivateUserAsync deliberately only disables accounts (accountEnabled: false), never deletes — that decision was specifically about what unsupervised, automated cleanup code should be trusted to do, where a bad staleness criterion could silently disable the wrong account with nobody watching. A human reviewing and deleting a specific, known test account directly in the portal is a different, lower-risk situation — exactly the kind of manual escape hatch that design intentionally left available outside application code.

What deleting an Entra identity does not affect​

roundtripapp.onmicrosoft.com is RoundTrip's own separate Entra External ID (CIAM) tenant. A "Member" account created via CreateMemberUserAsync is a self-contained login identity that happens to use a real email address as its sign-in identifier — it is not the actual mailbox. Deleting it has no effect on real email delivery to that address, wherever that's actually hosted.

Before deleting anything — check what's still legitimately in use​

Cross-reference against current Tenants/TenantUsers data before assuming an Entra identity is orphaned. Real categories to watch for:

  • Still-active team members on a kept tenant — e.g., a technician invited onto a tenant you're intentionally keeping. Deleting their Entra identity breaks their real login.
  • Real business-function accounts — e.g., an address also used for live support routing (Crisp), even if it also happens to be blocked from RoundTrip registration testing. No benefit to deleting, some risk.
  • Federated identities (signInType: federated, issuer: ExternalAzureAD) — behave differently than local CIAM accounts; worth understanding before touching, not assumed to be the same as a throwaway test account.
  • Ambiguous account types — a Member-type account tied to an email that should logically correspond to a Guest-created tenant admin is a signal something doesn't match expectations; worth confirming what it's actually for before deleting, not assuming it's safe.