Architecture¶
This page describes the high level design of the portal. It is a reference design: your deployment may differ in names, network layout and the set of enabled features.
Design principles¶
| Principle | Meaning |
|---|---|
| One code base, one image per environment | Every customer environment runs the same code. The build creates the image once and each environment receives it (Deployment and environments). |
| Features by configuration | A configuration file and feature flags decide which plugins load (Configuration). |
| Data stays in the environment | The portal reads from your Azure estate and stores its data in a database in your subscription. |
| Private by default | Database, key vault and container registry have no public access in the reference design, and the web app has private endpoints. Public access to the web app is a setting. |
System context¶
flowchart LR
user[Portal user]
subgraph env["Portal environment (customer subscription)"]
portal[Portal<br/>web app and backend]
end
entra[Microsoft Entra ID]
azure[Azure: Resource Graph,<br/>Cost Management, Defender,<br/>Policy, Log Analytics]
repos[Azure DevOps and GitHub<br/>repositories]
models[Model endpoint<br/>Azure AI Foundry]
mcp[MCP clients<br/>editors and agents]
user -->|HTTPS| portal
mcp -->|HTTPS| portal
portal -->|sign-in, users, groups| entra
portal -->|read| azure
portal -->|read catalog files, pull requests| repos
portal -->|chat completions| models
Components¶
flowchart TB
subgraph web["App Service (Linux container)"]
fe[Frontend<br/>React, Backstage]
be[Backend<br/>Node.js, Backstage]
fe --> be
end
subgraph plugins["Backend plugins"]
cat[Catalog and modules]
ins[Insights:<br/>cost, network, security, compliance]
ag[AI agents]
mr[Module registry]
act[MCP actions]
perm[Permission policy]
end
be --- plugins
pg[(PostgreSQL<br/>Flexible Server)]
sql[(Azure SQL<br/>cost data)]
be --> pg
ins --> sql
ag --> pg
| Component | Purpose |
|---|---|
| Frontend | Single-page web application built on Backstage. Pages: workloads, domains, organisation, docs, create, AI agents, module registry, MCP inspector. |
| Backend | One Node.js process hosting all backend plugins, the MCP server and the AI agent loop. |
| Catalog and modules | Entity model, Azure Resource Graph provider, Azure DevOps provider, merge of fragments. See Catalog and entity model. |
| Insights | Cost, network, security and compliance. See Insights. |
| AI agents | In-process agent loop that calls a model endpoint and the portal's own tools. See AI agents. |
| PostgreSQL | Catalog, plugin data and agent conversations. Entra authentication only. |
| Azure SQL | Cost data prepared by Data Factory. Present only when cost insights are enabled. |
Azure resources per environment¶
The Terraform modules create the following resources for one environment. Optional parts depend on the features you enable.
| Resource | Notes |
|---|---|
| Linux web app with a staging slot | Container image from the registry, system and user-assigned identities, HTTPS only, minimum TLS 1.2, health check on the liveness endpoint, VNet integration |
| App Service plan | The reference environment uses a premium v3 P0v3 plan. Size to your load. |
| PostgreSQL Flexible Server | Version 16, burstable B2s by default, 32 GB storage, 7-day backup, no geo-redundancy, no public access, Entra authentication only, extensions azure_storage, pg_cron and pg_stat_statements |
| Key Vault | RBAC authorisation, purge protection, 7-day soft delete, no public access |
| Container registry | Premium SKU, no public access |
| Application Insights and Log Analytics | Telemetry and logs |
| Private endpoints | For the production and staging slot, and for the supporting services |
| Cost insights (optional) | Data Factory, storage, optional Data Lake Gen2 accounts, Azure SQL (S0 by default), managed identities and failure alert. See Cost insights setup. |
| AI Foundry (optional) | AIServices account, project, model deployments and private endpoint. See AI setup. |
| Alerting (optional) | A PagerDuty service integrated through an Azure Monitor action group |
Existing landing zone
The portal is deployed into an existing spoke. A data-only Terraform module reads the existing spoke resource group, virtual network, subnets, Key Vault and Log Analytics workspace instead of creating them. See Spoke canvas.
Data and network boundaries¶
Two decisions shape the boundaries (Decisions):
- The environment is private. The web app, registry, key vault and database are reached over private endpoints. The provider's deployment runners reach them over private endpoints that the customer approves, and the customer can revoke that connection.
- Data is processed in the customer environment. Cost, security, network and compliance data are read and stored in the customer's subscription, so that the provider does not act as data processor for this data.
flowchart LR
subgraph prov["Provider platform network"]
run[Deployment runners]
end
subgraph cust["Customer subscription"]
subgraph vnet["Spoke VNet"]
pe1[Private endpoints:<br/>storage, key vault,<br/>registry, SQL, web app,<br/>staging slot]
app[Web app<br/>VNet integration]
pg[(PostgreSQL<br/>delegated subnet)]
end
end
run -->|manual connection,<br/>customer approves| pe1
app --> pg
app --> pe1
Public access to the web app has three values in the Terraform module: no public access, full public access (the default), or a restricted value intended for the Azure AI agent service. Use a WAF or an access restriction in front of the web app for user traffic (WAF and custom domain).
Outbound HTTPS from the web app is needed to the service tags AzureResourceManager, AzureActiveDirectory and AzureMonitor, and to graph.microsoft.com. See Prerequisites.
Subnets in the reference design¶
| Subnet | Purpose |
|---|---|
| default | Private endpoints |
| app service | Delegated to the web app (VNet integration) |
| PostgreSQL | Delegated to PostgreSQL Flexible Server |
| container app | Only used when AI agents run with bring-your-own resources |
Cost data flow¶
flowchart LR
cm[Cost Management<br/>FOCUS export] -->|Parquet, Snappy| st[(Storage account /<br/>Data Lake)]
st --> adf[Data Factory<br/>daily pipeline]
adf --> sql[(Azure SQL<br/>views)]
sql --> be[Portal backend]
The portal creates the cost exports itself. See Cost insights setup.