Skip to content
← Back to blog

Portal of Portals: One Console for Every Admin Portal

19 min readBy Updated
Dark architecture diagram showing Portal 360 routing Microsoft Entra ID, Intune, Azure, and Power Platform through a Next.js gateway and least-privilege policy engine.

Count the admin portals you touched this week. Entra for a user’s sign-in logs. Intune for a device that fell out of compliance. The Azure portal for a runaway cost alert. The Power Platform admin center for a DLP policy. Exchange for a stuck mailbox. Each one is a separate login, a separate navigation model, and a separate place to forget you left a tab open.

I got tired of the bookmark folder. So I built Portal of Portals, branded Portal 360°: a single console that folds all of those surfaces into one authenticated workspace. This is the deep dive I promised when I first mentioned it: what it actually is, why it exists, what it talks to, and how it stays safe while doing it.

One thing up front, because it matters: this is an independent project, not affiliated with, endorsed by, or supported by Microsoft. It’s a community tool I build in the open (well, in a private repo for now). For anything compliance- critical, you should still use Microsoft’s official portals. Portal 360° is about speed on the high-frequency stuff, not replacing the systems of record.

It’s also a handy way to stand up purpose-built consoles for people who don’t need the whole admin universe: a service desk view scoped to password resets, user lookups, and device actions; a manager view with just licensing, group membership, and a few reports; an on-call view that leads with service health and recent incidents. Instead of handing someone the full Entra or Intune portal (and the cognitive load and blast radius that come with it), you can carve out exactly the surface their role actually uses.

Heads up: the repo is currently private, but I’m planning to open source it very soon. If you’d like to help test it out before then, send me a message with your GitHub alias and I’ll add you to it.

What it is

Portal 360° is a unified administration portal for the Microsoft cloud. It brings identity, security, device, collaboration, and platform operations into one experience so IT and MSP teams can run their common workflows without the context-switch tax.

The scope is genuinely broad: about 800 API routes at last count. Today it covers:

  • Microsoft 365: licensing, Copilot usage, and Entra Agent Registry pages
  • Identity & Security: users (including a consolidated “User 360” view), groups, apps and service principals, PIM, entitlement management, sign-ins, Conditional Access, risky users, risky workload identities, Secure Score, and audit logs
  • Azure infrastructure: org hierarchy, resources, networking, VMs, deployment stacks, policy and compliance, Advisor, cost, budgets, forecasts, reservations, and monitoring
  • Power Platform: environments with a self-service request form, Copilot Studio, DLP, governance, billing and capacity, Dataverse analytics, and a tenant-settings reference that stays in sync with Microsoft’s docs
  • Device management: Intune devices, compliance and configuration, reports, and the Windows update catalog
  • Collaboration & health: Exchange mailboxes and folders, Teams, SharePoint, and service health
  • Threat and vulnerability data: a live CVE feed, a Windows CVE report, NVD search, and tenant configuration drift monitoring
  • Multi-tenant operations: cross-tenant user lookup, Conditional Access coverage comparison, a tenant registry, and per-tenant portal roles
  • Operations & integrations: ServiceNow, a human-review queue for Copilot Studio workflows, Azure DevOps, Microsoft Fabric, a reporting hub, Microsoft Foundry, a Graph Explorer, and a DOCX editor

That’s a lot of surface area, which is exactly why the boring parts had to be right from the start: auth, scopes, and rate limits.

Portal 360 home dashboard with a service health strip and grouped tiles for overview, identity and security, Microsoft 365, cloud and data platforms, and monitoring
The Portal 360° home after signing in with Entra ID — live service health up top, then every major service area grouped a click away.

Making 800 routes navigable

A console this wide fails on navigation before it fails on anything else. The left rail carries a tenant switcher, a favorites list you pin yourself, and a “narrow this menu” filter for when you know the page name but not the category. A Ctrl/Cmd+K command palette searches users, groups, devices, alerts, roles, and Foundry resources from anywhere, so the fastest path to a record is usually typing its name rather than clicking through a hierarchy.

