Skip to main content
HomeDocsDeployment

Deployment Guide

Two paths to production: managed SaaS (live in 5 minutes) or self-hosted on your own Kubernetes cluster with full infrastructure control. Both paths include HIPAA-compliant infrastructure and zero-downtime deployments.

Deployment pipeline showing SaaS signup flow and self-hosted DEV → UAT → PROD gated release strategy with multi-cloud support
SaaS auto-provisioning or self-hosted gated release pipeline — AKS, EKS, or GKE

Option A: SaaS (Recommended)

The fastest path to production. Sign up at the portal, select your tier, and your tenant is auto-provisioned with Cosmos DB, SFTP credentials, Azure AD, and a 14-day free trial.

1

Sign up and start trial

# Visit the portal
open https://portal.cloudhealthoffice.com

# Sign in with any Microsoft account
# Select tier: Starter | Professional | Enterprise (contact sales for pricing)
# Click "Start Free Trial"

What gets auto-provisioned: Cosmos DB tenant partition, SFTP credentials for clearinghouse integration, Azure AD application, 14-day trial subscription (Stripe), and welcome email with credentials.

Option B: Self-Hosted

For customers requiring on-premise or own-subscription deployment with full infrastructure ownership.

Prerequisites

ToolMinimum Version
Azure CLI2.77.0+
Bicep CLI0.37.0+
kubectl1.28+
Helm3.14+
Docker24+
Node.js18+
# Verify all tools
az --version
az bicep version
kubectl version --client
helm version
docker --version
node --version

Local Development

Start with Docker Compose for local development and testing. This runs MongoDB, Redis, and all core services locally. See the Quick Start for a complete walkthrough.

# Start local stack
docker compose up -d

# Verify health
curl -s http://localhost:5001/health

CI/CD Pipeline

The deployment pipeline uses GitHub Actions with OIDC authentication (no stored secrets) and environment protection rules for approval gates between stages.

Pipeline Stages

StageTriggerWhat Happens
CIPush to mainBuild, test (1,740 tests), security scan, Docker image build
DEVCI passAuto-deploy to DEV AKS, run E2E tests
UATDEV pass + approvalDeploy to UAT, run integration tests, security validation
PRODUAT pass + approvalBlue-green deploy to PROD AKS, smoke tests, rollback-ready

GitHub Actions Setup

# Configure OIDC federated credentials (no stored secrets)
az ad app federated-credential create \
  --id {app-id} \
  --parameters '{
    "name": "github-actions-main",
    "issuer": "https://token.actions.githubusercontent.com",
    "subject": "repo:aurelianware/cloudhealthoffice:ref:refs/heads/main",
    "audiences": ["api://AzureADTokenExchange"]
  }'

Production Deployment

Infrastructure (Bicep)

All Azure infrastructure is defined in Bicep templates under infrastructure/azure/. A single deployment command provisions the complete environment:

# Deploy infrastructure
az deployment sub create \
  --location eastus2 \
  --template-file infrastructure/azure/main.bicep \
  --parameters environment=prod tenantId={tenant}

Kubernetes (Helm)

Deploy all services to AKS (or EKS/GKE) using the Helm chart:

# Add Helm repos
helm repo add argo https://argoproj.github.io/argo-helm

# Deploy Cloud Health Office
helm install cloudhealthoffice ./infrastructure/helm/cloudhealthoffice \
  --namespace cloudhealthoffice \
  --create-namespace \
  --values infrastructure/helm/values-prod.yaml

Secret Management

Production secrets are stored in Azure Key Vault (Premium SKU with HSM-backed keys). Secrets flow to services through two mechanisms — a CI/CD bridge for immediate adoption, and CSI direct mount for zero-secrets-in-Kubernetes.

Architecture

PhaseFlowSecrets in K8s?When to Use
A: CI/CD BridgeKey Vault → GitHub Actions → K8s Secrets → Pod env varsYes (etcd)Initial migration, simpler setup
B: CSI DirectKey Vault → CSI Driver → Pod volume mount (future: file-based provider)NoPlanned — requires file-based ISecretProvider implementation

