1.3 Deploy services
Outcome
The four runtime services — rcm-core, rcm-app, edi-gateway, edi-app — are running in Azure, configured to talk to master DB, Key Vault, and Service Bus.
Prerequisites
- 1.2 Infrastructure complete.
Service map
Deploys are driven from GitHub Actions workflows under .github/workflows/:
| Workflow | Target |
|---|---|
deploy-dev-rcm-core.yml | App Service slot for rcm-core + slot swap |
deploy-dev-rcm-app.yml | Static Web App for rcm-app + master DB migrate job |
deploy-dev-edi-gateway.yml | App Service slot for edi-gateway + slot swap |
deploy-dev-edi-app.yml | Static Web App for edi-app |
deploy-dev-docs.yml | Matrix workflow that builds + deploys all four docs sites |
Steps
Wire up the self-hosted GitHub Actions runner
All deploy + infra-CI workflows run on the self-hosted runner inside the dev VNet (
vm-runner-dev-eus2-01). The runner's managed identity holds the role assignments for App Service, Static Web Apps, Key Vault, and the master DB. Federated identity on the runner means no client-secret rotation is required.Repo-level Actions variables (not secrets — these are non-sensitive):
Variable Value AZURE_SUBSCRIPTION_IDThe MedSuite DevTest subscription ID AZURE_TENANT_IDThe Azure AD tenant ID AZURE_RG_DEVrg-rcm-dev-eus2KEY_VAULT_NAME_DEVkv-rcm-dev-eus2Secrets are seeded out-of-band into Key Vault (Terraform no longer provisions them — see Secret rotation).
Trigger the deploy workflows
gh workflow run deploy-dev-rcm-core.ymlgh workflow run deploy-dev-rcm-app.yml -f run_migrate_master=true -f run_seed_master=truegh workflow run deploy-dev-edi-gateway.ymlgh workflow run deploy-dev-edi-app.ymlgh workflow run deploy-dev-docs.ymlgh run watchEach backend workflow:
- Builds the workspace with
pnpm turbo build. - Bundles
rcm-core/edi-gatewaywith esbuild (the SFTP / ssh2 native bindings stay external — see commitbc65543). - Deploys the bundle to the App Service staging slot, then runs the slot-swap
CLI scoped to actual
Swapoperations. - Health-checks
/health+/api/healthbefore marking the swap complete.
The SPA workflows deploy directly to the Static Web App with the SPA-fallback config in
staticwebapp.config.jsonso deep links resolve.- Builds the workspace with
Bind managed identities to Key Vault
Each App Service has a system-assigned managed identity. Grant
Key Vault Secrets Useron the Vault so backends can read secrets at startup:for app in app-rcmcore-dev-eus2 app-edigateway-dev-eus2; doPRINCIPAL_ID=$(az webapp identity show \--resource-group rg-rcm-dev-eus2 \--name "$app" \--query principalId -o tsv)az role assignment create \--assignee "$PRINCIPAL_ID" \--role "Key Vault Secrets User" \--scope $(az keyvault show -n kv-rcm-dev-eus2 --query id -o tsv)doneStatic Web Apps also have a system-assigned managed identity used by the SWA linked-API pattern; Terraform grants it the appropriate roles in
infra/terraform/modules/static-web-apps.Verify each service is up
curl -fsSL https://api.rcm.medsuite.com/healthcurl -fsSL https://api.rcm.medsuite.com/api/healthcurl -fsSL https://edi.medsuite.com/healthcurl -fsSL https://edi.medsuite.com/api/healthEach
/healthroute returns{"status":"ok"}. The/api/healthshim was added in commitd955d0fso SWA-routed/api/*traffic also resolves before slot swaps release new revisions.
Validation
| Check | Expected |
|---|---|
gh run list --workflow deploy-dev-rcm-core.yml --limit 1 | latest run is completed/success |
curl api.rcm.medsuite.com/health | 200 + {"status":"ok"} |
curl edi.medsuite.com/health | 200 + {"status":"ok"} |
curl api.rcm.medsuite.com/metrics | 200 (Prometheus exposition) |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
/health returns 503 | Service can't reach master DB | Check the App Service VNet integration + firewall rule on the master DB. PG passwords with ?#&: characters silently empty out — see PG URL gotcha. |
Cannot find secret in App Service logs | Managed identity missing role assignment | Re-run step 3 for the affected app. |
Build fails with turbo daemon errors in CI | Stale Turbo cache | Clear .turbo cache in the workflow or pin the Turbo version. |
Bundle fails with native .node binary | ssh2 / ssh2-sftp-client not external | Confirm esbuild marks them external (commit bc65543). |