Portal 360 App Insights AI analysis page with the left navigation rail expanded into Azure infrastructure categories and a Ctrl/Cmd+K search bar across the top
The navigation rail does the heavy lifting: expandable categories for every service area, with Ctrl/Cmd+K search across the top. The page behind it scans Application Insights instances for exceptions and failed requests.

Why I built it

Two reasons, one selfish and one that generalizes.

The selfish one: I do this work every day, and the portal sprawl is real. The Microsoft admin experience is a federation of consoles, each excellent in isolation and each unaware of the others. A single “who is this user, what do they have, and what’s wrong right now” question can span five portals. I wanted one front door.

The one that generalizes: I kept building smaller tools, Pulse 360° and the Microsoft Communications Portal, and each lived on its own. Portal 360° is the connective tissue. It’s the hub that ties the hubs together, so there’s one place to sign in and one consistent way to act, rather than a folder full of bookmarks pointing at a dozen different security models.

There’s also an honest personal thread here: I’m a Microsoft architect who came to real software development late, through AI-assisted coding. A project this broad is not something I’d have attempted the old way. What changed wasn’t that I got better at TypeScript, it’s that the models got good enough to work inside a harness, which turned “wire this page to the right API with the right scope” into a review problem instead of a research project. That approach gets its own post in Coding with AI. Portal 360° is the biggest thing I’ve built that way.

The goals

I set a few rules before writing much code, and they’ve shaped every decision since:

  1. Security first, always. No token ever reaches the browser. Every route proves the caller is authenticated before it does anything.
  2. Least privilege by default. A page that reads users asks for a read scope, not a write one. Writes are explicit and narrow.
  3. Audit-ready. Actions should be traceable, and sensitive operations should leave a record.
  4. Stay current without babysitting. Where Portal 360° mirrors a Microsoft schema, it should notice when Microsoft changes it automatically.
  5. Be honest about limits. When a data source is an estimate, say so in the UI.

How it works: the BFF architecture

The whole thing is built on a Backend-for-Frontend pattern. Privileged API calls never happen in the browser. The React front end calls the app’s own API routes; those routes run on the server, hold the tokens, and talk to Microsoft on the user’s behalf.

Why?Why BFF instead of calling Graph from the browser?

If the browser holds an access token, that token is exposed to every script on the page and to anyone who cracks open dev tools. A BFF keeps tokens on the server, hands the browser nothing but a session cookie, and gives you one place to enforce auth, scopes, CSRF, and rate limits. For an admin tool touching Graph and ARM, that’s not optional.

The stack is Next.js 16, React 19, TypeScript 5, Tailwind 4, and NextAuth (Auth.js) v5. A few of those are on the bleeding edge: NextAuth v5 is still in beta as I write this, so I pin versions and re-verify behavior when I bump them.

Every API route starts the same way: verify the session, or reject the request. There’s a small helper for it, and the rule is absolute: an API route with no auth check is a bug, and there’s tooling in the repo whose only job is to find routes missing that check.

export async function GET(request: NextRequest) {
  const session = await verifyAuth()
  if (!session) return UNAUTHORIZED_RESPONSE()

  // ...protected logic, using a delegated token acquired server-side
}

Authentication and least-privilege scopes

Users sign in with Entra ID through an app registration. From there, Portal 360° prefers delegated, on-behalf-of tokens over app-only credentials for anything a user triggers. That means the app acts as the signed-in admin, inside their permissions, so RBAC and Conditional Access still apply, and the audit trail shows a person, not a faceless service principal.

Portal 360 tenant-wide user list with search, status badges, and quick actions to create users and invite guests
A read-heavy page like the tenant user list asks only for Graph User.Read.All — the delegated token means it sees exactly what the signed-in admin is allowed to see.
Why?Delegated (OBO) vs. app-only — why it matters

App-only tokens carry the application’s permissions regardless of who’s driving. Powerful, and easy to over-grant. Delegated on-behalf-of tokens carry the user’s effective permissions, so a helpdesk admin can’t suddenly do Global Admin things just because the app could. For a multi-surface admin console, delegated-by-default is the safer posture.

