Skip to main content

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

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

Tenant scope enforcement: Every tenant selector across the application (Identity Config, Device Config, Mail Config, Standard Config, Policies Provisioning, Reports, etc.) automatically filters to show only tenants belonging to the current user's client. Administrator-tier accounts (ClientId = null) see all tenants.


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 TenantToolWeb app 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.

New User Provisioning

Create a new member-level user directly in a chosen tenant. Select the target tenant, authenticate via PKCE, fill in the user's details (first name, last name, UPN, department, job title, usage location), and optionally assign them to static security groups. Groups are presented in a searchable list showing name and description; only static security groups are shown (dynamic membership and Microsoft 365 groups are excluded).

CA Policy Deployment

Select standard CA policies and deploy them to a target tenant.

Workflow:

  1. Check for duplicates — any policy with the same display name on the target tenant is skipped and flagged "Already exists".
  2. 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.
  3. Create policy — the resolved JSON is posted to /identity/conditionalAccess/policies on 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.

Device Config

Intune Device Management — /tenant-config/device

Manages Intune configuration for a selected tenant. Authenticates via PKCE before accessing management operations.

Access: Admin + L1

App Packaging — /tenant-config/device/packaging

Creates Win32 LOB applications in Intune directly from TenantTool without needing the IntuneWin32AppPackager tool locally.

Two input modes:

Mode Description
Upload .intunewin Upload a pre-packaged .intunewin file produced by the Microsoft Win32 Content Prep Tool. TenantTool extracts the encrypted content and metadata from the file.
Upload source files Upload raw installer files (EXE, MSI, etc.). TenantTool builds the .intunewin package in-memory: creates a content ZIP, encrypts with AES-256-CBC + HMAC-SHA256, generates the Detection.xml manifest.

Configurable fields: App name, publisher, version, description, information URL, privacy URL, developer, owner, notes, featured flag, install/uninstall commands, install behavior (system/user), installer type (EXE/MSI), restart behavior, minimum OS version, supported architectures, target OS versions.

Detection methods: File/folder existence or version, registry key/value, MSI product code.

Upload flow (Graph API):

  1. Create Win32 LOB app entry
  2. Create content version
  3. Create file entry (name, unencrypted size, encrypted size)
  4. Poll for Azure Blob Storage URI
  5. Upload encrypted content in 6 MB chunks with block list
  6. Commit content version
  7. Patch app to activate the committed version

The generated .intunewin can also be downloaded for external use.


Mail Config

Mail Management — /tenant-config/mail

Manages Exchange Online mailboxes and distribution lists for a selected tenant. Requires a PKCE connection to the tenant before any management operations are available.

PKCE scopes required: Group.ReadWrite.All GroupMember.ReadWrite.All User.Read.All offline_access

Once connected, click Load Mailboxes to fetch all user and shared mailboxes via the EXO REST InvokeCommand API.

Access: Admin + L1

Shared Mailboxes tab

Create new shared mailboxes directly in Exchange Online. Fill in display name, alias, and primary SMTP address, then submit. The new mailbox appears in the list after creation.

Mailbox Permissions tab

Select any loaded mailbox (user or shared) to manage its delegation permissions. Account selection uses a searchable autocomplete populated from the loaded mailbox list.

Permission type Operations
Full Access View current delegates, add with automapping ON/OFF toggle, remove
Send As View current trustees, add, remove
Send on Behalf View current delegates, add, remove

Note: AutoMapping status cannot be retrieved from Get-MailboxPermission via EXO REST API — it is only settable at creation time via the toggle.

Distribution Lists tab

Load all distribution lists from Graph API (mail-enabled security groups; M365 unified groups excluded). Select a list to view and manage its members.

Operation Details
Load DLs Fetches all distribution lists for the tenant
View members Shows display name and UPN/email for each member
Add member Searchable autocomplete from loaded mailboxes; resolves by UPN via Graph before adding
Remove member One-click removal per member row

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 GroupMember.ReadWrite.All ⁣

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 ⁣


Firewall rules

Entrant

TCP 8585 ← clients web TCP 1433 ← app (si SQL Server distant)

Sortant — tout en HTTPS/443

login.microsoftonline.com → auth (tout) graph.microsoft.com → Graph API (tout) outlook.office365.com → EXO api.securitycenter.microsoft.com → Defender *.sharepoint.com → SharePoint management.azure.com → Azure ARM *.blob.core.windows.net → Azure Blob storage.azure.com → Azure Storage token api.interfaces.records.teams.microsoft.com → Teams admin API

No other ports—no direct SMTP (emails are sent via Graph sendMail), no ports 25 or 587. Everything else uses HTTPS port 443.

The biggest challenge is *.sharepoint.com and *.blob.core.windows.net, which require a wildcard because the subdomain depends on each client tenant. If your firewall doesn’t support wildcards, you’ll need to manually list the domains for each tenant.


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