🚧 Beta Notice: A new Backstage-powered UI is available at backstage.chrishouse.io. This interface is currently in beta and under active development.
The Azure Self-Service Portal is a lightweight, extensible platform designed to enable end-users to safely provision approved Azure resources through a GitOps workflow. Instead of granting users direct access to Azure, the portal automates infrastructure delivery through pull requests, GitHub Actions, Terraform, and Crossplane.
This ensures:
- Security — No direct Azure access for end users
- Governance — All changes tracked through Git
- Consistency — Reproducible infrastructure modules
- Automation — CI/CD handles validation and deployment
- Transparency — Users can watch deployments live
The primary goal of this project is to provide a self-service, auditable, GitOps-driven deployment experience.
The portal allows users to:
- Browse a catalog of approved blueprints (Terraform modules or Crossplane Claims)
- Provide parameters dynamically via the UI
- Automatically generate a new Git branch containing the requested infrastructure
- Open a pull request back to the infrastructure repository
- View real-time deployment status (Terraform Plan/Apply or Crossplane sync)
- Track created resources and outputs
Infrastructure is provisioned via two complementary approaches:
- Terraform — GitHub Actions executes plan/apply for Azure resources
- Crossplane — Kubernetes-native provisioning for claims (e.g., Redis, databases) that sync automatically via GitOps
The system consists of four core components:
- Provides UI for selecting blueprints and submitting requests
- Shows job history and real-time status
- Fetches data from the backend API
- Hosted on Azure Static Web Apps with CDN
- Acts as a broker between the UI and GitHub
- Generates module files based on blueprint definitions
- Creates branches, commits, and pull requests via the GitHub App installation
- Extracts Terraform outputs from GitHub Action comments
- Normalizes job history for the UI
- Uses Redis for distributed caching and notification storage
- Consumes GitHub webhooks via RabbitMQ for real-time notifications
- Broadcasts notifications to clients via Server-Sent Events (SSE)
- Feature flags via Azure App Configuration for runtime feature toggles
- Argo Rollouts — Blue-green deployments with automated analysis
- Istio Service Mesh — Traffic management, mTLS, observability
- Crossplane — GitOps-driven infrastructure provisioning
- Redis — Distributed cache and notification storage (deployed via Crossplane)
- RabbitMQ — Message queue for webhook-to-notification pipeline (deployed via Crossplane)
- Cert-Manager — Automated TLS certificate management via Let's Encrypt
- Terraform modules live in infra/environments/
- GitHub Actions executes:
- terraform init
- terraform plan
- terraform apply (on merge)
- Applies labels to PRs to show plan/apply status
- Posts Terraform outputs back to the PR as comments
+---------------------------+
| Cloudflare (DNS/CDN) |
+-------------+-------------+
|
+--------------------------------+--------------------------------+
| |
v v
+----------------------------------- Hub Traffic -----+ +---------- Spoke Traffic ------------+
| backstage.chrishouse.io | | portal.chrishouse.io |
| argocd.chrishouse.io | | portal-api.chrishouse.io |
+----------------------------------------------------+ | blog.chrishouse.io |
| +--------------------------------------+
v |
+--------------------------------------------------------------------------------+ |
| AKS Hub Cluster (aks-mgmt-hub) | |
| Management & Platform Services (Control Plane) | |
| +--------------------------------------------------------------------------+ | |
| | Istio Service Mesh | | |
| | +------------------------+ +------------------------+ | | |
| | | Istio Ingress Gateway | | Backstage | | | |
| | | (Hub Services Only) |--->| (Developer Portal) | | | |
| | +------------------------+ +------------------------+ | | |
| +--------------------------------------------------------------------------+ | |
| | |
| +------------------------+ +------------------------+ +------------------+ | |
| | ArgoCD | | Crossplane | | Cert-Manager | | |
| | (GitOps Controller) | | (Infrastructure IaC) | | (TLS Certs) | | |
| +------------------------+ +------------------------+ +------------------+ | |
+--------------------------------------------------------------------------------+ |
| |
| Manages (ArgoCD) |
v v
+---------------------------------------------------------------------------------+
| AKS Spoke Cluster (aks-app-spoke) |
| Application Workloads (Data Plane) |
| +-----------------------------------------------------------------------+ |
| | Istio Service Mesh | |
| | +---------------------------+ | |
| | | Istio Ingress Gateway |---+------------------+---------------+ | |
| | | (App Traffic - Direct) | | | | | |
| | +---------------------------+ v v v | |
| | +-----------+ +-----------+ +-----------+ |
| | | portal-api| | blog | | frontend | |
| | | (Node.js) | | (Static) | | (React) | |
| | +-----------+ +-----------+ +-----------+ |
| +-----------------------------------------------------------------------+ |
+---------------------------------------------------------------------------------+
| | |
v v v
+---------------------------+ +---------------------------+ +---------------------------+
| GitHub | | Azure | | External Services |
| - Source Repositories | | - Key Vault (Secrets) | | - Redis |
| - GitHub Actions (CI/CD) | | - Container Registry | | - RabbitMQ |
| - Webhooks | | - Blob Storage (TF State)| | - Azure App Config |
+---------------------------+ +---------------------------+ +---------------------------+
| Cluster | Purpose | Key Workloads |
|---|---|---|
| aks-mgmt-hub | Management & Control Plane | ArgoCD, Backstage, Crossplane, Cert-Manager |
| aks-app-spoke | Application Data Plane | Portal API, TheBlog, Portal Frontend |
Traffic Flow: Cloudflare routes traffic directly to each cluster's Istio Gateway based on hostname. Hub services (Backstage) go to the hub cluster. Application traffic (portal, blog, API) goes directly to the spoke cluster. The hub is not in the data path for application traffic - if the hub goes down, spoke applications remain accessible.
User Request
|
v
+-------------------+
| Frontend Portal |
+-------------------+
| POST /provision
v
+---------------------------+
| Backend API (Node.js) |
+---------------------------+
| Creates branch and PR
v
+---------------------------+
| GitHub Actions (Plan) |
+---------------------------+
| Labels PR
| Waits for merge
v
+---------------------------+
| GitHub Actions (Apply) |
+---------------------------+
| Posts outputs
v
+---------------------------+
| Frontend Jobs Dashboard |
+---------------------------+
The portal provides real-time notifications for GitHub events using a webhook-to-SSE pipeline:
GitHub Webhooks → Webhook Relay → RabbitMQ → Backend API → SSE → Frontend
- Webhook Relay — Standalone service that receives GitHub webhooks, validates signatures, and publishes to RabbitMQ
- RabbitMQ — Topic exchange with durable queue for reliable message delivery
- Backend NotificationService — Consumes messages, stores in Redis, broadcasts via SSE
- Frontend — Connects to SSE endpoint for live updates, displays toast notifications
workflow_run— CI/CD workflow success/failurepull_request— PR opened/closed/mergedpush— Code pushed to main branchdeployment— Deployment status changes
The portal uses Azure App Configuration for runtime feature flag management. This enables features to be toggled without code deployments.
Azure App Configuration → Backend API → Frontend
↑
Azure Workload Identity (AKS Pod)
- Azure App Configuration — Stores feature flags with labels for environment targeting
- Backend FeatureFlagService — Connects via Azure SDK with managed identity
- Frontend useFeatureFlags Hook — Fetches flags via
/api/features/batchendpoint - User Preferences — Stored in localStorage to override server flags per-user
| Flag | Description | Default |
|---|---|---|
notifications |
Real-time notification toasts and bell icon | true |
GET /api/features— List all feature flagsGET /api/features/:key— Get a specific flagGET /api/features/:key/enabled— Check if a feature is enabledPOST /api/features/batch— Check multiple flags at once (used by frontend)
- Create the flag in Azure App Configuration portal
- Add to backend catalog in
FeatureFlagService.jsif using local fallback - Use
useFeatureFlag("flag-key")hook in frontend components
- Backend loads GH_APP_PRIVATE_KEY_BASE64
- Signs a JWT
- Exchanges JWT for an installation access token
- Uses token to:
- Create branches
- Commit files
- Create pull requests
- Read comments and labels
- Tokens auto-expire (60 minutes)
- No long-lived PATs
- Least privilege access
- Private key stored securely as an Azure secret
Define variables:
variable "github_infra_owner" {}
variable "github_infra_repo" {}
variable "github_app_id" {}
variable "github_installation_id" {}
variable "github_app_private_key_base64" {}Run:
terraform init
terraform applySet:
- GH_INFRA_OWNER
- GH_INFRA_REPO
- GH_APP_ID
- GH_INSTALLATION_ID
- GH_APP_PRIVATE_KEY_BASE64
- Azure authentication variables
The backend runs on AKS with the following components:
- Argo Rollouts — Blue-green deployment with pre/post analysis
- Istio — Service mesh for traffic routing and mTLS
- Redis — Distributed caching (deployed via Crossplane)
- Secrets — Synced from Azure Key Vault via CSI driver
Deployments are triggered via GitHub Actions on push to main.
npm run buildDeployed to Azure Static Web Apps via GitHub Actions. The build uses VITE_API_URL to configure the backend endpoint.
/portal
/frontend # React/Vite SPA
/backend # Node.js API (DDD architecture)
/functions
/github-webhook-relay # Webhook receiver → RabbitMQ publisher
/infra
/modules # Terraform modules (blueprints)
/environments # Per-environment Terraform configs
/crossplane # Kubernetes infrastructure manifests
/cluster-setup # Argo, Istio, Cert-Manager configs
/applications # Application deployments (Rollouts)
/claims # Redis, RabbitMQ via Crossplane
/.github
/workflows # CI/CD pipelines
- PRs must pass terraform plan
- Secrets must never be logged
- GH_* prefix required for GitHub App env vars
- Add module under infra/modules
- Update backend blueprint config
- Rebuild UI
- Done: Dynamic validation
- RBAC and permissions model
- Admin UI for blueprint management
- Done: Cost estimation integration
- Done: workflows with approval gates
- Done: Blueprint versioning
- Done: Notifications
- Done: Feature flags (Azure App Configuration)