Skip to main content

1.2 Infrastructure

Outcome​

Azure resources for the master DB, tenant DB server, Service Bus (Premium with Private Endpoint), blob storage with SFTP, Key Vault, App Service backends, Front Door, and Static Web Apps are provisioned and reachable from your workstation. In practice this is done via the Terraform under infra/terraform/envs/dev (and prod) — the manual az commands below are reproduced for understanding only.

Prerequisites​

Architecture​

Provisioning sequence​

Two important notes that drive the rest of this guide:

  • Service Bus is Premium with Private Endpoint (hardcoded in Terraform). The Standard tier is not supported — message size and topic scale require Premium.
  • Backends run on App Service (Linux) with slot-based blue-green deploys, not Container Apps. The dev workflows under .github/workflows/deploy-dev-*.yml push to slots and then trigger a swap.

Steps​

  1. Create the resource group

    az group create \
    --name rg-rcm-prod-eastus2 \
    --location eastus2 \
    --tags env=prod app=rcm
  2. Provision the master Postgres flexible server

    az postgres flexible-server create \
    --resource-group rg-rcm-prod-eastus2 \
    --name pg-rcm-master-prod \
    --location eastus2 \
    --tier Burstable --sku-name Standard_B2ms \
    --version 16 --storage-size 64 \
    --admin-user rcm_master_admin \
    --admin-password "$(openssl rand -base64 24)" \
    --high-availability Disabled \
    --public-access 0.0.0.0

    Capture the admin password — store it directly in Key Vault in step 5 (don't save it to a file).

  3. Provision the first tenant DB server

    az postgres flexible-server create \
    --resource-group rg-rcm-prod-eastus2 \
    --name pg-rcm-tenant-eastus2-01 \
    --location eastus2 \
    --tier GeneralPurpose --sku-name Standard_D2s_v3 \
    --version 16 --storage-size 128 \
    --admin-user rcm_tenant_admin \
    --admin-password "$(openssl rand -base64 24)"

    You'll register this in master DB as a db_server row in 1.4 Master DB.

  4. Provision Key Vault, Service Bus, Storage, and Static Web Apps

    # Key Vault
    az keyvault create \
    --resource-group rg-rcm-prod-eastus2 \
    --name kv-rcm-prod-eastus2 \
    --location eastus2 \
    --enable-rbac-authorization true

    # Service Bus — Premium tier required (Private Endpoint, larger msg size)
    az servicebus namespace create \
    --resource-group rg-rcm-prod-eastus2 \
    --name sb-rcm-prod-eastus2 \
    --location eastus2 --sku Premium --capacity 1

    # Storage account with SFTP enabled
    az storage account create \
    --resource-group rg-rcm-prod-eastus2 \
    --name strcmprodeastus2 \
    --location eastus2 \
    --sku Standard_LRS \
    --enable-hierarchical-namespace true \
    --enable-sftp true

    # Static Web Apps for docs (one per docs subdomain — see Phase 1 step 6)
    for site in admin-docs docs edi-docs; do
    az staticwebapp create \
    --resource-group rg-rcm-prod-eastus2 \
    --name swa-rcm-${site}-prod \
    --location eastus2 \
    --sku Standard \
    --source https://github.com/medsuite/eligibility-rcm \
    --branch main --app-location "apps/docs-${site##*-}" --output-location build
    done
  5. Stash credentials in Key Vault

    az keyvault secret set \
    --vault-name kv-rcm-prod-eastus2 \
    --name pg-master-admin-password \
    --value '<paste the master DB password from step 2>'

    az keyvault secret set \
    --vault-name kv-rcm-prod-eastus2 \
    --name pg-tenant-eastus2-01-admin-password \
    --value '<paste the tenant DB password from step 3>'

    The platform code reads these via the @medsuite/key-vault package using DefaultAzureCredential (managed identity in production, your CLI login locally).

  6. Allow your workstation IP for one-time bootstrap access

    MY_IP=$(curl -s https://api.ipify.org)
    for server in pg-rcm-master-prod pg-rcm-tenant-eastus2-01; do
    az postgres flexible-server firewall-rule create \
    --resource-group rg-rcm-prod-eastus2 \
    --name "$server" --rule-name bootstrap-from-laptop \
    --start-ip-address "$MY_IP" --end-ip-address "$MY_IP"
    done

    Remove these rules after 1.7 Smoke tests — the deployed services reach Postgres via private endpoint or VNet integration in production.

Validation​

# Master DB reachable
psql "host=pg-rcm-master-prod.postgres.database.azure.com user=rcm_master_admin dbname=postgres sslmode=require" -c "SELECT now();"

# Tenant DB server reachable
psql "host=pg-rcm-tenant-eastus2-01.postgres.database.azure.com user=rcm_tenant_admin dbname=postgres sslmode=require" -c "SELECT now();"

# Key Vault accessible
az keyvault secret list --vault-name kv-rcm-prod-eastus2 -o table

# Service Bus reachable
az servicebus namespace show -g rg-rcm-prod-eastus2 -n sb-rcm-prod-eastus2 --query "status"

Troubleshooting​

SymptomLikely causeFix
psql: connection refusedNo firewall rule for your IPRe-run step 6.
az keyvault secret list returns 403RBAC not propagatedAdd yourself as Key Vault Administrator: az role assignment create --role "Key Vault Administrator" --assignee $(az ad signed-in-user show --query id -o tsv) --scope $(az keyvault show -n kv-rcm-prod-eastus2 --query id -o tsv)
Storage SFTP not enabled--enable-sftp true requires hierarchical namespaceRecreate with --enable-hierarchical-namespace true.

Next​

1.3 — Deploy services