TenantTool Web
TenantTool Documentation
A centralized management and auditing platform for Microsoft 365 tenants — built for managed service providers who operate fleets of customer tenants.
Tech stack: Blazor Server · .NET 8 · MudBlazor 9 · PostgreSQL · Microsoft Graph · PKCE OAuth2 · Docker + Caddy · ClosedXML
Table of Contents
- Architecture
- Roles & Access
- Dashboard
- Tenant Management
- Tenant Check
- Specialized Reports
- Gold Config
- Standard Config
- Identity Config
- My Org
- Authentication Flow
- Required API Permissions
- Data Model
Architecture
TenantTool is a Blazor Server application running against a PostgreSQL database. All Microsoft 365 operations use delegated PKCE OAuth2 flows — there is no application-only credential for per-tenant reads. Each registered tenant has its own Azure app registration.
| Component | Details |
|---|---|
| Rendering | Blazor Server interactive |
| Database | EF Core 8 + Npgsql (PostgreSQL) |
| Authentication | ASP.NET Core Identity for app login |
| Per-tenant auth | PKCE delegated OAuth2 |
| Token cache | 55 min TTL, 6 token types per tenant |
| Infrastructure | Docker + Caddy reverse proxy |
| Excel | ClosedXML for import/export |
| CI/CD | GitHub Actions → production |
Multi-Client Hierarchy
The data model is scoped: Clients group related Tenants. Application users are assigned to a Client and can only see tenants belonging to that Client. Administrator-tier users (no ClientId set) see everything.
| Entity | Description | Notes |
|---|---|---|
| Client | An MSP customer organization | Holds logo and breakglass name pool preference |
| Tenant | An Azure / M365 tenant | Belongs to a Client; one per client can be flagged IsGold |
| ApplicationUser | A TenantTool login account | ClientId = null = full admin; set = scoped to one client |
Roles & Access
Three application roles control what each user can see and do.
| Role | Name | Access |
|---|---|---|
| Admin | TenantTool.Admin |
Full access — all pages including Clients, Users, Tenant registration, Standard Config, Gold Config, Identity Config, and all reports. Can provision tenants, manage app users, and update standard templates. |
| L1 | TenantTool.L1 |
Restricted write access — can use Gold Config and Identity Config, run all reports, and perform Tenant Checks. Cannot manage clients, users, or tenant registrations. |
| Report | TenantTool.Report |
Read-only — all reports and Tenant Check only. No write operations. |
| Public | (anonymous) | The Quick Check page is accessible without login using ad-hoc credentials. |
Warning: The default seed account is
admin/admin. Change the password immediately on first deploy.
Dashboard
Home — /
Shows three KPI tiles: total tenants, Gold tenants, and active app users. Lists all tenants visible to the current user. Click any tenant to open its hub page with links to Tenant Check and all reports.
Access: All roles
Quick Check — /quick-check
Runs a full Tenant Check with ad-hoc credentials — Tenant ID, App Client ID, and client secret entered directly. No tenant registration required. Uses a client-credential (secret) flow instead of PKCE.
Access: No login required
Info & Permissions Reference — /info
Lists the OAuth redirect URI to register in each tenant's app registration, and all required API permissions grouped by API surface (Graph, Exchange Online, Defender ATP, ARM, Azure Storage).
Access: All roles
Tenant Management
Tenants — /tenants
Register, rename, and delete Azure tenant entries. Two creation modes:
- Manual — paste in the Tenant ID, App Client ID, and client secret from an app registration you created in the Azure Portal.
- Automatic provisioning — authenticates as Global Admin via the Microsoft device code flow, then fully creates the
TenantToolWebapp registration: all required permissions, admin consent, service principal with "assignment required", and a 24-month client secret. No Azure Portal work needed.
Access: Admin only
Clients — /clients
Create, rename, and delete MSP client organizations. Visible to administrator-tier accounts only (users with a ClientId set cannot access this page).
Access: Admin only
Users — /users
Create, edit, and delete TenantTool login accounts. Assign a role and optionally bind each user to a Client for data scoping.
Access: Admin only
Tenant Check
Full Tenant Analysis — /tenant-info/{id}
Authenticates via PKCE, then fetches all tenant data from Microsoft Graph in parallel across eight analysis tabs.
Access: All roles
| Tab | What it checks |
|---|---|
| Summary | Health status overview per platform, tenant country, technical contact, creation date, verified domains |
| Admin | Directory roles, Global Admins, breakglass accounts, MFA coverage for privileged users |
| Identity | All users, deleted users, risky users, MFA/SSPR registrations, sign-in log analysis, license overview |
| Intune | Managed devices, configuration profiles, compliance policies with assignment and success/error/conflict counts |
| Exchange | EOP policies (Anti-Malware, Anti-Spam, Anti-Phishing, Safe Links, Safe Attachments, Quarantine), Secure Score, mailbox sizes |
| Files | SharePoint tenant settings, OneDrive quota overview, sharing and external-access policies |
| Defender | Open alerts, active incidents, endpoint status, exposure level from Microsoft Defender ATP |
| Teams | Teams calling settings, channel overview, membership summary |
| Gold Check | Compares this tenant against the Gold baseline — per Intune profile, compliance policy, and CA policy: Match / Differ / Missing / Extra |
A standalone HTML report can be exported containing all results with embedded styles and the client's logo. The file is fully self-contained and can be emailed or archived without dependencies.
Specialized Reports
Each report authenticates via PKCE and is accessible from the tenant hub. All reports include an export to a standalone HTML file.
| Report | Route | Details |
|---|---|---|
| SharePoint Rights | /tenant-sharepoint/{id} |
Select sites, then fetch Owners / Members / Visitors groups and expand Azure AD security group members |
| SharePoint Storage | /tenant-sp-storage/{id} |
Per-site and per-OneDrive storage used vs. allocated. Export HTML + JSON |
| Azure Blob Storage | /tenant-blob-storage/{id} |
Uses ARM + Azure Storage tokens to list accounts, containers, and blob tree with file sizes. Export HTML + JSON |
| Exchange Mailboxes | /tenant-exchange/{id} |
Shared mailboxes with full-access and read-only delegate permissions via Exchange Online token |
| Teams | /tenant-teams/{id} |
All Teams, their channels, owners, and members |
| Conditional Access | /tenant-conditional-access/{id} |
All CA policies with state, conditions summary, and grant controls |
| Privileged Access | /tenant-privileged/{id} |
All Entra ID directory roles and their current holders |
| PIM Status | /tenant-entra/{id} |
PIM-eligible vs. permanent role assignments; identifies over-privileged accounts |
| Maintenance | /tenant-maintenance/{id} |
Stale users, inactive devices, and other maintenance-relevant findings |
Gold Config
Gold Reference Baseline — /gold-config
Designate one tenant per client as the Gold tenant — the approved configuration baseline. TenantTool syncs that tenant's Intune configuration profiles, compliance policies, and Conditional Access policies into the local database.
Once synced, compare any other tenant against the Gold baseline. Each item shows one of four states:
| State | Meaning |
|---|---|
| ✅ Match | Configuration is identical to Gold |
| ⚠️ Differ | Item exists but configuration differs |
| ❌ Missing | Item exists in Gold but not in this tenant |
| ℹ️ Extra | Item exists in this tenant but not in Gold |
Comparison is by display name. Use the Gold Check tab in Tenant Check to quickly spot configuration drift across managed tenants.
Access: Admin + L1
Standard Config
Provisioning Templates — /standard-config
Defines the templates used when provisioning new or existing tenants. Five tabs, all supporting JSON export and import.
Access: Admin only
| Tab | What it defines | Import formats |
|---|---|---|
| Admin Users | Admin and breakglass accounts — UPN prefix, first/last name, department, company, job title, security group rights, usage location | JSON, Excel |
| Admin Groups | RBAC-bearing groups with role assignments | JSON, Excel |
| Standard Groups | Security / M365 groups with type (Assigned or Dynamic), category, and membership rules | JSON, Excel |
| CA Policies | Conditional Access policy templates stored as JSON. Full typed form editor. Import directly from a live tenant | JSON, from Tenant |
| EOP Policies | Exchange Online Protection templates — Anti-Malware, Anti-Spam Inbound/Outbound, Anti-Phishing, Safe Links, Safe Attachments, Quarantine — with per-field typed editors | JSON |
Importing CA Policies from a Live Tenant
The CA Policies tab includes an Import from Tenant button. Select a source tenant, authenticate via PKCE (scope: Policy.Read.All), and TenantTool fetches all CA policies from Graph API. Pick which ones to bring in as standard templates.
Server-only fields (id, createdDateTime, modifiedDateTime, templateId) are stripped automatically. Existing templates with the same name are updated; new ones are added.
Identity Config
Tenant Identity Provisioning — /tenant-config/identity
Applies standard templates to a specific tenant via Graph API. Four sub-tabs.
Access: Admin + L1
Group Provisioning
Select standard groups (admin and/or standard groups) and provision them in a chosen tenant. Groups are created with the configured type, membership rule, and category. Groups that already exist are skipped.
Admin User Provisioning
Create admin and breakglass accounts from standard templates. A temporary password is generated per account. Manager assignments are set automatically when a manager UPN is specified in the template.
Member User Provisioning
Import a member user Excel file and create accounts in bulk.
Required columns: First Name, Last Name, UserPrincipalName, Company Name, Usage Location
Optional columns: Job Title, Department, Manager (UPN)
Group memberships and manager relationships are set after account creation. Results are shown per-user with per-assignment status.
CA Policy Deployment
Select standard CA policies and deploy them to a target tenant.
Workflow:
- Check for duplicates — any policy with the same display name on the target tenant is skipped and flagged "Already exists".
- Resolve references — group names, user UPNs, and named location names are looked up in the target tenant and replaced with their object IDs. Unresolvable references block deployment for that policy.
- Create policy — the resolved JSON is posted to
/identity/conditionalAccess/policieson the target tenant. New policies are always created as disabled.
Note: CA policies are always created as disabled. Review and enable them manually in the tenant's Azure portal to prevent accidental lockout.
In-session customization
Before deploying, policies can be customized without modifying the stored standard templates:
- Edit — open a per-policy JSON editor. Changes are stored in memory for the current session only.
- Reset — discard in-session edits and revert to the standard template.
- Export JSON — download the current working set (including any in-session edits) as a JSON file.
- Import JSON — load a previously exported or manually crafted JSON file. Policies matching existing standard names override their JSON for the session; unrecognized names are added as temporary session-only policies.
My Org
Organisation Settings — /my-org
Client Logo — Upload a PNG or SVG (max 2 MB). The logo is stored as Base64 and embedded in all exported HTML reports for that client.
Breakglass Name Pool — Choose a themed name pool for breakglass account naming suggestions:
- Saint Seiya
- Greek & Roman Gods
- Norse Mythology
- Arthurian Legends
Each pool contains 50 names. Use "Pick Random" to suggest a name and copy it to clipboard.
Access: Admin only
Authentication Flow
Each report and provisioning operation uses a PKCE delegated OAuth2 flow. The token cache is checked first; if no valid token exists, the user is redirected to Microsoft's authorization endpoint for that specific tenant.
1. TenantTool generates a PKCE code verifier + SHA-256 challenge
and redirects to:
login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize
2. After Microsoft sign-in and consent, the browser is redirected
to /oauth-callback with an authorization code.
3. The callback fires six parallel token exchanges:
- Microsoft Graph
- Exchange Online
- Microsoft Defender ATP
- SharePoint REST
- Azure Resource Manager
- Azure Storage
4. All tokens are stored in TokenCacheService keyed by (userId, tenantDbId)
with a 55-minute TTL. The user is returned to the originating page.
After 55 minutes, the next operation will trigger re-authentication. Each browser session maintains its own token set per tenant.
Required API Permissions
The following delegated permissions must be granted with admin consent on each tenant's app registration. The Automatic Provisioning flow creates all of these automatically.
Microsoft Graph — Delegated
User.Read Organization.Read.All
User.Read.All User.ReadWrite.All
Group.Read.All Group.ReadWrite.All
Directory.Read.All Sites.Read.All
Sites.ReadWrite.All DeviceManagementManagedDevices.Read.All
DeviceManagementConfiguration.Read.All DeviceManagementApps.Read.All
RoleManagement.Read.Directory Policy.Read.All
Policy.ReadWrite.ConditionalAccess IdentityRiskyUser.Read.All
Reports.Read.All AuditLog.Read.All
UserAuthenticationMethod.Read.All SecurityEvents.Read.All
SecurityIncident.Read.All SharePointTenantSettings.Read.All
TeamMember.Read.All Channel.ReadBasic.All
ChannelMember.Read.All TeamSettings.Read.All
TeamsActivity.Read
Office 365 Exchange Online — Delegated
Exchange.Manage
Windows Defender ATP — Delegated
Alert.Read
Machine.Read
Azure Service Management — Delegated
user_impersonation
Azure Storage — Delegated
user_impersonation
Data Model
Key entities persisted in PostgreSQL via Entity Framework Core.
| Entity | Key Fields | Purpose |
|---|---|---|
| Client | Name, LogoBase64, BreakglassNamePool |
MSP customer organization; holds logo and name pool preference |
| Tenant | AzureTenantId, AppClientId, ClientSecret, IsGold |
Azure / M365 tenant registration; one per client can be Gold |
| ApplicationUser | ClientId (nullable), Role |
TenantTool login; null ClientId = administrator |
| StandardUser | Type, UpnPrefix, FirstName, LastName, SecurityGroupsRights |
Admin / breakglass user provisioning template |
| StandardAdminGroup | DisplayName, Description, RbacRole |
RBAC group provisioning template |
| StandardGroup | DisplayName, GroupType, GroupCategory, MembershipRule |
Standard group provisioning template |
| StandardCaPolicy | Name, Comment, ConfigJson |
CA policy template (full Graph API JSON payload) |
| StandardEopPolicy | PolicyType, Name, Comment, ConfigJson |
EOP policy template by type |
| GoldSync | TenantId, LastSyncedAt |
Tracks last Gold sync timestamp per tenant |
| GoldConfigEntry | ExternalId, Name, Category, Platform, SettingsJson |
Intune configuration profile snapshot from Gold tenant |
| GoldComplianceEntry | ExternalId, Name, Category, Platform |
Intune compliance policy snapshot from Gold tenant |
| GoldCaEntry | ExternalId, DisplayName, State, IncludeGroupsJson |
CA policy snapshot from Gold tenant |
TenantTool · Blazor Server .NET 8