Setup (Phase A — CI/CD Bridge)

# 1. Provision Key Vault with RBAC, purge protection, and AKS network rules
./scripts/setup-azure-keyvault.sh \
  --resource-group rg-cloudhealthoffice-prod \
  --vault-name cho-app-kv \
  --aks-cluster cho-aks \
  --location eastus \
  --log-analytics cho-logs

# 2. Populate secrets from manifest
cp scripts/secrets-manifest.example.env scripts/secrets-manifest.env
# Edit with real values — NEVER commit this file
./scripts/populate-keyvault-secrets.sh --vault-name cho-app-kv --file scripts/secrets-manifest.env

# 3. Enable in GitHub Actions (workflow falls back to GitHub Secrets if unset)
gh variable set KEY_VAULT_NAME -b 'cho-app-kv'

# 4. Validate access
./scripts/validate-keyvault-access.sh --vault-name cho-app-kv --verbose

Setup (Phase B — CSI Direct Mount)

# 1. Install CSI driver add-on
az aks enable-addons --addons azure-keyvault-secrets-provider \
  --name cho-aks --resource-group rg-cloudhealthoffice-prod

# 2. Enable OIDC issuer and Workload Identity
az aks update --name cho-aks --resource-group rg-cloudhealthoffice-prod \
  --enable-oidc-issuer --enable-workload-identity

# 3. Per-service: create managed identity, federated credential, and RBAC
# See docs/AZURE-KEYVAULT-INSTALLATION.md for the full 30-service checklist

Service Inventory

Each service requires its own Managed Identity for per-service RBAC scoping to Key Vault.

CategoryCountSecret Source
.NET Microservices29ISecretProvider (Phase B) or K8s Secret env vars (Phase A)
Blazor Portal1ISecretProvider (Phase B) or K8s Secret env vars (Phase A)
Non-.NET Containers8K8s Secret env vars only (Phase A — no code changes needed)

Secret Naming Convention

Key Vault does not allow : in secret names. Use -- (double-dash) as the hierarchy delimiter. The ISecretProvider configuration layer maps --: automatically.

Key Vault Name.NET Configuration Key
CosmosDb--ConnectionStringCosmosDb:ConnectionString
Stripe--SecretKeyStripe:SecretKey
AzureAd--ClientSecretAzureAd:ClientSecret

Key Vault Security Configuration

SettingValueRationale
SKUPremiumHSM-backed keys for HIPAA
RBAC AuthorizationEnabledPer-identity scoping (not vault-level access policies)
Purge ProtectionEnabledCannot be disabled once enabled — prevents accidental deletion
Soft Delete90 daysRecovery window for accidental deletions
Network DefaultDenyOnly AKS subnet and Azure trusted services allowed
DiagnosticsAuditEvent → Log Analytics365-day retention for HIPAA audit trail
Rotation Policy90 daysAutomated expiration alerts via rotate-keyvault-secrets.sh

Rotation & Alerting

# Check for secrets expiring within 14 days
./scripts/rotate-keyvault-secrets.sh --vault-name cho-app-kv --days 14

# JSON output for CI/CD alerting (scheduled GitHub Action)
./scripts/rotate-keyvault-secrets.sh --vault-name cho-app-kv --days 30 --format json
Full installation guide

For the complete per-service Workload Identity checklist with step-by-step commands for all 30 services, see AZURE-KEYVAULT-INSTALLATION.md on GitHub.

Post-Deployment Verification

# Health check all services
kubectl get pods -n cloudhealthoffice
kubectl exec -it deploy/claims-service -n cloudhealthoffice -- curl -s localhost:5001/health

# Run E2E test suite
npm run test:e2e -- --environment=prod

# Verify FHIR endpoints
curl https://api.cloudhealthoffice.com/fhir/r4/metadata | jq .status
Full deployment reference

For the complete 3,000+ line deployment guide including secrets management, rollback procedures, and troubleshooting, see DEPLOYMENT.md on GitHub.