Azure Architecture
Last updated: 10/20/2018
Azure Architecture
Overview
This document outlines the Azure architecture for CareHub using managed services designed for HIPAA compliance, security, and scalability.
Status Legend:
- β IMPLEMENTED - Deployed and operational
- π§ PARTIAL - Partially implemented
- π PLANNED - In specification, not yet built
Architecture Diagram β IMPLEMENTED
Internet
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Azure Container Apps β
β (Next.js Full-Stack App) β
β - All Portals β
β - API Routes β
β - Inngest Workflows β
ββββββββββββββββ¬βββββββββββββββββββββββ
β
βββββββββββββββββββββββββΌββββββββββββββββββββββββ
β β β
β ββββββββββββββββββββ β
β β VNet ββ β
β β Integration ββ β
β ββββββββββ¬ββββββββββ β
β β β β
βΌ βΌ βΌ βΌ
βββββββββββββββββββ ββββββββββββ βββββββββββββββββ ββββββββββββββββ
β PostgreSQL β β Blob β β Key Vault β β Log Analyticsβ
β Flexible β β Storage β β (Secrets) β β + App β
β Server β β (PHI) β β β β Insights β
ββββββββββ¬βββββββββ ββββββ¬ββββββ βββββββββ¬ββββββββ ββββββββββββββββ
β β β
β Private Endpoints (VNet) β
βββββββββββββββββ¬ββββββββββββββββ
β
Private DNS Zones
Terraform Module Structure β IMPLEMENTED
infrastructure/terraform/
βββ environments/
β βββ dev/main.tf β
Deployed
β βββ qa/main.tf β
Configured
β βββ prod/main.tf β
Configured
βββ modules/
βββ hipaa-baseline/ β
Log Analytics, App Insights, Azure Policies
βββ networking/ β
VNet, Subnets, NSGs, Private DNS Zones
βββ data-services/ β
PostgreSQL, Storage, Key Vault
βββ app-hosting/ β
Container Apps Environment + App
βββ ai-services/ β
Speech Services, OpenAI
Service Details
App Hosting: Azure Container Apps β IMPLEMENTED
Use for: Full-stack Next.js application (all portals + API)
Architecture:
- Container Apps Environment - Isolated runtime with VNet integration
- Container App - Next.js app with health probes
- User-Assigned Managed Identity - For accessing Azure services
# Container App configuration
resource "azurerm_container_app" "web" {
name = "ca-carehub-web-${var.environment}"
container_app_environment_id = azurerm_container_app_environment.main.id
revision_mode = "Single"
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.app.id]
}
ingress {
external_enabled = true
target_port = 3000
transport = "http"
}
template {
min_replicas = var.min_replicas # 0 for dev, 1+ for prod
max_replicas = var.max_replicas # 3 default
container {
name = "web"
image = var.container_image
cpu = 0.5
memory = "1Gi"
liveness_probe {
transport = "HTTP"
path = "/api/health"
port = 3000
}
}
}
}
Key Features:
- VNet integration for private connectivity to data services
- Health probes for reliability (
/api/health) - Auto-scaling (0-3 replicas in dev, 1-10 in prod)
- Secrets injected via Container Apps secrets (linked to Key Vault)
Pricing:
- Development: ~$10-20/month (scale to zero)
- Production: ~$50-150/month (always-on with scaling)
Networking: VNet + Private Endpoints β IMPLEMENTED
Architecture:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Azure Virtual Network β
β 10.0.0.0/16 β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β App Subnet β β
β β 10.0.1.0/24 β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Container Apps Environment β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Private Endpoints Subnet β β
β β 10.0.2.0/24 β β
β β ββββββββββββ ββββββββββββ ββββββββββββ β β
β β β Storage β β Key Vaultβ β (Future)β β β
β β β Endpoint β β Endpoint β β β β β
β β ββββββββββββ ββββββββββββ ββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PostgreSQL Delegated Subnet β β
β β 10.0.3.0/24 β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β PostgreSQL Flexible Server β β β
β β β (VNet Integration) β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Private DNS Zones:
# Private DNS zones for service resolution
resource "azurerm_private_dns_zone" "postgres" {
name = "privatelink.postgres.database.azure.com"
}
resource "azurerm_private_dns_zone" "blob" {
name = "privatelink.blob.core.windows.net"
}
resource "azurerm_private_dns_zone" "keyvault" {
name = "privatelink.vaultcore.azure.net"
}
Database: Azure Database for PostgreSQL Flexible Server β IMPLEMENTED
Configuration:
resource "azurerm_postgresql_flexible_server" "main" {
name = "psql-carehub-${var.environment}"
resource_group_name = var.resource_group_name
location = var.location
version = "16"
delegated_subnet_id = var.postgres_subnet_id
private_dns_zone_id = var.postgres_dns_zone_id
# SKU - cost-effective dev, performant prod
sku_name = var.postgres_sku # B_Standard_B1ms for dev
storage_mb = var.postgres_storage_mb
backup_retention_days = var.postgres_backup_retention_days
geo_redundant_backup_enabled = var.postgres_geo_redundant_backup
# Authentication - Entra ID + password for dev
authentication {
active_directory_auth_enabled = true
password_auth_enabled = var.postgres_password_auth_enabled
}
}
Environment Settings:
| Setting | Dev | QA | Prod |
|---|---|---|---|
| SKU | B_Standard_B1ms | B_Standard_B2s | GP_Standard_D2s_v3 |
| Storage | 32 GB | 64 GB | 128 GB |
| Backup Retention | 7 days | 14 days | 35 days |
| Geo-Redundant Backup | No | No | Yes |
| High Availability | No | No | Yes |
| Password Auth | Yes | No | No |
Pricing:
- Development: ~$16/month
- Production: ~$130/month + HA
File Storage: Azure Blob Storage β IMPLEMENTED
Use for: PHI documents, uploaded files, generated PDFs
Configuration:
resource "azurerm_storage_account" "phi" {
name = "stcarehub${var.environment}${random_string.storage_suffix.result}"
resource_group_name = var.resource_group_name
location = var.location
account_tier = "Standard"
account_replication_type = "LRS" # GRS for prod
# HIPAA requirements
min_tls_version = "TLS1_2"
enable_https_traffic_only = true
allow_nested_items_to_be_public = false
blob_properties {
versioning_enabled = true # Document history
delete_retention_policy {
days = 30
}
}
}
Containers:
documents- Generated documents (PR-2, care plans, etc.)uploads- User-uploaded filesrecordings- Session recordings (if stored)
Access: Private endpoint only, no public access
Secrets: Azure Key Vault β IMPLEMENTED
Secrets Stored:
DATABASE_URL- PostgreSQL connection stringNEXTAUTH_SECRET- NextAuth.js session secretAZURE_SPEECH_KEY- Speech Services API keyAZURE_OPENAI_KEY- OpenAI API keyTWILIO_AUTH_TOKEN- Twilio for video/SMSGITHUB_TOKEN- GitHub integration (feedback system)
Access Pattern:
// App uses Managed Identity for Key Vault access
import { DefaultAzureCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";
const credential = new DefaultAzureCredential({
managedIdentityClientId: process.env.AZURE_CLIENT_ID,
});
const client = new SecretClient(
process.env.AZURE_KEY_VAULT_URI,
credential
);
AI Services β IMPLEMENTED
Azure Speech Services β IMPLEMENTED
Use for: Session transcription (Twilio recordings β text)
resource "azurerm_cognitive_account" "speech" {
name = "cog-speech-carehub-${var.environment}"
resource_group_name = var.resource_group_name
location = var.location
kind = "SpeechServices"
sku_name = "S0"
}
Integration:
// Batch transcription API
import { createBatchTranscription, getTranscriptionResult } from "@/lib/azure-speech";
// Workflow processes recordings after video session ends
const transcriptionId = await createBatchTranscription(recordingUrls, displayName);
// ... poll for completion ...
const result = await getTranscriptionResult(transcriptionId);
Azure OpenAI β IMPLEMENTED
Use for: Intake analysis, session summarization, care plan suggestions
resource "azurerm_cognitive_account" "openai" {
name = "oai-carehub-${var.environment}"
resource_group_name = var.resource_group_name
location = var.openai_location # eastus2
kind = "OpenAI"
sku_name = "S0"
}
resource "azurerm_cognitive_deployment" "gpt4o" {
name = "gpt-4o"
cognitive_account_id = azurerm_cognitive_account.openai.id
model {
format = "OpenAI"
name = "gpt-4o"
version = "2024-08-06"
}
sku {
name = "Standard"
capacity = var.openai_capacity # 10K TPM for dev
}
}
Monitoring: Log Analytics + Application Insights β IMPLEMENTED
Log Analytics Workspace:
resource "azurerm_log_analytics_workspace" "main" {
name = "log-carehub-${var.environment}"
resource_group_name = var.resource_group_name
location = var.location
sku = "PerGB2018"
retention_in_days = var.log_retention_days # 30 dev, 2190 prod
}
Application Insights:
resource "azurerm_application_insights" "main" {
name = "appi-carehub-${var.environment}"
resource_group_name = var.resource_group_name
location = var.location
workspace_id = azurerm_log_analytics_workspace.main.id
application_type = "web"
}
Diagnostic Settings: All resources send logs to Log Analytics:
- PostgreSQL: query logs, errors
- Storage: blob access, writes
- Key Vault: access attempts
- Container Apps: stdout/stderr, metrics
HIPAA Compliance β IMPLEMENTED
Azure Policies
# HIPAA baseline policies (enabled in qa/prod)
resource "azurerm_subscription_policy_assignment" "hipaa" {
count = var.enable_hipaa_policies ? 1 : 0
name = "hipaa-hitrust-${var.environment}"
subscription_id = data.azurerm_subscription.current.id
policy_definition_id = "/providers/Microsoft.Authorization/policySetDefinitions/a169a624-5599-4385-a696-c8d643089fab"
}
Security Controls
| Control | Implementation | Status |
|---|---|---|
| Encryption at Rest | Azure-managed keys (default) | β |
| Encryption in Transit | TLS 1.2+ enforced | β |
| Network Isolation | VNet + Private Endpoints | β |
| Access Logging | Log Analytics (6-year retention) | β |
| Secret Management | Key Vault with RBAC | β |
| Identity Management | Entra ID + Managed Identity | β |
| Audit Trail | Activity model + Log Analytics | β |
| BAA | Sign with Microsoft | π PENDING |
Identity & Access β IMPLEMENTED
Managed Identity
# User-Assigned Managed Identity for Container App
resource "azurerm_user_assigned_identity" "app" {
name = "id-carehub-app-${var.environment}"
resource_group_name = var.resource_group_name
location = var.location
}
# Role assignments for service access
resource "azurerm_role_assignment" "keyvault_secrets" {
scope = var.key_vault_id
role_definition_name = "Key Vault Secrets User"
principal_id = azurerm_user_assigned_identity.app.principal_id
}
resource "azurerm_role_assignment" "storage_blob" {
scope = var.storage_account_id
role_definition_name = "Storage Blob Data Contributor"
principal_id = azurerm_user_assigned_identity.app.principal_id
}
User Authentication
| Portal | Auth Method | Provider |
|---|---|---|
| Admin | OAuth 2.0/OIDC | Microsoft Entra ID |
| Psych | OAuth 2.0/OIDC | Microsoft Entra ID |
| LPCC | OAuth 2.0/OIDC | Microsoft Entra ID |
| Member | Token-based | Intake token + Session |
CI/CD: GitHub Actions with OIDC β IMPLEMENTED
Authentication Pattern
No stored secrets! Uses Workload Identity Federation:
# .github/workflows/terraform.yml
jobs:
terraform:
permissions:
id-token: write # Required for OIDC
contents: read
steps:
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
Deployment Flow
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β PR Open βββββΊβ Plan Only βββββΊβ Review β
βββββββββββββββ βββββββββββββββ ββββββββ¬βββββββ
β Merge
βΌ
βββββββββββββββ
β Apply β
β (Auto) β
βββββββββββββββ
Cost Estimates
Development Environment
| Service | SKU | Monthly Cost |
|---|---|---|
| Container Apps | Consumption | ~$15 |
| PostgreSQL | B_Standard_B1ms | ~$16 |
| Blob Storage | Standard LRS | ~$5 |
| Key Vault | Standard | ~$1 |
| Log Analytics | 30-day retention | ~$5 |
| App Insights | Included | $0 |
| Speech Services | S0 | ~$10 |
| OpenAI | 10K TPM | ~$10 |
| Total | ~$60/month |
Production Environment
| Service | SKU | Monthly Cost |
|---|---|---|
| Container Apps | Dedicated D4 | ~$150 |
| PostgreSQL | GP_Standard_D2s_v3 + HA | ~$260 |
| Blob Storage | Standard GRS | ~$20 |
| Key Vault | Standard | ~$3 |
| Log Analytics | 6-year retention | ~$50 |
| App Insights | Included | $0 |
| Speech Services | S0 | ~$50 |
| OpenAI | 100K TPM | ~$100 |
| Front Door (optional) | Standard | ~$35 |
| Total | ~$700/month |
Environment Configuration
Required Variables
# environments/dev/main.tf
variable "subscription_id" {
default = "82fa3931-e5a0-400a-94ff-46cefbce3645"
}
variable "environment" {
default = "dev"
}
variable "location" {
default = "eastus2"
}
variable "project" {
default = "carehub"
}
variable "github_token" {
description = "GitHub PAT for feedback system"
sensitive = true
}
Container App Environment Variables
| Variable | Source | Description |
|---|---|---|
NODE_ENV |
Terraform | production/development |
DATABASE_URL |
Secret (Key Vault) | PostgreSQL connection |
AZURE_STORAGE_ACCOUNT_NAME |
Terraform | Storage account |
AZURE_KEY_VAULT_URI |
Terraform | Key Vault URI |
AZURE_CLIENT_ID |
Terraform | Managed Identity |
AZURE_SPEECH_KEY |
Secret | Speech Services |
AZURE_SPEECH_REGION |
Terraform | eastus2 |
AZURE_OPENAI_API_KEY |
Secret | OpenAI key |
AZURE_OPENAI_ENDPOINT |
Terraform | OpenAI endpoint |
AZURE_OPENAI_DEPLOYMENT |
Terraform | gpt-4o |
GITHUB_TOKEN |
Secret | Feedback system |
APPLICATIONINSIGHTS_CONNECTION_STRING |
Secret | App Insights |
Deployment Commands
# Initialize Terraform with backend config
cd infrastructure/terraform/environments/dev
terraform init \
-backend-config="resource_group_name=rg-carehub-tfstate-dev" \
-backend-config="storage_account_name=stcarehubtfdevXXXX" \
-backend-config="container_name=tfstate" \
-backend-config="key=carehub-dev.tfstate"
# Plan changes
terraform plan -out=tfplan
# Apply changes
terraform apply tfplan
# View outputs
terraform output
Scaling Considerations
Horizontal Scaling
# Container App auto-scaling
template {
min_replicas = 1 # Always-on for prod
max_replicas = 10 # Scale up during peak
# Scale rules (optional)
scale {
min_replicas = 1
max_replicas = 10
rules {
name = "http-rule"
http {
metadata {
concurrent_requests = 100
}
}
}
}
}
Database Scaling
- Read Replicas - For reporting workloads (prod)
- Connection Pooling - PgBouncer (built into Flexible Server)
- Storage Auto-grow - Enable for prod
Performance Optimization
- Enable CDN for static assets (Front Door)
- Implement API response caching (Redis - π PLANNED)
- Use database connection pooling
- Optimize container image size
Disaster Recovery π PLANNED
| Component | Strategy | RTO | RPO |
|---|---|---|---|
| PostgreSQL | Geo-redundant backup | 1 hour | 5 min |
| Blob Storage | GRS replication | Minutes | 0 |
| Container Apps | Multi-region deployment | Minutes | 0 |
| Key Vault | Soft delete + backup | Minutes | 0 |
Security Alerts π PLANNED
| Alert | Threshold | Action |
|---|---|---|
| Failed login attempts | > 5 in 5 min | Email ops team |
| Database CPU | > 80% for 10 min | Scale up alert |
| API error rate | > 5% | Page on-call |
| Key Vault access denied | Any | Security review |
| Unusual data access | AI detection | HIPAA incident |