Scopes are centralized in one file so nothing gets sloppy. Reads ask for read scopes; writes name the specific write scope so it surfaces in incremental-consent prompts. A sampling of what’s registered:

  • Graph reads: User.Read.All, Directory.Read.All, Group.Read.All, AuditLog.Read.All, Reports.Read.All, Device.Read.All
  • Graph writes: User.ReadWrite.All, Group.ReadWrite.All, RoleManagement.ReadWrite.Directory, DeviceManagementManagedDevices.ReadWrite.All
  • Azure Resource Manager: https://management.azure.com/user_impersonation
  • Exchange Online: https://outlook.office365.com/EWS.AccessAsUser.All
  • Power Platform: https://api.bap.microsoft.com/.default, https://service.powerapps.com/.default
  • Microsoft Fabric: https://api.fabric.microsoft.com/.default

Keeping the scope map in one place makes over-permissioning a review problem instead of an accident scattered across hundreds of files.

The pages I’d actually demo

Most of Portal 360° is table stakes: a list, a filter, an export. These are the parts that earn the project its existence.

One console, many tenants

If you run more than one tenant, this is the section that matters. The multi-tenant console does a cross-tenant UPN lookup: it searches one user across every registered tenant in a single query, then renders Conditional Access coverage for all tenants in one matrix, which is how you find the customer who never got the policy you thought you’d rolled out everywhere.

The auth model splits deliberately. Your home tenant stays delegated, the same on-behalf-of flow the rest of the portal uses. Secondary tenants are app-only, through a single multi-tenant Entra app that each customer admin-consents once. The tenant registry is a file on the server that stores the Key Vault secret name, never the secret itself; the plaintext is fetched server-side with DefaultAzureCredential. Per-tenant portal roles override the global ones, and every role change appends to an audit log.

Why?Why app-only for secondary tenants is the risky part

Application permissions are tenant-wide and identical in every tenant that consents. There’s no user context to narrow them, so User.Read.All in one customer means User.Read.All in all of them. Grant the smallest set your features genuinely use, and treat the multi-tenant app’s client secret as the single highest-value credential in the whole system.

Workload identity risk, not just user risk

The identity surface covers the usual suspects: users, groups, PIM, entitlement management, Conditional Access, Secure Score. The one that earns its keep is risky service principals, because compromised app registrations are the thing that gets missed. Identity Protection flags them; almost nobody goes looking.

Portal 360 risky service principals page showing high-risk application identities with risk level, risk state, and last updated columns
Identity Protection's risky workload identities, surfaced as a first-class page. App registrations get compromised too, and they rarely have a human watching them.

Insights that name the fix

There’s a small set of pages that don’t just list data: they answer a specific question and hand you the remediation list. MFA gap reports registration coverage, passwordless capability, SSPR registration, and, critically, admins who haven’t registered. Legacy authentication breaks recent sign-ins down by protocol and user. Stale users lists accounts with no sign-in in 30, 60, or 90 days alongside their assigned license count, which is usually the fastest way to find money on the floor.

Configuration drift monitoring

Portal 360° wraps the Microsoft Graph beta admin/configurationManagement endpoints: take a snapshot of tenant configuration, register a monitor, and get a list of drifts when something moves. It covers Conditional Access policies, the authentication methods policy, security defaults, domains, Exchange transport rules and org config, SharePoint settings, and Teams settings. It’s a beta Graph surface, so treat the API shape and its availability as subject to change, and don’t build a compliance process on it yet.

Azure, with AI that stays modest

The Azure surface spans the org hierarchy, resources, networking, VMs, policy, Advisor, and cost. The resource explorer runs Azure Resource Graph queries across every subscription with custom KQL, type filtering, and CSV export.

Portal 360 Azure Resource Graph query interface showing tenant-wide resource inventory across subscriptions
Tenant-wide resource inventory via Azure Resource Graph — custom KQL, type filtering, and CSV export across every subscription.

Individual resources get a diagnostics tab that pulls Azure Resource Health and Advisor into a single scored view with the detected issues and the specific fix for each. It isn’t magic, and it isn’t a new data source. It’s Microsoft’s own signal, collapsed into one screen instead of four blades.

Portal 360 virtual machine AI diagnostics tab showing a health score, detected issues with severity, impact, and recommended fixes
A VM's diagnostics tab: health score, detected issues with severity and impact, and the recommended fix for each — assembled from Azure Resource Health and Advisor.

Devices and the update story

Intune backs the device surface: inventory, compliance and configuration state, policy assignments, and reporting.

Portal 360 Intune managed devices inventory listing enrolled devices with OS, ownership, and compliance status
Intune managed device inventory — every enrolled device with OS, ownership, and compliance status in one list.

The Windows updates catalog is the piece that pays for itself. It joins each Windows quality update to the CVEs it fixes, with CVSS scores and an exploited badge on the ones already being used in the wild. “Which update do I need to chase this week” stops being a research project.

Portal 360 Windows updates catalog listing quality updates with release date, max severity, CVSS score, and linked CVEs flagged as exploited
Windows quality updates joined to the CVEs they fix, with CVSS scores and exploited-in-the-wild badges.

Power Platform governance, including a human in the loop

Environments, DLP, capacity, Dataverse analytics, and a tenant-settings reference that stays in sync with Microsoft’s docs. The environment view splits into Summary, Details, and Analytics tabs with managed-environment controls, and there is a self-service request form so environment creation can be a request instead of a ticket.

Portal 360 Power Platform environment management showing environment counts by type and capacity consumption
Power Platform environments — capacity, state, and resource inventory, with managed-environment controls and a self-service request path.

The governance area also has a human review queue for Copilot Studio. A Copilot Studio topic or a Power Automate flow can pause itself by calling a custom connector webhook action; Portal 360° stores the Power Platform callback URL server-side, shows the request with its connector context, and resumes the workflow only after an authenticated reviewer submits a decision. It’s a small feature with a big implication: agentic workflows get a real approval gate that lives outside the agent.

Purview audit data at volume

Unified Audit Log queries fall over at scale. Portal 360° reads audit records, Copilot usage, and Microsoft 365 usage from a Fabric lakehouse populated by Microsoft PAX, over the lakehouse’s SQL analytics endpoint. The portal is a read-only consumer, with no PAX runtime inside it. Every query runs under the signed-in admin’s identity via on-behalf-of, and only SELECT/WITH statements against a server-side table allowlist are permitted. That last part matters when you’re handing a UI a SQL connection.

A briefer that reads the console for you

There’s an in-portal chat agent, the Tenant Health Briefer, that answers questions like “what should I fix first” and “which pages are failing health checks” by reading the portal’s own drift, health, and KPI data. Its replies link straight to the relevant page. It’s a router, not an oracle, but for a console with 800 routes, a router is worth a lot.

The APIs it talks to

Portal 360° is, fundamentally, a careful aggregator of a lot of APIs. The main ones:

  • Microsoft Graph for identity, groups, devices, audit logs, reports, and most of the M365 surface.
  • Azure Resource Manager (management.azure.com) for subscriptions, resources, cost, policy, and monitoring.
  • Exchange Online (EWS, as the user) for mailbox and folder operations.
  • The M365 admin center’s internal APIs (admin.microsoft.com) for the bits Graph doesn’t expose.
  • Power Platform APIs: the BAP service, Power Apps service, and Fabric for environments, governance, and analytics.
  • Public vulnerability feeds: the MSRC CVRF v3 feed, the NIST NVD API, and the CISA Known Exploited Vulnerabilities catalog. None require auth.
  • Microsoft Defender for Endpoint TVM (securitycenter.microsoft.com) as an optional, higher-fidelity data source.
  • The Fabric SQL analytics endpoint (database.windows.net, delegated) for PAX-exported audit and usage tables.
  • Plus integrations like ServiceNow, Azure DevOps, Application Insights, and Microsoft Foundry.

Those three public feeds power a page called Security Pulse: CVEs disclosed in the last 24 hours, the top critical ones, and vendor advisories, with the CISA KEV catalog marking what’s actually being exploited. No licensing, no tenant data, and it answers “does anything today need my attention” in one screen.

A concrete example shows how much thought the aggregation takes. The Windows CVE Report reproduces Intune’s Autopatch CVE report, which Microsoft doesn’t expose through any API. So Portal 360° ships two interchangeable backends. The default pulls ~90 days of Windows CVEs from the public MSRC feed and joins them against Intune managedDevices to estimate exposure. It works with zero extra licensing, and the UI is honest that the “devices missing update” count is a deliberate undercount. For tenants with Defender for Endpoint Plan 2, an opt-in backend swaps in Defender’s vulnerability assessment for authoritative per-CVE machine counts. Same page, different data source behind an env flag, and the report tells you which one you’re looking at.

Portal 360 NIST NVD vulnerability search with keyword, CVE ID, CVSS severity, and date-range filters
The vulnerability tooling leans on public feeds like the NIST NVD API — zero extra licensing, and the UI is honest that it isn't endorsed or certified by the NVD.

Service health rounds out the aggregation. Issue counts, degraded and extended-recovery services, and clickable incident detail sit on the home page and on their own page, which is usually where a “is it us or is it Microsoft” morning ends.

Portal 360 real-time Microsoft service health monitoring with issue counts and degraded service tracking
Microsoft service health with issue counts, degraded and extended-recovery tracking, and clickable incident detail.

Keeping it secure past the front door

Auth checks are the floor, not the ceiling. A few other layers matter:

  • RBAC on top of Entra. Beyond “are you signed in,” routes can check role and permission, so the app can gate sensitive actions even further than the tenant would.
  • CSRF protection on state-changing requests. Portal 360° validates the request origin and compares a CSRF token using a constant-time comparison, so a malicious site can’t ride a logged-in admin’s session to POST something nasty.
  • Distributed rate limiting. A Redis-backed sliding-window limiter enforces per-user, per-route limits with separate, stricter budgets for writes than reads, and tighter limits for unauthenticated callers. It falls back to in-memory only for local dev; production runs on Redis (authenticated with Entra ID) so limits hold across replicas.
  • Server-side token storage. Tokens live in a server-side store, never in the browser, and the Redis connection itself uses Entra ID auth rather than a connection-string secret where possible.

None of these are exotic. They’re the unglamorous OWASP-adjacent basics that a tool with this much reach absolutely has to get right.

Staying current without babysitting

Two of Portal 360°‘s integration surfaces mirror Microsoft schemas that drift over time: the Power Platform tenant-settings registry and the supported inventory API version. Rather than let those rot, a weekly GitHub Actions workflow diffs them against their upstream Microsoft sources: the Terraform Power Platform provider docs and the inventory-api.md in the Microsoft Docs repo, and opens a pull request whenever it spots a change. New tenant-settings attributes even get stub entries with Microsoft Learn links attached. I review the PR instead of manually chasing a moving target.

There’s also a full interactive API reference baked in. Any authenticated user can hit an OpenAPI schema, Swagger UI, and ReDoc. Swagger’s “Try it out” reuses the current session cookie and forwards the CSRF token automatically, so it works against a running instance with no manual token juggling. That’s a nice payoff of the BFF design.

Where it stands, honestly

Portal 360° is still private while I harden it, and I’m deliberate about that. Eight hundred routes across this many privileged APIs is a big attack surface, and I’d rather ship it when the security story is airtight than rush a public repo.

It’s also worth repeating the disclaimer, because I mean it: this is an independent tool, provided as-is, with no warranty and no Microsoft affiliation. You’re responsible for making sure your use fits Microsoft’s terms and your own compliance requirements, and you should keep using the official admin portals for critical operations. Portal 360° is for the daily grind, not the audit of record.

If a single, security-first console for Microsoft administration sounds useful, or if you’ve solved the portal-sprawl problem a different way, I’d genuinely like to compare notes. I’ll share more as it moves toward something I can show off in public.

Get the latest learnings

Occasional notes on Azure, AI, and cloud architecture. No spam, unsubscribe anytime.

11 min readCopilot Studio

Finding GitHub Copilot Harness Agents Before PPAC Shows Them

The Power Platform admin center still doesn't flag which Copilot Studio agents run on the GitHub Copilot harness. The isCLIAgent property does, and Microsoft has now published governance guidance built on it.

Comments

Comments are hosted by GitHub Discussions. Loading them connects your browser to giscus.app and GitHub.