Executive summary
Meridian Health runs medicine at the scale of a small country: 14 hospitals, ~120 clinics and ambulatory sites, 6 diagnostic labs, 9 imaging centres, a telemedicine platform, a clinical-trials research institute and the corporate functions that keep all of it solvent — roughly 55,000 staff, 180+ applications and about 2.3 PB of medical imaging. Today that estate lives in three data centres (Ashburn VA, Chicago IL, Dublin IE) around an Epic-class EHR, PACS/VNA, LIS/RIS, SAP S/4HANA and an on-prem AD DS forest corp.meridianhealth.org. This document specifies the secure multi-cloud landing zone that becomes the network, identity, governance and data foundation for moving that estate onto Azure (primary East US 2, secondary Central US, EU West Europe) and AWS (primary us-east-1, secondary us-west-2, EU eu-west-1) — without weakening a single compliance obligation on the way.
Those obligations are the hard constraint, not the epilogue. Every decision here must hold up under HIPAA/HITECH, HITRUST CSF, GDPR for EU patients, SOC 2, NIST 800-53 / 800-66, FDA guidance for connected medical devices and 42 CFR Part 2 for behavioural-health records — with US-in-US / EU-in-EU residency enforced structurally (by where accounts, subscriptions and keys physically sit) rather than by a policy memo nobody reads at 02:00. The reference bar is two existing KloudVin designs — the zero-downtime multi-cloud landing zone for a universal bank and the secure multi-cloud landing zone for global logistics — and this one must match their depth while adding what a regulated healthcare provider actually runs: EHR/ADT, HL7/FHIR/DICOM interoperability, a 2.3 PB imaging archive, telemedicine, connected medical devices and a de-identified research zone.
The design rests on six load-bearing decisions. One identity fabric keeps AD DS authoritative on-prem, projects it to a single Entra ID tenant, and federates AWS to Entra as IdP — one joiner-mover-leaver flow for 55,000 people, no second directory to drift out of sync. Private-only PHI: every PHI-bearing PaaS service is reachable solely through private endpoints / PrivateLink with customer-managed keys, and public network paths are denied by policy, not firewall rule. Segmented clinical domains: Clinical, Imaging, Telemedicine, Research and Corporate/Integration each become their own landing zone with default-deny east-west, so a breach in one is not a breach in five. Tiered resilience: a four-tier RTO/RPO model puts the EHR, ADT, results, imaging core and telemedicine into in-country two-region active/active at RTO ≤30 min / RPO ≤5 min. Guardrails as code: Azure Policy at the mh management-group root and AWS Organizations SCPs/RCPs make non-compliance structurally impossible rather than merely discouraged. Brokered interoperability: a single integration engine plus an event mesh carries all HL7 v2, FHIR R4, DICOM and X12 traffic, so every message is transformable, replayable and traceable end to end.
| Decision area | Target-state choice | Why it matters for Meridian |
|---|---|---|
| Cloud posture | Dual-cloud with workload affinity (place each domain where it runs best), not lift-and-shift into both | Avoids doubling cost/ops while keeping a second-cloud exit and DR option |
| Identity | Single Entra ID hub; AD DS stays authoritative; AWS federated via OIDC/SAML; M365 E5; Okta not used | One JML lifecycle, one MFA/PIM story, one audit of who touched PHI |
| Connectivity | Dual ExpressRoute + dual Direct Connect from separate DCs; Virtual WAN + Transit Gateway hubs; SD-WAN to sites | No PHI ever transits the public internet; no single circuit/DC failure severs a cloud |
| Governance | Azure MG root mh + AWS Organizations / Control Tower, policy-as-code, delegated admin |
Guardrails inherit down; teams cannot opt out of residency or encryption |
| Segmentation | Per-domain landing zones, own /22 CIDR, default-deny east-west, microsegmentation for devices | Breach containment; unpatchable medical devices isolated from the EHR |
| PHI data plane | Private endpoints + CMK/HSM + immutable audit on every PHI service; break-the-glass with audit | HIPAA/HITRUST minimum-necessary and access-logging by construction |
| Resilience | Four tiers; Tier-1 in-country active/active (RTO ≤30 m / RPO ≤5 m); EU region as residency boundary | Clinical care and revenue survive a regional loss; EU data stays in EU |
| Residency | US-in-US, EU-in-EU pinned per patient jurisdiction at account/subscription/key level | GDPR + HIPAA residency provable to an auditor, not asserted |
| Interoperability | One Rhapsody-class engine + event mesh; HL7 v2, FHIR R4, DICOM, IHE, X12; resilient replay | End-to-end message traceability; no fragile point-to-point interface sprawl |
| Patient edge | Public-in / private-out via Front Door + WAF + DDoS (Azure) / CloudFront + WAF (AWS) | One hardened attack surface instead of 180 internet-exposed apps |
Target-state overview
The target state reads left to right as five planes: the on-prem data centres and hospital edge; a private-connectivity layer that reaches both clouds without ever touching the internet; the Azure platform and its workload landing zones; the AWS platform and its landing zones; and a hardened patient edge whose origins are always private. Three data centres feed both clouds over redundant circuits; each cloud carries its own governed platform and segmented workloads; and patients, partners and apps only ever meet a WAF-fronted edge — the 180+ back-end applications are never directly exposed.
Each cloud is built as a small set of platform capabilities that never host workloads, plus workload landing zones that do. On Azure that is the mh management-group hierarchy with platform subscriptions mh-plat-identity / -connectivity / -management / -security, an Azure Virtual WAN hub per region carrying an Azure Firewall for egress and east-west inspection, and landing-zone subscriptions mh-lz-clinical / -imaging / -telemed / -research / -corp / -integration / -sandbox per environment. On AWS the mirror is Root > Security > Infrastructure > Workloads{Clinical,Imaging,Research,Corp} > {Prod,NonProd} plus a Sandbox OU, a Transit Gateway hub with a Gateway Load Balancer inspection VPC, and accounts named mh-<purpose>-<env>. The two clouds are deliberately symmetric in intent — same identity, same guardrail classes, same tiering — but idiomatic in mechanism, because forcing Azure to behave like AWS (or vice versa) is how landing zones rot. The Azure spine follows the enterprise-scale landing zone pattern; the AWS spine follows Control Tower multi-account guardrails.
The planes below are the control surfaces every workload inherits. Read this as “what exists once, centrally, so a workload team never re-invents it”:
| Plane | Azure realization | AWS realization | Purpose |
|---|---|---|---|
| Governance / policy | Management groups under root mh; Azure Policy + initiatives; Blueprints/Bicep |
Organizations OUs; SCPs + Resource Control Policies; Control Tower controls | Guardrails inherit down; residency/encryption non-negotiable |
| Identity | Entra ID tenant; PIM; Conditional Access; Entra Connect cloud sync (PHS + SSO) | Federated to Entra as IdP (OIDC/SAML); IAM Identity Center; permission sets | One JML flow, one MFA/PIM control for all 55,000 staff |
| Connectivity | Virtual WAN hub /20 per region; Azure Firewall; Private DNS; ExpressRoute GW |
Transit Gateway; GWLB inspection VPC; Route 53 Resolver; Direct Connect GW | Private east-west + inspected egress; hub-and-spoke |
| Security operations | Microsoft Defender for Cloud; Microsoft Sentinel SIEM; Key Vault (HSM) | Security Hub; GuardDuty; Detective; CloudTrail org trail; KMS | Central detection, immutable audit, CMK custody |
| Management / observability | Azure Monitor; central Log Analytics workspace; Update Manager; Backup | CloudWatch; central log archive account; Systems Manager; AWS Backup | One pane of telemetry; patch + backup as platform |
| Workload landing zones | mh-lz-* subscriptions × env; spoke VNets /22; PE subnets /26 |
Workloads OU accounts × env; spoke VPCs /22; PrivateLink subnets /26 |
Where PHI apps actually run, isolated per domain |
Regions carry two distinct jobs that must never be confused: resilience pairs (two regions in the same country that back each other) and residency boundaries (a region that exists to keep a jurisdiction’s data inside it). Conflating them is how a well-meaning DR failover becomes a GDPR breach. Meridian’s map:
| Region | Cloud | Role | Residency scope | Tier-1 pattern |
|---|---|---|---|---|
East US 2 |
Azure | Primary US | US PHI | Active/active with Central US |
Central US |
Azure | Secondary US | US PHI | Active/active with East US 2 |
West Europe |
Azure | EU primary | EU-only (GDPR) | In-region HA; no US failover |
us-east-1 |
AWS | Primary US | US PHI | Active/active with us-west-2 |
us-west-2 |
AWS | Secondary US | US PHI | Active/active with us-east-1 |
eu-west-1 |
AWS | EU primary | EU-only (GDPR) | In-region HA; no US failover |
Architecture principles and operating model
Principles are the tie-breakers you appeal to when two good options collide at 02:00 during a design review — they only earn their keep if each one is enforceable by a named control and kills a named anti-pattern. Vague virtues (“be secure”) are useless. Meridian’s twelve principles each map to a mechanism and a failure mode it forecloses:
| # | Principle | What it means in practice | How it is enforced | Anti-pattern it kills |
|---|---|---|---|---|
| P1 | PHI is private by default | No PHI service has a public endpoint; access is via PE/PrivateLink only | Azure Policy deny public network access; SCP deny s3:PublicAccessBlock off |
A storage account or S3 bucket silently exposed to the internet |
| P2 | Identity is the perimeter | Every access is an authenticated, authorised, MFA’d, conditional decision | Entra Conditional Access + PIM; IAM Identity Center permission sets | Flat network trust; standing admin credentials |
| P3 | Segment by clinical domain | Each domain is its own landing zone with default-deny east-west | NSG/SG deny-all baseline; firewall-brokered inter-domain flows | Lateral movement from a breached clinic app into the EHR |
| P4 | Guardrails as code, not tickets | Compliance is a policy definition in git, applied at the root | Azure Policy initiatives + AWS SCP/RCP via pipeline | Drift; per-team exceptions that quietly become the norm |
| P5 | Residency is structural | US data in US accounts/subs/keys; EU data in EU; pinned at deploy | deny out-of-region resource types; region-scoped CMKs |
A DR failover that moves EU PHI to a US region |
| P6 | Least privilege, just-in-time | No standing production access; elevate through approval + audit | PIM eligible roles; IAM Identity Center session policies | 200 people with permanent Owner/AdministratorAccess |
| P7 | Encrypt with our keys | CMK/HSM for PHI at rest; TLS 1.2+ in transit; key custody ours | Key Vault Managed HSM; AWS KMS CMK + key policies | Provider-managed keys with no revocation story |
| P8 | Everything auditable + immutable | PHI access and control-plane actions logged to WORM storage | Immutable Blob / S3 Object Lock; Sentinel + CloudTrail org trail | Tamperable logs; unprovable “who read this chart” |
| P9 | Resilience by tier, not by wish | Each app gets a tier with explicit RTO/RPO and a DR pattern | Tag-driven backup/replication policy; game-day tests | One-size DR that over-spends on Tier-3 and under-protects Tier-1 |
| P10 | Interoperate through one broker | All HL7/FHIR/DICOM/X12 crosses via the engine + event mesh | Integration LZ as sole message path; firewall east-west rules | A point-to-point interface mesh nobody can trace or replay |
| P11 | Automate the landing zone | Subs/accounts vended by pipeline with baseline baked in | Account/subscription factory (IaC); no click-ops in prod | Snowflake environments; inconsistent security baselines |
| P12 | Break-the-glass, but witnessed | Emergency PHI/admin access exists but is loud and logged | Dedicated break-glass accounts; alert + review workflow | Clinicians blocked in an emergency, or silent super-users |
The operating model that runs these principles is a thin Cloud Platform / CoE owning the platform planes, a Security & GRC function owning policy and evidence, and workload teams who consume vended landing zones through a self-service factory. The CoE does not deploy workloads; it vends a subscription/account with the network, identity, logging and policy baseline already attached, then gets out of the way. Security & GRC author the guardrails as code and own the audit evidence, but do not gate every deployment manually — the policies do that continuously. This is the split that lets a 55,000-person organisation move without a central bottleneck, and it is why P4 and P11 exist. Responsibilities in a RACI-style cut:
| Capability | Cloud Platform / CoE | Security & GRC | Workload team | Primary tooling |
|---|---|---|---|---|
| Management-group / OU tree | Own | Consulted | Inherits | Bicep / Terraform, Control Tower |
| Guardrail policies (Policy/SCP/RCP) | Implements | Own | Complies | Policy-as-code pipeline |
| Landing-zone vending | Own (factory) | Reviews baseline | Requests + consumes | Subscription/account factory |
| Network hub + firewall | Own | Reviews rules | Requests spoke + rules | Virtual WAN, Transit Gateway |
| Identity / PIM / Conditional Access | Operates | Own policy | Uses eligible roles | Entra ID, IAM Identity Center |
| Workload build + run | Enables | Audits | Own | App CI/CD, IaC |
| PHI data protection (PE/CMK/audit) | Provides platform | Own standard | Applies in workload | Key Vault HSM, KMS |
| DR tier + game-days | Provides patterns | Validates evidence | Own their RTO/RPO | Backup, replication, ASR |
| Compliance evidence + audit | Supports | Own | Supplies control proof | Sentinel, Security Hub, Purview |
Healthcare domains and segmentation
Segmentation is the single most important healthcare-specific decision in the whole design, because the blast radius of a breach in an under-segmented hospital network is the entire patient population. Meridian splits the estate into five clinical/functional domains, each realised as its own landing zone with a dedicated /22 from the Azure 10.20.0.0/12 super-net (AWS mirrors from 10.40.0.0/12), default-deny east-west, and its own data-sensitivity posture. The only traffic that crosses a domain boundary is a governed interoperability contract — an HL7 message, a FHIR call, a DICOM transfer, an X12 transaction — and every one of them is brokered by the integration engine and inspected at the hub firewall. There is no flat network in which a compromised radiology workstation can reach the EHR database.
The domain matrix below is the authoritative placement and isolation reference. Cloud placement follows workload affinity (P-cloud posture): the Epic-class EHR, telemedicine and the integration engine lean Azure (closest to Entra, M365 E5 and the on-prem EHR); the 2.3 PB imaging archive and the research/analytics estate lean AWS (object economics and Lake Formation governance), with cross-cloud replication for DR. Every PHI-bearing row carries private endpoints, CMK and immutable audit as a non-negotiable baseline.
| Domain | Key applications | Data classes | Landing zone (Azure sub / AWS OU) | CIDR /22 |
Cloud placement | Tier | Isolation boundary |
|---|---|---|---|---|---|---|---|
| Clinical | Epic-class EHR, HIS, ADT, CPOE, pharmacy/eMAR, LIS, revenue cycle | PHI (highest), ePHI | mh-lz-clinical / Workloads/Clinical |
10.20.16.0/22 |
Azure primary (EUS2+CUS a/a), AWS warm DR | Tier-1 | PE-only; CMK; deny public; break-glass audited |
| Imaging | PACS, RIS, VNA, DICOM routers, modality worklist, zero-footprint viewers | PHI, DICOM (2.3 PB) | mh-lz-imaging / Workloads/Imaging |
10.20.20.0/22 |
AWS primary (S3 tiers), Azure Blob cache/DR | Tier-1 core / Tier-2 archive | Own archive; lifecycle + Object Lock; no EHR write path |
| Telemedicine | Virtual visits, scheduling, secure messaging, video, intake/consent | PHI (in-session), no PHI at rest on edge | mh-lz-telemed / Workloads/Clinical |
10.20.24.0/22 |
Azure primary | Tier-1 | Edge WAF; EHR write-back via queue only |
| Research | Data lake/warehouse, de-id/pseudonymisation, ML, clinical trials, genomics | De-identified, limited datasets, 42 CFR Part 2 | mh-lz-research / Workloads/Research |
10.20.28.0/22 |
AWS primary (Lake Formation), Azure secondary | Tier-2 / Tier-3 | De-id gate inbound; IRB approval; export audit |
| Integration | Rhapsody/Mirth-class engine, FHIR façade, API gateway, event mesh, X12 | PHI in transit | mh-lz-integration / Workloads/Clinical |
10.20.32.0/22 |
Azure primary + AWS presence | Tier-1 | Sole cross-domain path; brokered + inspected |
| Corporate | SAP S/4HANA, HR, finance, contact-centre, collaboration | Business, some PII | mh-lz-corp / Workloads/Corp |
10.20.36.0/22 |
Azure primary (M365 E5) | Tier-2 | Segregated from clinical PHI; separate IdP groups |
| Devices / IoT | Biomed devices, RPM, edge gateways, telemetry, biomed asset tracking | Device telemetry, some PHI | Overlay in clinical/imaging LZ; NAC micro-segment | 10.20.16.0/22 (micro-seg) |
Azure IoT Hub + edge | Tier-1/2 | NAC + microsegmentation; unpatchable devices quarantined |
Cross-domain traffic is a small, explicit, auditable set of contracts — not an open mesh. Each is a real healthcare integration pattern with a defined transport and a single broker, which is exactly what makes end-to-end message traceability and resilient replay possible:
| From → To | Message / standard | Transport | Broker | Control |
|---|---|---|---|---|
| Clinical → Imaging | HL7 v2 ORM (order) → modality worklist | MLLP over private link | Integration engine | Firewall east-west allow; message audit |
| Imaging → Clinical | HL7 v2 ORU (result) + report | MLLP / FHIR DiagnosticReport |
Integration engine | Signed report; reconciliation |
| Any → Any | IHE XDS.b / XCA / PIX-PDQ document + patient identity | SOAP/REST over PrivateLink | XDS registry/repository | Consent check; affinity domain |
| Telemedicine → Clinical | Encounter, notes, vitals EHR write-back | FHIR R4 over queue + replay | Event mesh | Idempotent write; no PHI on edge |
| Clinical/Imaging → Research | De-identified extract | Batch + FHIR Bulk ($export) |
De-id gateway | Safe Harbor/expert; IRB; export log |
| Corporate ↔ Payers | X12 270/271/276/277/834/835/837 | AS2 / API over PrivateLink | Integration engine | Trading-partner cert; X12 validation |
| Devices → Clinical | Telemetry, alerts, RPM readings | MQTT/AMQP to IoT Hub | Edge gateway + IoT Hub | Device identity; microsegment; alert routing |
Requirements and non-functional targets
Non-functional targets are where “compliant” becomes a number an engineer can build to and an auditor can test. Meridian’s resilience model is four tiers, each with an explicit RTO, RPO, availability SLO, multi-region pattern and backup posture — assigned by tag so the platform (not a human) enforces the right protection. The clinical core (EHR, ADT, results, imaging core, medication, emergency access, patient portal, telemedicine core) is Tier-1 at RTO ≤30 min / RPO ≤5 min, delivered by in-country two-region active/active per the Azure active/active and AWS multi-region active/active patterns. Tier-0 (identity, DNS, network control, privileged access, core security) is the foundation everything else depends on and carries the tightest target of all.
| Tier | Example workloads | RTO | RPO | Availability SLO | Multi-region pattern | Backup / immutability |
|---|---|---|---|---|---|---|
| Tier-0 | Entra/AD DS, DNS, network control plane, PIM, Key Vault/KMS, Sentinel | ≤15 min | ≈0 | 99.99% | Global/active-active; region-independent | Geo-redundant; HSM key backup; immutable audit |
| Tier-1 | EHR, ADT, results, imaging core, eMAR/medication, emergency, patient portal, telemedicine core | ≤30 min | ≤5 min | 99.95% | In-country active/active (EUS2+CUS · us-east-1+us-west-2) | Continuous replication; PITR; immutable + geo copy |
| Tier-2 | Business apps, analytics, revenue cycle, contact-centre, most research | ≤4 h | ≤1 h | 99.9% | Active/passive warm standby; cross-region backup | Nightly + hourly log; cross-region restore tested |
| Tier-3 | Dev, sandbox, non-critical reporting, batch research | ≤24 h | ≤24 h | 99.5% | Backup/restore; single region acceptable | Daily backup; standard redundancy |
Resilience is only one axis. The security, privacy and interoperability NFRs below are the measurable targets that make the compliance posture real — each is a number or a binary state a control can prove, not an aspiration. They apply across both clouds; the mechanism differs, the target does not:
| NFR domain | Target / measurable state | Azure control | AWS control |
|---|---|---|---|
| PHI network exposure | Zero public endpoints on PHI services | Private Endpoint + Policy deny public | PrivateLink + SCP + BlockPublicAccess |
| Encryption at rest | 100% CMK/HSM; keys in our custody | Key Vault Managed HSM (FIPS 140-2 L3) | KMS CMK + key policy + CloudHSM option |
| Encryption in transit | TLS 1.2+ everywhere; mTLS for device/interop | App Gateway/Front Door policy; APIM | ALB/CloudFront TLS policy; API Gateway |
| PHI access audit | 100% reads/writes logged, WORM ≥ 7 yr | Immutable Blob + Sentinel | S3 Object Lock (compliance) + CloudTrail |
| Privileged access | 0 standing prod admins; JIT only | PIM eligible + approval | IAM Identity Center + session policy |
| Break-the-glass | ≤ 2 min to grant; 100% alerted + reviewed | Break-glass accounts + alert rule | Dedicated role + GuardDuty/EventBridge alert |
| Data residency | 100% of EU PHI in EU regions | Policy allowedLocations (EU) |
SCP aws:RequestedRegion deny |
| Interop message durability | 0 lost clinical messages; replayable ≥ 72 h | Service Bus / Event Hubs + dead-letter | SQS/Kinesis + DLQ + archive/replay |
| Device segmentation | 100% unpatchable devices micro-segmented | NSG + IoT Hub + Defender for IoT | Security Group + NAC + IoT Device Defender |
| Patient portal edge | WAF + DDoS on 100% of public entry | Front Door + WAF + DDoS Protection | CloudFront + WAF + Shield Advanced |
Capacity and performance carry healthcare-specific numbers too: the integration engine must sustain peak HL7 v2 throughput across 14 hospitals without back-pressure loss (sized for ADT/ORM/ORU bursts at shift change), the imaging plane must serve diagnostic-quality studies from a 2.3 PB archive with hot-tier latency for current studies and lifecycle demotion to cold/archive for priors, and FHIR APIs published to third-party and patient apps must meet a documented p95 latency and rate-limit budget through the API gateway. These are specified per workload in the detailed design; the platform’s job is to make the ceilings (Event Hubs throughput units, storage tiers, APIM units) elastic and observable rather than fixed.
Requirements traceability
Traceability is the artefact an auditor asks for first: for every requirement, the control that satisfies it and the concrete cloud service that implements the control, on both clouds. This matrix is the spine that ties the compliance obligations in the executive summary to the segmentation and NFRs above — and every later section of this document elaborates a row of it. Requirement IDs are stable references used throughout the design.
| Req ID | Requirement | Compliance driver | Control | Azure service | AWS service | Evidence / verify |
|---|---|---|---|---|---|---|
| R-01 | PHI never traverses the public internet | HIPAA §164.312(e); HITRUST | Private connectivity + PE-only | ExpressRoute + Private Endpoint | Direct Connect + PrivateLink | Route tables; no public IP on PHI svc |
| R-02 | Encrypt PHI at rest with our keys | HIPAA §164.312(a); NIST SC-28 | CMK/HSM | Key Vault Managed HSM | KMS CMK / CloudHSM | Key inventory; enableKeyRotation |
| R-03 | Encrypt PHI in transit | HIPAA §164.312(e); NIST SC-8 | TLS 1.2+ / mTLS | Front Door/App GW/APIM | CloudFront/ALB/API GW | TLS policy; scanner report |
| R-04 | Log all PHI access, tamper-proof | HIPAA §164.312(b); 42 CFR Part 2 | Immutable audit | Immutable Blob + Sentinel | S3 Object Lock + CloudTrail | WORM lock; log completeness |
| R-05 | Least-privilege, just-in-time admin | HITRUST; NIST AC-2/AC-6 | PIM / JIT | Entra PIM + Conditional Access | IAM Identity Center + SCP | 0 standing admins; PIM logs |
| R-06 | Segment domains; deny lateral movement | NIST SC-7; HITRUST | Per-domain LZ + default-deny | NSG + Azure Firewall | Security Group + GWLB | Flow logs; deny-all baseline |
| R-07 | EU PHI stays in EU | GDPR Art. 44–49 | Residency guardrail | Policy allowedLocations |
SCP aws:RequestedRegion |
Policy compliance; region audit |
| R-08 | Tier-1 RTO ≤30 m / RPO ≤5 m | Business continuity; HITRUST | Active/active + replication | ASR/geo + Cosmos/SQL geo | Aurora Global + DynamoDB GT | Game-day RTO/RPO evidence |
| R-09 | Guardrails enforced, not optional | SOC 2 CC; NIST CM | Policy-as-code at root | Azure Policy at mh MG |
SCP/RCP + Control Tower | Deny-event logs; drift report |
| R-10 | Clinical messages durable + replayable | Patient safety; HL7 | Broker + DLQ + replay | Service Bus/Event Hubs | SQS/Kinesis + DLQ | 0 lost msgs; replay test |
| R-11 | De-identify before research use | HIPAA §164.514; 42 CFR Part 2 | De-id gate + approval | Data Factory + Purview | Glue + Lake Formation | De-id job logs; IRB record |
| R-12 | Connected medical devices controlled | FDA premarket/postmarket; NIST | Device identity + microseg | IoT Hub + Defender for IoT | IoT Core + Device Defender | Device inventory; NAC policy |
| R-13 | Break-the-glass emergency access | HIPAA emergency; HITRUST | Witnessed elevation | Break-glass + alert rule | Dedicated role + EventBridge | Alert + review record |
| R-14 | Single identity lifecycle (JML) | SOC 2; HITRUST | Entra hub + federation | Entra Connect + Governance | Federated to Entra IdP | Joiner/leaver logs; access reviews |
| R-15 | Central detection + response | SOC 2; NIST IR | SIEM/SOAR | Microsoft Sentinel + Defender | Security Hub + GuardDuty | Incident MTTD/MTTR; playbooks |
| R-16 | Immutable, restorable backups | HIPAA contingency; HITRUST | Tiered backup + immutability | Azure Backup + immutable vault | AWS Backup + Vault Lock | Restore test; Vault Lock state |
| R-17 | Data residency provable per patient | GDPR; HIPAA | Structural placement + tagging | Subscription/region + tags | Account/region + tags | Residency report per dataset |
| R-18 | No PHI in dev/test | HIPAA minimum-necessary | De-id/synthetic lower envs | Purview + masked datasets | Lake Formation + masking | Scan of non-prod; policy check |
Azure landing zone
Azure carries Meridian Health’s largest regulated footprint — the Epic-class EHR read replicas, the imaging estate, the patient portal and telemedicine core — so its foundation is not a generic enterprise-scale landing zone but the Azure Landing Zone (ALZ) accelerator with a HIPAA/HITRUST healthcare overlay, deployed through Azure Verified Modules (AVM) so the hierarchy itself is reproducible Bicep rather than a console artefact nobody can rebuild. A Tenant Root Group anchors a management-group tree that pushes Azure Policy and RBAC down by inheritance — the single mechanism that lets a control authored once apply to every subscription beneath it. In a 14-hospital integrated delivery network spanning two US regions and one EU region, that inheritance is exactly what stops an encryption or residency rule from silently lapsing in some clinic subscription nobody is watching at 02:00. Directly beneath the tenant root sits the intermediate root management group mh, and under it four deliberately different policy postures: a mh-platform group for shared services, a mh-landingzones group for workloads, a mh-sandbox group for guarded experimentation with PHI structurally forbidden, and a mh-decommissioned group for subscriptions being offboarded. The full tree is code:
Tenant Root Group
└── mh # intermediate root — regs + residency assigned here
├── mh-platform
│ ├── mh-plat-identity # Entra Connect cloud sync, PHS, private DNS, DC extension
│ ├── mh-plat-connectivity # Virtual WAN hubs, Azure Firewall Premium, dual ExpressRoute
│ ├── mh-plat-management # Log Analytics, Azure Monitor, Backup / Recovery Services
│ └── mh-plat-security # Sentinel, Key Vault Managed HSM, break-glass, Defender
├── mh-landingzones
│ ├── mh-lz-clinical → -prod / -nonprod (Tier-1 EHR, ADT, CPOE, pharmacy)
│ ├── mh-lz-imaging → -prod / -nonprod (PACS, VNA, DICOM routers, 2.3 PB)
│ ├── mh-lz-telemed → -prod / -nonprod (virtual visits, scheduling, video)
│ ├── mh-lz-research → -prod / -nonprod (clinical trials, genomics, de-ID)
│ ├── mh-lz-integration → -prod / -nonprod (HL7 v2 / FHIR R4 / X12 event mesh)
│ └── mh-lz-corp → -prod / -nonprod (SAP S/4HANA, HR, M365 adjacency)
├── mh-sandbox
│ └── mh-lz-sandbox # detached policy, egress-locked, PHI denied by policy
└── mh-decommissioned # deny-all posture, pending deletion
The mh-platform group holds four dedicated platform subscriptions, and the separation earns its keep by keeping shared services out of any single workload’s blast radius and letting each platform team run its own change cadence. Because clinical Tier-1 services carry an RTO ≤ 30 minutes and RPO ≤ 5 minutes, the platform subscriptions themselves are treated as Tier-0 (RTO ≤ 15 minutes, RPO ≈ 0) — if identity, DNS or the network control plane is down, nothing else matters.
| Platform subscription | Region footprint | What it holds | Why it is isolated |
|---|---|---|---|
mh-plat-identity |
East US 2 + West Europe | Entra Connect cloud sync, Password Hash Sync, Seamless SSO, private DNS resolver, extended AD DS domain controllers fronting corp.meridianhealth.org |
Identity is Tier-0; a workload compromise must never reach the directory plane |
mh-plat-connectivity |
East US 2, Central US, West Europe | Virtual WAN hubs (/20 per region), Azure Firewall Premium, dual ExpressRoute gateways, DDoS Network Protection, DNS Private Resolver, Bastion | Single central inspection and egress point; workloads never own their own internet path |
mh-plat-management |
East US 2 (US), West Europe (EU) | Log Analytics workspaces (US + EU, residency-split), Azure Monitor, Automation, Update Manager, Recovery Services vaults | Observability and backup must survive a workload-subscription failure or ransomware event |
mh-plat-security |
East US 2 (US), West Europe (EU) | Microsoft Sentinel, Key Vault Managed HSM (FIPS 140-3 Level 3), the two break-glass identities, Defender for Cloud, PHI-access audit archive | Security tooling and key custody must survive a compromise of everything else |
The mh-landingzones group then splits by clinical domain rather than by org chart, so a customer-facing telemedicine workload and an internal research trials workload never share a guardrail set sized for the wrong risk. Each landing-zone family fans into -prod and -nonprod subscriptions, addressed out of the Azure super-net 10.20.0.0/12 with /22 spoke allocations and /26 private-endpoint subnets, and each carries a data-classification and criticality-tier tag from birth.
| Landing-zone subscription | Parent MG | Tier | PHI? | Home regions | Env pattern | Primary systems |
|---|---|---|---|---|---|---|
mh-lz-clinical-* |
mh-lz-clinical |
Tier-1 | Yes (PHI) | East US 2 + Central US (a/a) | prod / nonprod | Epic-class EHR read tier, ADT, CPOE, eMAR, results |
mh-lz-imaging-* |
mh-lz-imaging |
Tier-1 | Yes (PHI) | East US 2 + Central US | prod / nonprod | PACS, VNA, DICOM routers, zero-footprint viewers, 2.3 PB blob |
mh-lz-telemed-* |
mh-lz-telemed |
Tier-1 | Yes (PHI) | East US 2 + Central US | prod / nonprod | Virtual visits, scheduling, encrypted video, EHR write-back |
mh-lz-research-* |
mh-lz-research |
Tier-2 | De-identified | West Europe + East US 2 | prod / nonprod | Clinical-trials isolation, genomics, de-ID / pseudonymisation |
mh-lz-integration-* |
mh-lz-integration |
Tier-1 | Yes (PHI in transit) | East US 2 + Central US + West Europe | prod / nonprod | HL7 v2 interface engine, FHIR R4 APIs, X12, event mesh |
mh-lz-corp-* |
mh-lz-corp |
Tier-2 | Limited (HR PII) | East US 2 | prod / nonprod | SAP S/4HANA, HR, revenue cycle, corporate analytics |
What makes this a healthcare landing zone rather than a generic one is the control plane wrapped around the hierarchy, assigned at the mh and mh-landingzones scopes so every subscription beneath inherits it. The built-in HIPAA HITRUST 9.2 and NIST SP 800-53 Rev. 5 regulatory-compliance initiatives run in audit posture for continuous evidence, while a custom mh-phi-baseline initiative enforces the three non-negotiables — private endpoints, customer-managed-key encryption, and US/EU-only placement — with Deny and DeployIfNotExists effects. The assignment is Bicep at management-group scope, with a system-assigned identity so DINE remediations can run:
targetScope = 'managementGroup'
@description('Approved Azure regions for Meridian Health PHI workloads.')
param allowedLocations array = [ 'eastus2', 'centralus', 'westeurope' ]
// Custom PHI baseline: private endpoints + CMK + region pinning (Deny / DINE)
resource phiBaseline 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
name: 'mh-phi-baseline'
location: 'eastus2'
identity: { type: 'SystemAssigned' } // required for DINE remediation tasks
properties: {
displayName: 'Meridian PHI baseline — private endpoints, CMK, US/EU only'
policyDefinitionId: tenantResourceId(
'Microsoft.Authorization/policySetDefinitions', 'mh-phi-baseline')
enforcementMode: 'Default' // Deny/DINE actively enforced (not DoNotEnforce)
parameters: { allowedLocations: { value: allowedLocations } }
}
}
// Built-in HIPAA HITRUST 9.2 — audit posture, continuous compliance evidence
resource hipaaHitrust 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
name: 'mh-hipaa-hitrust'
properties: {
displayName: 'HIPAA HITRUST 9.2'
policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/a169a624-5599-4385-a696-c8d643089fab'
enforcementMode: 'Default'
}
}
Application subscriptions are never hand-built. A subscription vending factory — the AVM avm/ptn/lz/sub-vending pattern — places each new subscription under the correct management group, inherits policy, assigns RBAC, applies the cost, data-classification and tier tags, peers it to the regional Virtual WAN hub, and enrols it in logging and Defender before a single workload resource exists. A clinical production environment arrives already governed, well inside the vend in under one business day target the platform team is held to:
targetScope = 'tenant'
module clinicalProd 'br/public:avm/ptn/lz/sub-vending:0.3.0' = {
name: 'vend-mh-lz-clinical-prod'
params: {
subscriptionAliasName: 'mh-lz-clinical-prod'
subscriptionDisplayName: 'mh-lz-clinical-prod'
subscriptionBillingScope: billingScope
subscriptionManagementGroupId: 'mh-lz-clinical' // inherits mh-phi-baseline
subscriptionTags: { dataClass: 'PHI', tier: 'Tier-1', residency: 'US', costCenter: 'CLIN-01' }
virtualNetworkEnabled: true
virtualNetworkAddressSpace: [ '10.20.16.0/22' ] // /22 spoke from 10.20.0.0/12
virtualNetworkPeeringEnabled: true
hubNetworkResourceId: hubVwanEastUs2Id // peer to regional hub
}
}
Several vending inputs are load-bearing rather than cosmetic — each one either drives a policy decision or wires the subscription into the platform, so the environment is compliant the moment it exists.
| Vending input | Example value | What it drives |
|---|---|---|
subscriptionManagementGroupId |
mh-lz-clinical |
Placement under the right MG → inherits mh-phi-baseline Deny/DINE |
subscriptionTags.dataClass |
PHI |
Policy predicate that forces private endpoints + CMK on PHI resources |
subscriptionTags.residency |
US | EU |
Selects paired region set and residency-split Log Analytics workspace |
subscriptionTags.tier |
Tier-1 |
Sets backup RPO/RTO policy and Defender plan tier |
virtualNetworkAddressSpace |
10.20.16.0/22 |
/22 spoke from the 10.20.0.0/12 super-net; non-overlapping by IPAM |
hubNetworkResourceId |
regional Virtual WAN hub | Auto-peers the spoke; forces egress through Azure Firewall Premium |
Two of these matter most: dataClass=PHI is what the mh-phi-baseline policies key on to decide whether to force private endpoints and CMK, and residency=US|EU selects which paired region set and which residency-split Log Analytics workspace the subscription binds to. This mirrors the enterprise-scale landing zone management-group design but tightens it for regulated PHI. The result is the diagram below.
The mh tree vends and pushes policy left-to-right into platform then landing-zone subscriptions; every arrow is one-way inheritance a workload can build within but never weaken.
AWS landing zone
AWS mirrors the same intent through AWS Organizations with a multi-account model delivered by Control Tower and Account Factory for Terraform (AFT), so the second cloud is governed by the same philosophy — accounts as the unit of isolation, guardrails inherited from above — expressed in that cloud’s native primitives. A management account sits at the apex purely as the organisation root and billing anchor, deliberately empty of workloads. Beneath it the organisation is partitioned into purpose-built organisational units whose boundaries follow blast-radius and audit lines, not the org chart. The Security OU isolates the accounts that must survive a compromise of everything else; the Infrastructure OU carries the shared network and services; and workloads split by clinical domain into Prod and NonProd child OUs, with a fully detached Sandbox OU where PHI is denied outright. Accounts follow the mh-<purpose>-<env> convention and are addressed from the AWS super-net 10.40.0.0/12:
Root (mh Organization)
├── Security OU
│ ├── mh-logarchive-prod # org CloudTrail sink → S3 Object Lock (compliance mode)
│ ├── mh-audit-prod # AWS Config aggregator, Security Hub delegated admin
│ └── mh-sectooling-prod # GuardDuty, Macie, Detective, Inspector delegated admin
├── Infrastructure OU
│ ├── mh-network-prod # Transit Gateway, Network Firewall, ingress/egress VPCs
│ └── mh-sharedsvcs-prod # IAM Identity Center, AD Connector, private DNS, ECR
└── Workloads OU
├── Clinical → mh-clinical-prod / mh-clinical-nonprod (Tier-1, us-east-1 + us-west-2)
├── Imaging → mh-imaging-prod / mh-imaging-nonprod (Tier-1, PACS/VNA on S3)
├── Research → mh-research-prod / mh-research-nonprod (de-ID, genomics on HealthOmics)
├── Corp → mh-corp-prod / mh-corp-nonprod (Tier-2, revenue cycle, analytics)
└── Sandbox OU (detached) → mh-sandbox-* (SCP: deny PHI services + regions)
The Security OU is the estate’s spine. mh-logarchive-prod holds the tamper-evident record: the organisation-wide CloudTrail trail lands in S3 with Object Lock in compliance mode, write-once and delete-proof even for the account owner, satisfying the HIPAA six-year retention expectation. mh-audit-prod aggregates AWS Config and runs Security Hub as the delegated administrator, and mh-sectooling-prod runs GuardDuty, Macie (which discovers and classifies PHI sitting in S3), Detective and Inspector across every account. The Infrastructure OU’s mh-network-prod owns the regional Transit Gateway and AWS Network Firewall so that clinical, imaging and research VPCs are segmented and every east-west and egress flow is inspected, never peered directly.
| Account | OU | Purpose | PHI? | Home region(s) | CIDR (primary) |
|---|---|---|---|---|---|
mh-management |
Root | Billing, org root, Control Tower home — no workloads | No | us-east-1 | n/a |
mh-logarchive-prod |
Security | Immutable CloudTrail / Config log sink (Object Lock) | Metadata | us-east-1 | 10.40.0.0/24 |
mh-audit-prod |
Security | Config aggregator, Security Hub, Audit Manager | No | us-east-1 | 10.40.1.0/24 |
mh-sectooling-prod |
Security | GuardDuty, Macie, Detective, Inspector delegated admin | Findings | us-east-1 | 10.40.2.0/24 |
mh-network-prod |
Infrastructure | Transit Gateway, Network Firewall, ingress/egress VPCs | In transit | us-east-1, us-west-2, eu-west-1 | 10.40.16.0/20 |
mh-sharedsvcs-prod |
Infrastructure | IAM Identity Center, AD Connector, private DNS, ECR | No | us-east-1 | 10.40.32.0/20 |
mh-clinical-prod |
Workloads/Clinical/Prod | FHIR APIs, patient services, EHR integration tier | Yes | us-east-1 + us-west-2 (a/a) | 10.41.0.0/16 |
mh-imaging-prod |
Workloads/Imaging/Prod | DICOM ingest, VNA archive on S3, HealthImaging | Yes | us-east-1 + us-west-2 | 10.42.0.0/16 |
mh-research-prod |
Workloads/Research/Prod | Trials data lake, HealthOmics genomics, de-ID pipelines | De-identified | eu-west-1 + us-east-1 | 10.43.0.0/16 |
mh-corp-prod |
Workloads/Corp/Prod | Revenue cycle, claims (X12 835/837), analytics | Limited | us-east-1 | 10.44.0.0/16 |
Guardrails on the AWS side are enforced as Service Control Policies that inherit down the OU tree, expressing exactly the controls the Azure policy hierarchy enforces on the other cloud: approved regions only, no public exposure of PHI stores, mandatory encryption with customer-managed KMS keys, and an unbreakable path from every account to the immutable Log Archive. The whole OU tree, its SCPs and the accounts are Terraform — an application account is never a manual ticket, it is a reviewed pull request through AFT that yields an account already wired to centralised logging, Security Tooling enrolment, a Transit Gateway attachment, IAM Identity Center federation brokered by Entra, approved-region settings and baseline KMS keys. The region-deny SCP is the sharpest example, and it is deliberately careful to exempt the global services that have no regional endpoint:
data "aws_iam_policy_document" "region_deny" {
statement {
sid = "DenyOutsideApprovedRegions"
effect = "Deny"
# global / control-plane services must stay reachable from any region
not_actions = [
"iam:*", "organizations:*", "sts:*", "route53:*", "cloudfront:*",
"waf:*", "wafv2:*", "support:*", "health:*", "kms:CreateKey",
]
resources = ["*"]
condition {
test = "StringNotEquals"
variable = "aws:RequestedRegion"
values = ["us-east-1", "us-west-2", "eu-west-1"] # US-in-US / EU-in-EU
}
}
}
resource "aws_organizations_policy" "region_deny" {
name = "mh-scp-region-deny"
type = "SERVICE_CONTROL_POLICY"
content = data.aws_iam_policy_document.region_deny.json
}
resource "aws_organizations_policy_attachment" "region_deny_root" {
policy_id = aws_organizations_policy.region_deny.id
target_id = aws_organizations_organization.mh.roots[0].id # inherits to every OU
}
The encryption SCP rides alongside it, denying any s3:PutObject that is not aws:kms-encrypted and blocking the disabling of account-level S3 Block Public Access, so a workload account physically cannot create a plaintext or internet-exposed PHI store. Below the SCP floor, Control Tower preventive and detective controls and Config conformance packs add the framework-specific rules, and the whole posture is evaluated against named healthcare baselines rather than a generic one — this is the same account-vending discipline described in AWS Control Tower guardrails for a multi-account foundation.
| SCP / control | Attached at | Effect | Healthcare purpose |
|---|---|---|---|
mh-scp-region-deny |
Root | Deny actions outside us-east-1/us-west-2/eu-west-1 | US-in-US and EU-in-EU data residency, structurally |
mh-scp-require-kms |
Root | Deny unencrypted S3/EBS/RDS; force SSE-KMS CMK | PHI-at-rest encryption (HIPAA §164.312(a)(2)(iv)) |
mh-scp-deny-public |
Workloads | Deny disabling S3 BPA; deny public RDS/ELB | No PHI store ever exposed to the internet |
mh-scp-protect-guardrails |
Root | Deny leaving org, disabling GuardDuty/Config/CloudTrail | Detective controls cannot be silenced by a workload team |
mh-scp-sandbox-lock |
Sandbox | Deny HealthLake, HealthImaging, HealthOmics + all regions but us-east-1 | Sandbox may never touch PHI services |
| Control Tower — CloudTrail enabled | Root (mandatory) | Detective | Every account streams to the immutable Log Archive |
| Config conformance — HIPAA Security | Workloads | Detective | Continuous evidence against the HIPAA Security Rule |
Control Tower governs the mh Organizations root; SCPs inherit down the OU tree into the Security, Infrastructure and per-domain workload accounts, and AFT vends every account pre-governed.
Governance hierarchy and policy inheritance
The two landing zones look different on the surface — Azure management groups on one side, AWS organisational units on the other — but they are deliberately governed by one guardrail set compiled into both, and that single-source-of-truth approach is the load-bearing decision of the entire foundation. Meridian Health cannot afford two divergent control estates audited and reasoned about separately, because a HITRUST r2 assessment and an OCR audit will each ask whether the same control holds everywhere PHI lives, on either provider. So the controls are authored once, as policy-as-code in Terraform and Bicep, and rendered into each cloud’s native enforcement: Azure Policy definitions and RBAC assignments that inherit down the management-group tree, and Service Control Policies that inherit down the AWS OU tree. The module library is the canonical artefact; the cloud-specific assignments are compiled outputs of it. Five shared guardrails travel down both hierarchies as the non-negotiable floor every subscription and account inherits.
| Shared guardrail | Azure mechanism | AWS mechanism | Effect |
|---|---|---|---|
| Approved regions / residency only | Allowed locations policy at mh (US/EU set) |
mh-scp-region-deny at Root |
Deny (preventive) |
| No public exposure of PHI services | Custom Deny on publicNetworkAccess + built-in Private Link audit |
mh-scp-deny-public (S3 BPA, public RDS/ELB) |
Deny (preventive) |
| Private endpoints for PHI PaaS | DeployIfNotExists private endpoint + private DNS |
VPC interface endpoints + PrivateLink baseline (AFT) | DINE / provisioned |
| CMK encryption for PHI | DINE binds Storage/SQL/KV to Managed HSM key | mh-scp-require-kms forces SSE-KMS CMK |
Deny + DINE |
| Immutable audit logging | DINE diagnostic settings → immutable Log Analytics / storage | Org CloudTrail → S3 Object Lock (compliance mode) | DINE / provisioned |
Inheritance is the property that lets this operating model scale without growing a control team in proportion to the estate. Platform engineers change a guardrail in one module, and on the next pipeline run every subscription beneath the Azure tree and every account beneath the AWS tree conforms — no fleet of environments to revisit by hand, and no window in which half the estate runs an old rule. Crucially the relationship is one-directional: workload teams are free to build inside the guardrails, but inheritance means they cannot weaken a control handed down from above. A telemedicine team cannot grant itself an unapproved region, and a research application owner cannot open a genomics bucket to the internet, because the denial is asserted higher in the tree than they have authority to edit. That asymmetry — maximum delivery autonomy inside a control floor a single misconfigured workload can never sink — is precisely what a Tier-1 clinical estate needs.
The enforcement verbs matter as much as the rules, and healthcare uses all of them deliberately. Azure Policy’s effect model (covered in depth in Azure Policy effects: Deny, Audit, Modify, DeployIfNotExists) maps cleanly onto the AWS side, and the choice of verb is a choice about when the control acts.
| Effect | What it does | Azure use | AWS analogue | Healthcare example |
|---|---|---|---|---|
| Deny | Blocks a non-compliant create/update outright | Deny effect |
SCP Deny statement |
Reject a public-facing PHI storage account at create time |
| DeployIfNotExists | Auto-provisions the missing control | DeployIfNotExists |
AFT baseline / Config remediation | Create the private endpoint + DNS record for a new FHIR store |
| Modify | Adds/changes a property to conform | Modify |
Config auto-remediation (SSM) | Stamp dataClass=PHI tag; enable HTTPS-only on storage |
| Audit | Flags drift without blocking | Audit / AuditIfNotExists |
Config rule (non-blocking) | Report any Key Vault without purge protection |
| DenyAction | Blocks a specific operation (e.g. delete) | DenyAction |
SCP deny on s3:DeleteObject |
Prevent deletion of immutable imaging archive objects |
The custom mh-phi-baseline initiative is where these verbs become concrete. Its Deny arm rejects any PHI-tagged storage account that leaves public network access enabled or encrypts with a platform-managed key; its DINE arm provisions the private endpoint the workload team forgot. A compact, real custom definition shows the shape — note the storage aliases and the tags[dataClass] predicate that scopes enforcement to PHI only:
resource denyPublicPhiStorage 'Microsoft.Authorization/policyDefinitions@2024-04-01' = {
name: 'mh-deny-public-phi-storage'
properties: {
policyType: 'Custom'
mode: 'All'
displayName: 'Deny public network access on PHI storage accounts'
metadata: { category: 'Meridian PHI' }
policyRule: {
if: {
allOf: [
{ field: 'type', equals: 'Microsoft.Storage/storageAccounts' }
{ field: 'tags[dataClass]', equals: 'PHI' }
{ anyOf: [
{ field: 'Microsoft.Storage/storageAccounts/publicNetworkAccess', notEquals: 'Disabled' }
{ field: 'Microsoft.Storage/storageAccounts/encryption.keySource', notEquals: 'Microsoft.Keyvault' }
] }
]
}
then: { effect: 'deny' } // no PHI store may be public OR platform-key encrypted
}
}
}
The one place inheritance bends is break-the-glass. A single misapplied Deny can turn an emergency-access account into a locked-out account at exactly the wrong moment, so the two break-glass identities and the emergency clinical-access path are covered by a narrow, audited policy exemption at the mh-plat-security scope, time-boxed and alerting on every use, rather than a blanket carve-out — the pattern detailed in break-glass emergency access monitoring and governance. The initiatives assigned at each scope build the posture up in layers, most general at the root and most specific at the workload.
| Scope | Assigned initiative(s) | Posture |
|---|---|---|
mh (intermediate root) |
Microsoft Cloud Security Benchmark; Allowed locations (US/EU) | Baseline + residency, all subs |
mh-landingzones |
HIPAA HITRUST 9.2; NIST SP 800-53 Rev. 5 (audit) | Continuous framework evidence |
mh-lz-clinical / -imaging / -telemed |
mh-phi-baseline (Deny + DINE) |
Private endpoints, CMK, PHI enforced |
mh-lz-research |
mh-phi-baseline + de-identification / export-audit set |
De-ID gate, dataset-approval, 42 CFR Part 2 |
mh-sandbox |
mh-deny-phi-services |
PHI services denied; egress-locked |
Controls are authored once and compiled into Azure Policy and AWS SCPs; each arrow is one-way inheritance from root to workload, where Deny is preventive, DINE remediates and Audit reports.
Compliance, data residency and control mapping
Meridian Health does not answer to one regulator but to an overlapping mesh of them, and the architecture has to satisfy all of them at once without forking into a dozen incompatible estates. The control plane is therefore designed so that a single set of controls maps to many regulatory drivers simultaneously — one encryption model serving HIPAA, HITRUST, GDPR and NIST 800-66 at once; one segmentation boundary serving both the HIPAA Security Rule and 42 CFR Part 2. The regulatory reality this design is built against is explicit: the HIPAA Security Rule and HITECH breach-notification regime governing all electronic PHI; HITRUST CSF v11 as the certifiable framework the health system attests against; GDPR (with Schrems II and transfer-impact) for EU personal data in the West Europe region; NIST SP 800-53 Rev. 5 and NIST SP 800-66 Rev. 2 (the HIPAA Security Rule implementation guide) as the control catalogues; SOC 2 Type II for service assurance; 42 CFR Part 2 for substance-use-disorder records, which demand consent-gated segmentation beyond ordinary PHI; and FDA pre/postmarket cybersecurity expectations for the connected medical devices feeding the estate. A four-band data classification underpins the mapping, and the band an asset carries determines its encryption, residency, key custody and access controls.
| Data class | Examples | Residency rule | Enforcement |
|---|---|---|---|
| Restricted — PHI | EHR records, ADT, results, DICOM images, medication orders | In-region only; US PHI in US, EU PHI in EU; no cross-border replication | Private Endpoint/PrivateLink-only, CMK in Managed HSM/CloudHSM, region-pinned guardrails, immutable audit |
| Restricted — Part 2 / behavioural | Substance-use-disorder and behavioural-health records | As PHI, plus consent-gated segmentation and separate key scope | Dedicated segment, consent-checked access, break-glass audited, separate CMK |
| Confidential — PII | Staff HR data, patient demographics outside clinical context | Resident in data-subject jurisdiction; transfer-impact assessed | Approved-region guardrails, CMK, Purview classification, access reviews |
| Internal / Public | Reference data, formularies, public patient-education content | No residency constraint; integrity-protected | Standard encryption, WAF at the edge, change control |
The control matrix below is the operational heart of the compliance posture: it maps each control domain to Meridian’s concrete named control, to the Azure service and the AWS service that implement it, and to the policy that enforces it — so no control is orphaned and no framework is unmapped. Every row can be defended to an auditor with a real tool and a real enforcement point rather than a policy aspiration. This is the same discipline applied end-to-end in the HIPAA healthcare data platform on Azure reference.
| Control domain | Meridian control | Azure service | AWS service | Enforcing policy / SCP |
|---|---|---|---|---|
| PHI at rest | CMK encryption, FIPS 140-3 keys, no platform keys | Key Vault Managed HSM + Storage/SQL CMK | KMS / CloudHSM + SSE-KMS | mh-phi-baseline DINE + mh-scp-require-kms |
| PHI in transit | TLS 1.2+, private paths only, no public endpoints | Private Link + Private DNS | PrivateLink + VPC endpoints | Deny publicNetworkAccess + mh-scp-deny-public |
| Identity & access | Entra as hub, phishing-resistant MFA, zero standing privilege | Entra ID + PIM + Conditional Access | IAM Identity Center (Entra federated) | CA policies + permission sets, Audit no-MFA |
| Privileged access | JIT elevation, PAWs, two break-glass, brokered admin | PIM + Bastion | IdC + Session Manager | PIM approval flow; SCP deny root-user access keys |
| Network segmentation | Segmented clinical/imaging/research, default-deny, central inspection | Azure Firewall Premium + NSG | Network Firewall + Security Groups | UDR-to-firewall policy + TGW route tables |
| Audit logging | Immutable, centralised, 6-year PHI-access log | Sentinel + immutable Log Analytics/storage | CloudTrail → S3 Object Lock | DINE diagnostic settings + mh-scp-protect-guardrails |
| Data residency | US-in-US, EU-in-EU, region-pinned placement | Allowed locations policy | mh-scp-region-deny |
Deny outside US/EU region set |
| Threat detection | 24×7 SOC, PHI-exfil detection across both clouds | Defender for Cloud + Sentinel | GuardDuty + Security Hub + Macie | Config/Defender enabled by policy; deny disable |
| PHI discovery / DLP | Locate and classify PHI wherever it lands | Microsoft Purview | Macie | Purview scan rules; Macie job baseline (AFT) |
| De-identification | Pseudonymise before research use, export-audited | De-ID service + Synapse/Databricks | HealthLake / HealthOmics + Glue | Research initiative de-ID gate + Lake Formation |
| Backup & resilience | Tier-aligned RPO/RTO, immutable ransomware-safe backup | Recovery Services (immutable vault) | AWS Backup (Vault Lock) | Backup policy assignment; mh-scp deny vault delete |
| Device / IoT security | Device identity, segmentation of unpatchable devices | Azure IoT Hub + Defender for IoT | IoT Core + Device Defender | NAC/microseg policy; deny un-attested device onboarding |
Two mappings prove the “one control, many regulations” claim explicitly. The first anchors the estate to the HIPAA Security Rule citations and shows each safeguard landing on named services in both clouds; the second is the cross-framework crosswalk showing a single Meridian control answering HIPAA, HITRUST, GDPR and NIST 800-66 in one row.
| HIPAA Security Rule safeguard | Citation | Azure implementation | AWS implementation |
|---|---|---|---|
| Access control | §164.312(a)(1) | Entra RBAC + PIM + Conditional Access | IAM Identity Center + permission sets + SCP |
| Audit controls | §164.312(b) | Sentinel + immutable Log Analytics | CloudTrail Object Lock + Config |
| Integrity | §164.312©(1) | Immutable (WORM) blob, versioning | S3 Object Lock + versioning |
| Transmission security | §164.312(e)(1) | TLS 1.2+, Private Link, no public paths | TLS 1.2+, PrivateLink, VPC endpoints |
| Encryption (addressable) | §164.312(a)(2)(iv) | Managed HSM CMK on Storage/SQL | KMS/CloudHSM SSE-KMS CMK |
| Person/entity authentication | §164.312(d) | Phishing-resistant MFA (FIDO2) | Entra-federated MFA into IdC |
| Facility / device controls | §164.310, §164.312 | Defender for IoT, biomed segmentation | IoT Device Defender, microsegmentation |
| Meridian control | HIPAA | HITRUST CSF v11 | GDPR | NIST 800-66 Rev. 2 |
|---|---|---|---|---|
| CMK encryption of PHI | §164.312(a)(2)(iv) | 06.d / 10.f | Art. 32 | §164.312(a)(2)(iv) guidance |
| Private-endpoint-only access | §164.312(e)(1) | 09.m | Art. 32 | Transmission security |
| Immutable PHI-access audit | §164.312(b) | 09.aa / 12.b | Art. 30, 33 | Audit controls |
| Region residency guardrails | §164.308(a)(1) | 13.k | Art. 44–49 (transfers) | Risk management |
| Consent-gated Part 2 segment | §164.508 | 06.d / 19.b | Art. 9 (special category) | Access control |
| De-identification for research | §164.514(b) | 06.e | Art. 4 / Recital 26 | De-identification |
Data residency closes the loop between classification and enforcement, and the decisive property is that it is enforced structurally, not by policy memo. The approved-region guardrails inherited down both the Azure management-group tree and the AWS OU tree mean a workload simply cannot place Restricted PHI outside its permitted geography: US clinical and imaging data is pinned to East US 2 / Central US and us-east-1 / us-west-2, EU research and personal data to West Europe and eu-west-1, and the residency-split Log Analytics workspaces and separate CMK scopes mean even the telemetry and keys respect the boundary. Where a research dataset must cross from clinical to research use, it passes through a de-identification gate and an export-audit checkpoint before it is allowed to leave its home segment — so the health system can give an honest, evidenced answer to the hardest question a regulator or a patient asks, which is where does my record live and who can read it. The frameworks map to one control, the control maps to a named service on each cloud, and the enforcement is a policy an auditor can inspect running.
A regulatory driver maps to a control domain, then to a named Azure service and its AWS parity, then to the policy or SCP that proves it — one control answering many frameworks at once.
Global hybrid connectivity
Meridian Health’s network is not one network — it is three on-premises data centres, two hyperscaler backbones and 120-plus care sites that must behave like a single routed fabric with residency walls that never leak. Every design choice below exists to satisfy one sentence: a packet carrying PHI must travel a private, encrypted, inspected path, and a US packet must never land in the EU (nor an EU packet in the US) except where the law explicitly allows it. The physical layer that makes this real is dedicated circuits — not internet VPN — from all three data centres into both clouds.
The three data centres (Ashburn VA, Chicago IL, Dublin IE) each home to two carrier peering locations. From the US DCs we run dual ExpressRoute landing in East US 2 and dual Direct Connect landing in us-east-1; from Dublin we run ExpressRoute into West Europe and Direct Connect into eu-west-1. Azure terminates these into a Virtual WAN with a secured hub per region; AWS terminates them into a Transit Gateway per region via a Direct Connect Gateway. The rule that trips most teams is that neither dedicated circuit is transitive between clouds: ExpressRoute will not carry a packet from an Azure spoke to an AWS VPC, and Direct Connect will not do the reverse. East-West between clouds rides an explicit vWAN-hub-to-TGW connection (IPsec/BGP) or hairpins through the on-prem core — and we advertise only the prefixes each side is entitled to see.
The three data centres feed dual dedicated circuits into the Azure Virtual WAN and AWS Transit Gateway backbones, which in turn front the regional workload estates; the diagram traces that left-to-right path and marks where redundancy, encryption and non-transitivity are won or lost.
Here is the circuit inventory the network team provisions and monitors. Two circuits per cloud per country is the floor for the 99.95% ExpressRoute / 99.9% Direct Connect SLA — a single circuit carries no SLA at all.
| Circuit | Terminates in | Peering / model | Provisioned | Redundancy | Encryption |
|---|---|---|---|---|---|
| ExpressRoute A (US) | Azure East US 2 |
Private peering · ExpressRoute Direct 10G | 5 Gbps (burst 10) | 2nd port, distinct MEEP pair | MACsec (AES-256) |
| ExpressRoute B (EU) | Azure West Europe |
Private peering · ExpressRoute Direct 10G | 5 Gbps (burst 10) | 2nd port, distinct MEEP pair | MACsec (AES-256) |
| Direct Connect A (US) | AWS us-east-1 |
Transit VIF → DXGW → TGW | 2× 10 Gbps LAG | 2 devices, 2 locations | MACsec (10G+) |
| Direct Connect B (EU) | AWS eu-west-1 |
Transit VIF → DXGW → TGW | 2× 10 Gbps LAG | 2 devices, 2 locations | MACsec (10G+) |
| SD-WAN overlay | vWAN + TGW (NVA) | BGP over IPsec, per-site | 100 Mbps–1 Gbps/site | Dual-homed large hospitals | IPsec (site tunnels) |
BGP is where residency and blast-radius are actually enforced. On-prem advertises summarized routes only — one prefix per data-centre and per site block — and each cloud advertises its /12 super-net back, filtered so US ranges never propagate over the EU circuit. The ExpressRoute gateway is ErGw3AZ (zone-redundant, FastPath for data-plane bypass of the gateway VM); the Direct Connect side uses per-environment DXGW association route tables and allowed-prefixes so prod and non-prod cannot bleed across the hybrid edge.
| Advertiser | Advertises | To | Filter / guardrail |
|---|---|---|---|
| On-prem (US DCs) | 10.0.0.0/16, 10.1.0.0/16, site summaries |
ExpressRoute A, Direct Connect A | US prefixes only; no EU leak |
| On-prem (Dublin) | 10.2.0.0/16, EU site summaries |
ExpressRoute B, Direct Connect B | EU prefixes only |
| Azure vWAN | 10.20.0.0/12 (per-region /16s) |
On-prem via ER | Region-scoped; AS-path prepend on backup |
| AWS TGW/DXGW | 10.40.0.0/12 (per-region /16s) |
On-prem via DX | Allowed-prefixes list; per-env assoc RT |
| vWAN ↔ TGW | Only shared-service /22s | Cross-cloud East-West | Explicit allow-list, not full tables |
A minimal, real provisioning path for the Azure side looks like this — a Virtual WAN, a secured hub, an ExpressRoute gateway sized for FastPath, and the circuit connection:
az network vwan create -g mh-connectivity-eus2-rg -n mh-eus2-vwan \
--type Standard --location eastus2
az network vhub create -g mh-connectivity-eus2-rg -n mh-eus2-vhub \
--vwan mh-eus2-vwan --address-prefix 10.20.0.0/23 --sku Standard
# Zone-redundant ExpressRoute gateway (ErGw3AZ) with FastPath scale units
az network express-route gateway create -g mh-connectivity-eus2-rg \
-n mh-eus2-ergw --virtual-hub mh-eus2-vhub --min-scale-units 2 --max-scale-units 10
# Bind the dual circuits (second circuit gives the 99.95% SLA)
az network express-route gateway connection create -g mh-connectivity-eus2-rg \
--gateway-name mh-eus2-ergw -n er-a --peering /subscriptions/.../peerings/AzurePrivatePeering \
--routing-weight 32000
For repeatable, reviewed infrastructure this is Terraform, not click-ops. The Terraform module for Azure Virtual WAN and the AWS Transit Gateway module encode the hub SKUs, routing intent and RAM shares so every region is provisioned identically. The topology decision behind all of this — a managed Virtual WAN versus classic hub-spoke — is worked through in hub-spoke vs Virtual WAN enterprise topology; the circuit-level HA design is in ExpressRoute private peering failover design and the peering types in ExpressRoute circuits and peering types explained. The 120-plus care sites reach the backbone over an SD-WAN fabric integrated as described in SD-WAN integration with the cloud backbone — with large hospitals dual-homed to two secured hubs so a regional drain never dark-sites a facility.
Azure regional network
Inside each Azure region the pattern is a Virtual WAN secured hub fronting a set of segmented spokes, one spoke per security segment, with all egress and all cross-segment traffic forced through Azure Firewall Premium. Meridian runs six segments — clinical, imaging, research, business, internet-facing and management — and the firewall is the only lawful path between any two of them. This is the design pattern from the enterprise-scale landing zone, tightened for PHI: no spoke has a default route to the internet, and no spoke peers directly to another.
Every spoke’s 0.0.0.0/0 is a routing-intent rule that points at the hub firewall; the firewall does TLS inspection and IDPS before anything leaves, so PHI exfiltration over an outbound TLS session is visible and droppable. Segments never route to each other except through a firewall allow-list; PHI PaaS is reached only through a per-spoke /26 Private Endpoint subnet whose names are answered by hub-linked Private DNS zones.
The segment map is the contract between the network team and every workload team. Each segment is a spoke VNet carved from the region /16 on a /22 boundary (the full carve-up is in the IPAM plan below), with a fixed egress policy on the firewall.
| Segment (spoke) | CIDR (EUS2) | Workloads | Tier | Firewall egress policy |
|---|---|---|---|---|
| Clinical | 10.20.16.0/22 |
EHR/EMR, ADT, CPOE, results, eMAR | Tier-1 | Deny-all + FQDN allow-list (Epic, payers) |
| Imaging | 10.20.20.0/22 |
PACS, VNA, DICOM routers, viewers | Tier-1 | Deny-all + modality/CDN allow-list |
| Research | 10.20.24.0/22 |
Clinical-trials, de-ID, ML workspaces | Tier-2 | Deny-all + curated dataset egress only |
| Business | 10.20.28.0/22 |
SAP S/4HANA, revenue cycle, analytics | Tier-2 | Deny-all + SaaS FQDN allow-list |
| Internet-facing | 10.20.36.0/22 |
Patient portal, telemedicine front door | Tier-1 | Ingress via Front Door/App GW; egress inspected |
| Management | 10.20.40.0/22 |
Bastion, jump hosts, tooling, agents | Tier-0 | Deny-all + update/OS/agent FQDNs |
The controls that make segmentation real are a routing table (UDR via routing intent), NSGs on every subnet, and the firewall policy. Meridian’s firewall policy is hierarchical — a parent policy at the organisation level for the deny-by-default IDPS and TLS baseline, and a per-region child policy for the FQDN allow-lists.
| Control | Where it lives | What it enforces | How to confirm |
|---|---|---|---|
Routing intent 0/0 → AzureFirewall |
Secured hub | Forced tunnelling of all egress | az network vhub get-effective-routes |
| Inter-segment deny | Firewall policy rule collection | Clinical ↔ research/business blocked | Firewall logs: Deny on the flow |
| NSG per subnet | Spoke subnets | L4 micro-seg inside a spoke | az network watcher effective SG |
| TLS inspection + IDPS | Azure Firewall Premium | PHI exfil / C2 over TLS dropped | AZFWIdpsSignature / AZFWApplicationRule logs |
| PE subnet policy | Clinical/imaging spokes | Private-only PaaS reachability | privateEndpointNetworkPolicies=Enabled |
Creating the secured hub’s firewall policy and the routing intent that forces every spoke through it is a few commands; the child policy inherits the parent’s IDPS baseline:
# Parent (org baseline): IDPS = Deny, TLS inspection with a Key Vault cert
az network firewall policy create -g mh-connectivity-eus2-rg -n mh-fwpol-parent \
--sku Premium --idps-mode Deny \
--cert-name mh-tls-insp --key-vault-secret-id https://mh-kv-fw.vault.azure.net/secrets/fw-ca
# Child policy for EUS2 inherits the parent, adds regional FQDN allow-lists
az network firewall policy create -g mh-connectivity-eus2-rg -n mh-fwpol-eus2 \
--sku Premium --base-policy mh-fwpol-parent
# Routing intent: send BOTH private and internet traffic through the hub firewall
az network vhub routing-intent create -g mh-connectivity-eus2-rg --vhub mh-eus2-vhub \
-n mh-ri --routing-policies \
'[{"name":"internet","destinations":["Internet"],"nextHop":"<fw-id>"},
{"name":"private","destinations":["PrivateTraffic"],"nextHop":"<fw-id>"}]'
The Azure Firewall Terraform module codifies the parent/child policy split and the rule collections. When a spoke’s effective route table shows a next hop of Internet instead of the firewall, egress is bypassing inspection — the single most common finding, diagnosable exactly as shown in troubleshooting VNet connectivity with effective routes.
AWS regional network
The AWS side mirrors the Azure pattern with AWS primitives: a Transit Gateway per region as the router, a centralized inspection VPC running AWS Network Firewall, and workload VPCs per segment with route domains that keep clinical, imaging, research and corp — and prod versus non-prod — from ever reaching each other. Every workload VPC default-routes 0.0.0.0/0 at the TGW, which (with appliance mode on) hairpins the flow through one firewall endpoint before NAT and egress. Gateway and interface endpoints keep S3, DynamoDB and PHI PaaS off the internet path entirely.
TGW segmentation is done with one route table per domain: an attachment associates to exactly one domain’s table and propagates into it, and isolation is simply the absence of cross-propagation. This is what makes “clinical cannot reach research” a routing fact rather than a security-group hope.
| TGW route domain | Associated attachments | Propagates into | Reaches |
|---|---|---|---|
| Clinical | Clinical prod/non-prod VPCs | Clinical table only | Inspection VPC + shared services |
| Imaging | Imaging VPCs | Imaging table only | Inspection VPC + VNA archive EP |
| Research | Research/trials VPCs | Research table only | Inspection VPC (curated egress only) |
| Corp | Corp/business VPCs | Corp table only | Inspection VPC + on-prem via DX |
| Egress/inspection | Inspection VPC, DXGW | Summaries from all domains | Central choke point |
The workload VPCs are carved from the region /16 on the same /22 boundaries as Azure, so the two clouds are trivially comparable and never overlap. Each VPC reserves small per-AZ subnets for the TGW attachment and a /26 for interface endpoints.
| VPC (segment) | CIDR (us-east-1) | Key subnets | Endpoints |
|---|---|---|---|
| Clinical | 10.40.16.0/22 |
app /25 ×3 AZ, data /26 ×3 | Interface EP /26, TGW-attach /28 ×3 |
| Imaging | 10.40.20.0/22 |
app /25, VNA data /24 | S3 gateway EP, TGW-attach /28 ×3 |
| Research | 10.40.24.0/22 |
compute /24, ML /25 | Interface EP /26, no IGW |
| Corp | 10.40.28.0/22 |
app /24, db /25 | Interface EP, DX reachable |
| Inspection | 100.64.0.0/16 |
firewall subnet /28 ×3 AZ | Firewall endpoints, NAT/IGW |
Provisioning the TGW, its domain tables and the Network Firewall policy is straightforward; appliance mode is the non-obvious must-have — without it a long DICOM transfer can hash to a different AZ’s firewall endpoint on the return path and die mid-transfer:
# Regional Transit Gateway, default route-table association/propagation OFF (we do it explicitly)
aws ec2 create-transit-gateway --description "mh-use1-tgw" \
--options AmazonSideAsn=64512,DefaultRouteTableAssociation=disable,\
DefaultRouteTablePropagation=disable,MulticastSupport=disable
# Appliance mode on the inspection-VPC attachment keeps flows AZ-symmetric
aws ec2 modify-transit-gateway-vpc-attachment \
--transit-gateway-attachment-id tgw-attach-0inspection \
--options ApplianceModeSupport=enable
# Network Firewall policy: STRICT order, drop-by-default, managed threat-intel groups
aws network-firewall create-firewall-policy --firewall-policy-name mh-use1-fwpol \
--firewall-policy '{"StatelessDefaultActions":["aws:forward_to_sfe"],
"StatelessFragmentDefaultActions":["aws:forward_to_sfe"],
"StatefulEngineOptions":{"RuleOrder":"STRICT_ORDER"},
"StatefulDefaultActions":["aws:drop_established","aws:alert_established"]}'
The Suricata rule engineering behind the allow-list — SNI/host allow-lists, HOME_NET scoping and STRICT-order semantics — is worked through in AWS Network Firewall Suricata egress inspection, and the reusable stacks are the Network Firewall and Route 53 Resolver modules. Hybrid name resolution — workloads resolving corp.meridianhealth.org and on-prem resolving cloud PaaS — runs on Route 53 Resolver inbound/outbound endpoints with FORWARD rules shared across accounts by RAM.
IP address management plan
Address space is the one design decision you cannot cheaply change after workloads land, so Meridian treats it as a governed asset: nothing is hand-picked, everything is vended from Azure Virtual Network Manager IPAM pools and AWS VPC IPAM. The scheme is three non-overlapping RFC1918 /12 super-nets, each split into per-region /16s, each region reserving its first /20 for the hub/inspection fabric and handing segments contiguous /22 spokes, each spoke dedicating a /26 for Private Endpoints. The full CIDR discipline is the subject of VNet IP address planning and CIDR subnetting; what follows is the concrete plan.
The carve-down is a strict hierarchy — three /12 super-nets to per-region /16s to the reserved hub /20 and per-segment /22 spokes, down to the /26 Private-Endpoint and /28 attachment leaf subnets — with every level allocated from an IPAM pool so overlap is structurally impossible.
The top of the plan is the super-net and per-region allocation. The /12s canonicalize to disjoint blocks (on-prem 10.0–15, Azure 10.16–31, AWS 10.32–47), so no cloud can ever collide with another or with on-prem.
| Scope | Super-net | Primary region /16 |
Secondary /16 |
EU /16 |
|---|---|---|---|---|
| On-prem | 10.0.0.0/12 |
Ashburn 10.0.0.0/16 |
Chicago 10.1.0.0/16 |
Dublin 10.2.0.0/16 |
| Sites / edge | (within on-prem) | 10.8.0.0/13 |
/22 per hospital, /24 per clinic | — |
| Azure | 10.20.0.0/12 |
EUS2 10.20.0.0/16 |
CUS 10.21.0.0/16 |
WEU 10.22.0.0/16 |
| AWS | 10.40.0.0/12 |
use1 10.40.0.0/16 |
usw2 10.41.0.0/16 |
euw1 10.42.0.0/16 |
Within a region /16, the first /20 is reserved — never vended to a workload — for the hub. In the Azure Virtual WAN model the managed hub takes a /23, and the remaining hub /20 space holds the shared platform services (Bastion, DNS Private Resolver, shared platform Private Endpoints).
Hub /20 sub-block (EUS2 10.20.0.0/20) |
CIDR | Purpose |
|---|---|---|
| vWAN managed hub | 10.20.0.0/23 |
ExpressRoute/VPN gateways + Azure Firewall |
| AzureBastionSubnet | 10.20.2.0/26 |
Bastion host for management access |
| DNS Private Resolver — inbound | 10.20.2.64/28 |
On-prem → cloud name resolution |
| DNS Private Resolver — outbound | 10.20.2.80/28 |
Cloud → on-prem corp.meridianhealth.org |
| Shared platform PE subnet | 10.20.3.0/26 |
Platform-wide Private Endpoints |
| Reserved headroom | 10.20.4.0/22 |
Future shared services |
Below the hub, segments take contiguous /22s so on-prem advertises exactly one summarized route per segment. Each spoke splits into app/data/compute /24s and a dedicated /26 Private Endpoint subnet — the clinical spoke shown here is the template for all six.
| Segment | Spoke /22 (EUS2) |
App / data / compute | PE subnet /26 |
|---|---|---|---|
| Clinical | 10.20.16.0/22 |
.16.0/24 · .17.0/24 · .18.0/24 |
10.20.19.0/26 |
| Imaging | 10.20.20.0/22 |
.20.0/24 · .21.0/23 (VNA) |
10.20.23.0/26 |
| Research | 10.20.24.0/22 |
.24.0/24 · .25.0/24 (ML) |
10.20.27.0/26 |
| Business | 10.20.28.0/22 |
.28.0/24 · .29.0/24 |
10.20.31.0/26 |
| Integration | 10.20.32.0/22 |
.32.0/24 (HL7/FHIR engine) |
10.20.35.0/26 |
| Management | 10.20.40.0/22 |
.40.0/24 (jump/tooling) |
10.20.43.0/26 |
The AWS side follows the identical shape: the region /16, a reserved hub /20 for the inspection and shared-services VPCs (with 100.64.0.0/16 CGNAT “dark space” for the TGW-attach subnets to conserve RFC1918), and /22 workload VPCs that each reserve a /28 per AZ for the attachment and a /26 for interface endpoints.
| AWS block (us-east-1) | CIDR | Notes |
|---|---|---|
| Inspection VPC | 10.40.0.0/22 |
Firewall subnets /28 ×3 AZ (+ 100.64.0.0/16 dark) |
| Egress VPC | 10.40.4.0/22 |
NAT gateways, IGW, per-AZ EIP |
| Shared-services VPC | 10.40.8.0/22 |
Resolver endpoints, central endpoints |
| Clinical VPC | 10.40.16.0/22 |
TGW-attach 10.40.19.0/28 ×3, EP 10.40.19.64/26 |
| Imaging VPC | 10.40.20.0/22 |
VNA data /24, S3 gateway EP |
Vending a VPC from IPAM instead of picking a CIDR is a one-liner, and it is the single most important habit for a multi-cloud estate — it is what prevents the peering-time overlap you cannot fix while workloads are live:
# AWS: allocate the clinical VPC CIDR FROM the regional IPAM pool (no hand-picked CIDR)
aws ec2 create-vpc --ipv4-ipam-pool-id ipam-pool-0use1clinical \
--ipv4-netmask-length 22 --tag-specifications \
'ResourceType=vpc,Tags=[{Key=Name,Value=mh-use1-clinical-prod-vpc}]'
# Azure: create the IPAM pool in Virtual Network Manager, then allocate the spoke from it
az network manager ipam-pool create -g mh-connectivity-rg --network-manager mh-avnm \
-n mh-eus2-pool --address-prefixes 10.20.0.0/16
az network vnet create -g mh-clinical-eus2-rg -n mh-eus2-clinical-prod-vnet \
--ipam-allocations '[{"pool":"mh-eus2-pool","numberOfIpAddresses":"1024"}]'
The residency guarantee falls out of this plan for free: because EU ranges are physically inside the EU super-net blocks and the EU regions, “is this address subject to GDPR?” is answerable from the address itself. Global peering and gateway-transit rules that keep those boundaries intact are in VNet peering, gateway transit and global peering; the dual-stack path for the estate’s IPv6 rollout is planned in dual-stack VPC/VNet design.
Capacity and sizing
Sizing a healthcare network is dominated by two workloads that most enterprises never see at this scale: imaging (a single CT study is 300 MB–1.5 GB; the estate holds ~2.3 PB and moves it between modality, PACS, VNA and cold archive) and HL7/FHIR interface throughput (millions of ADT/ORU messages a day that cannot queue behind a saturated link). The circuits, gateways and firewalls are sized for the imaging burst and the message floor with real headroom, not the average.
| Fabric element | Chosen size | Throughput / scale | Headroom rationale |
|---|---|---|---|
| ExpressRoute (per country) | ExpressRoute Direct 10G, 5 Gbps circuit | Burst to 10 Gbps; ErGw3AZ FastPath | Nightly VNA sync + PACS pre-fetch |
| Direct Connect (per country) | 2× 10 Gbps LAG | 20 Gbps aggregate | Cross-region imaging + DR replication |
| Azure Firewall Premium | Auto-scale, AZ-redundant | ~30 Gbps (lower with TLS insp) | Sized for inspected imaging egress |
| AWS Network Firewall | Multi-AZ endpoints | 100 Gbps aggregate, scales in units | Central choke for all VPC egress |
| Transit Gateway | 1 per region | ~50 Gbps/VPC (burst), ~5k attachments | Per-segment attachments + hybrid |
| NAT gateway (AWS) | Per-AZ, per egress VPC | 45 Gbps, 55k conns/dest | Bursty SaaS + patch egress |
The gateway and inspection SKUs deserve their own table because picking one size too small forces a disruptive re-platform later, and one size too big burns budget that Meridian tracks in INR against a fixed envelope.
| Element | SKU / mode | Limits that matter | Failure if undersized |
|---|---|---|---|
| ExpressRoute gateway | ErGw3AZ |
10 Gbps, 16k routes, 4 circuits, FastPath | Data-plane bottleneck at gateway VM |
| VPN gateway (backup) | VpnGw3AZ |
1.25 Gbps, BGP | SD-WAN failover saturation |
| Azure Firewall | Premium | TLS inspection halves effective Gbps | Inspection becomes the bottleneck |
| DX virtual interface | Transit VIF → DXGW | 1 transit VIF per DX, prefix limits | Over-advertise / prefix-limit drops |
| R53 Resolver endpoint | Inbound + outbound | ~10k QPS per ENI | DNS resolution throttled |
Address capacity is deliberately generous — a /16 per region per cloud is ~65k addresses, and current utilization sits around a quarter, leaving multi-year runway even as Meridian adds hospitals and imaging centres.
| Scope | Allocated | ~Current use | Utilization | Growth runway |
|---|---|---|---|---|
Azure EUS2 /16 |
65,536 | ~16,000 | ~24% | Hospitals + telemedicine scale-out |
AWS use1 /16 |
65,536 | ~14,000 | ~21% | Imaging/VNA + research compute |
Clinical spoke /22 |
1,024 | ~420 | ~41% | EHR module + integration nodes |
PE subnet /26 |
59 usable | ~22 | ~37% | One PE per PaaS subresource |
On-prem /12 |
1,048,576 | legacy sprawl | — | Being consolidated behind summaries |
Finally, the minimum subnet sizes per workload class — the numbers workload teams request from IPAM. The imaging and AKS classes are the large ones; the Private Endpoint and gateway subnets have hard platform minimums.
| Workload class | Minimum subnet | Why |
|---|---|---|
| AKS / EKS node pool | /24 (256) |
Pod density + node scale + upgrade surge |
| Imaging (PACS/VNA) | /23–/24 |
High node count + burst compute |
| App / API tier | /25 (128) |
Scale-set headroom |
| Private Endpoint subnet | /26 (59 usable Azure) |
One NIC per PaaS subresource; dedicated |
| GatewaySubnet (Azure) | /27 minimum, /26 recommended |
ExpressRoute + VPN coexistence |
| TGW / gateway attach (AWS) | /28 per AZ |
Attachment ENI per AZ |
Private connectivity for PaaS services
Every PaaS service that touches PHI — Azure SQL, Blob/Data Lake, Key Vault, Cosmos DB, Azure Health Data Services, and on the AWS side S3, RDS, DynamoDB, HealthLake, Secrets Manager and KMS — is reached over a private path only, and its public endpoint is denied by policy, not merely left unused. The distinction is the whole point: a Private Endpoint with the public endpoint still enabled is one misconfigured firewall rule away from exposure; a Private Endpoint plus a deny on public network access is structurally private. The decision framing for private endpoints versus service endpoints is in private endpoint vs service endpoint, and the DNS mechanics at scale in Private Link private DNS for PaaS.
The flow is always the same three moves — private DNS overrides the public name to a private address, the Private Endpoint or interface endpoint carries the session over the provider backbone, and a policy guardrail makes the public path structurally unavailable — so a resource owner cannot re-open it.
On Azure, the rule is one Private Endpoint per subresource — Storage alone needs separate endpoints for blob, dfs, file and queue, each with its own privatelink.* zone and A record. Getting the subresource groupId wrong is the classic failure: the endpoint resolves, but the app cannot reach the sub-service it actually needs.
| PaaS service | Subresource (groupId) |
Private DNS zone |
|---|---|---|
| Azure SQL Database | sqlServer |
privatelink.database.windows.net |
| Storage (Data Lake) | blob, dfs |
privatelink.blob/dfs.core.windows.net |
| Key Vault | vault |
privatelink.vaultcore.azure.net |
| Cosmos DB | Sql |
privatelink.documents.azure.com |
| Health Data Services (FHIR) | fhir |
privatelink.fhir.azurehealthcareapis.com |
| Event Hubs (HL7 ingest) | namespace |
privatelink.servicebus.windows.net |
On AWS the equivalents are interface endpoints (PrivateLink) for the API-driven services and gateway endpoints for S3/DynamoDB, each locked down with an endpoint policy scoped to Meridian principals.
| AWS service | Endpoint type | Private DNS / access control |
|---|---|---|
| S3 (imaging archive) | Gateway endpoint | Bucket policy aws:sourceVpce condition |
| DynamoDB | Gateway endpoint | Route-table prefix; no NAT |
| Secrets Manager / KMS | Interface (PrivateLink) | Private DNS on; endpoint policy → org |
| RDS / RDS Data API | Interface (PrivateLink) | Private DNS; SG on ENI, app CIDR:443 |
| HealthLake (FHIR) | Interface (PrivateLink) | Endpoint policy restricts principals |
Creating the private path and — critically — denying the public one is two steps. The Private Endpoint with its DNS zone group is the connectivity; the Azure Policy deny on publicNetworkAccess is the guardrail that a resource owner cannot override:
# Private Endpoint for Azure SQL into the clinical PE subnet, with the zone group
az network private-endpoint create -g mh-clinical-eus2-rg -n pe-sql-clinical \
--vnet-name mh-eus2-clinical-prod-vnet --subnet pe-subnet \
--private-connection-resource-id <sql-server-id> --group-id sqlServer \
--connection-name sql-pe
az network private-endpoint dns-zone-group create -g mh-clinical-eus2-rg \
--endpoint-name pe-sql-clinical -n zg \
--private-dns-zone privatelink.database.windows.net --zone-name sql
// Azure Policy: DENY (not audit) publicNetworkAccess on PHI PaaS, assigned at the mh root MG
{
"if": {
"allOf": [
{ "field": "type", "in": [
"Microsoft.Sql/servers", "Microsoft.Storage/storageAccounts",
"Microsoft.KeyVault/vaults", "Microsoft.DocumentDB/databaseAccounts" ] },
{ "field": "Microsoft.Sql/servers/publicNetworkAccess", "notEquals": "Disabled" }
]
},
"then": { "effect": "deny" }
}
The AWS guardrail is the account-level S3 Block Public Access plus a resource control policy / SCP data perimeter that denies access unless the caller matches aws:sourceVpce or aws:PrincipalOrgID — public exposure becomes impossible rather than merely discouraged. The deny-public controls line up one-to-one across the two clouds:
| Control intent | Azure | AWS |
|---|---|---|
| Force private data path | Private Endpoint + privatelink DNS |
Interface/Gateway endpoint + private DNS |
| Deny public endpoint | Policy deny on publicNetworkAccess |
Block Public Access + RCP/SCP perimeter |
| Restrict callers | NSG on PE subnet, PE per subresource | Endpoint policy + SG on ENI (:443) |
| Encrypt with owned key | Key Vault CMK (HSM-backed) | KMS CMK (per-segment, HSM) |
| Prove residency | EU PaaS pinned to WEU + allowed-locations | EU PaaS pinned to euw1 + SCP region deny |
The DNS-at-scale question — whether to centralize privatelink zones on a private resolver or link zones per-hub — is the subject of private endpoints DNS at scale, and the cross-region resolution needed for active/active PHI apps is in cross-region Private Link DNS for global active-active apps. The reusable building blocks are the Azure Private Endpoint module and, where Meridian publishes its own internal services privately, the Private Link service module. With this in place, the reference architecture holds a hard line: no PHI PaaS service in the estate has a reachable public endpoint, and the guardrail — not the goodwill of a resource owner — is what keeps it that way.
Identity and zero-trust control plane
For Meridian Health, identity is not a supporting service — it is the Tier-0 control plane that every other tier authenticates against, and its blast radius is total. If the identity plane is down, 55,000 staff cannot open the EHR, radiologists cannot read studies, and the patient portal cannot issue a token; if it is compromised, every one of the 180+ applications and 2.3 PB of imaging is reachable with a stolen credential. That is why identity carries the strictest objective in the whole estate — RTO ≤ 15 minutes, RPO ≈ 0 — and why the zero-trust posture (“never trust, always verify, assume breach”) is enforced here first, before network, before data. The organising principle mirrors the zero-trust architecture blueprint: every access to Protected Health Information (PHI) is an explicitly verified transaction — authenticated identity, healthy device, evaluated risk, least-privilege authorisation, and an immutable audit record — with no implicit trust granted by network location.
The system of record for the workforce is the on-prem AD DS forest corp.meridianhealth.org, with domain controllers in all three data centres (Ashburn, Chicago, Dublin) so authentication survives the loss of any one site — the forest design and DC placement follow the pattern in Active Directory DS forest design and DC promotion. But AD DS is deliberately not the cloud identity provider. Entra ID is the single IdP that every relying party — AWS, SaaS, and the clinical applications — federates to. Staff exist in AD DS, are synchronised into Entra, and from that point forward Entra issues every token, applies Conditional Access to every sign-in, and is the only trust anchor the clouds and SaaS estate know.
Here is the Tier-0 identity plane inventory — what each component is for, where it runs, and the recovery objective it inherits:
| Component | Role in the plane | Where it runs | Objective | Failure blast radius |
|---|---|---|---|---|
AD DS corp.meridianhealth.org |
Workforce system of record; Kerberos for on-prem/clinical apps | 3 DCs (Ashburn, Chicago, Dublin) + hospital RODCs | RTO ≤15m, RPO ≈0 | On-prem clinical logins, file/print, legacy EHR auth |
| Entra Connect cloud sync | AD DS → Entra provisioning + PHS | HA agent pairs at each DC | RTO ≤30m | New/changed accounts stop flowing (existing tokens fine) |
| Entra ID tenant | Sole cloud IdP; issues all cloud tokens | Microsoft global (geo EU) | RTO ≤15m, RPO ≈0 | Every cloud + SaaS + clinical SSO sign-in |
| Conditional Access | Per-sign-in policy engine | Entra | (part of tenant) | Wrong policy = mass lockout or over-grant |
| Entra ID Protection | User/sign-in risk scoring | Entra P2 | best-effort | Risk-based blocks stop firing |
| PIM | Just-in-time privileged role activation | Entra P2 | RTO ≤15m | Admins cannot elevate (break-glass covers) |
| Entra ID Governance | JML lifecycle workflows, access reviews, access packages | Entra | RTO ≤4h | Automated joiner/leaver stalls |
| Break-glass accounts ×2 | Emergency Global Admin, excluded from all policy | Entra (cloud-only) | always available | Last-resort admin if PIM/CA fails |
Sync topology: cloud sync, PHS and Seamless SSO
Meridian runs Entra Connect cloud sync, not the older heavyweight Connect Sync server, and the decision is deliberate — the trade-offs are laid out in Entra Connect Sync vs cloud sync. Cloud sync uses lightweight provisioning agents (installed in HA pairs at each DC, so no single sync server is a Tier-0 single point of failure), supports the multi-domain reality of an IDN cleanly, and moves the sync ruleset into the cloud. Authentication uses Password Hash Synchronisation (PHS) plus Seamless SSO — the mechanics are covered in Entra Connect Sync deep dive: PHS, PTA, Seamless SSO. PHS is chosen over Pass-Through Auth and over AD FS federation for one overriding reason: cloud sign-in must not depend on an on-prem service being reachable. When a data centre or ExpressRoute circuit fails, clinicians must still authenticate to the cloud EHR — PHS makes Entra self-sufficient for auth, while PTA/AD FS would tie every cloud sign-in back to an on-prem endpoint. AD FS is being retired entirely.
| Sync/auth model | Cloud sign-in survives on-prem outage? | Infra footprint | Why (not) chosen for Meridian |
|---|---|---|---|
| Cloud sync + PHS + Seamless SSO | Yes — Entra authenticates independently | Lightweight HA agents, no sync server | Chosen — Tier-0 resilience, multi-forest ready, cloud-managed rules |
| Connect Sync (server) + PHS | Yes | A Windows server (+ staging server) to patch/HA | Rejected — heavier Tier-0 component to keep alive |
| Pass-Through Authentication | No — needs on-prem agent at sign-in | Agents on the auth path | Rejected — couples every cloud login to on-prem |
| AD FS federation | No — federation server on the auth path | AD FS farm + WAP + certs | Being retired — most fragile, largest attack surface |
Registering a cloud-sync agent and confirming PHS/Seamless SSO is live:
# (AADConnectProvisioningAgentSetup.exe installs the service; registration is interactive/Global-Admin-scoped)
# Confirm the agents are healthy and cloud sync is the active model:
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/onPremisesPublishingProfiles/provisioning/agents" \
--query "value[].{id:id, status:status, machine:machineName}" -o table
# Confirm PHS is on (feature flag) and Seamless SSO computer object exists in AD (AZUREADSSOACC)
az rest --method GET \
--url "https://graph.microsoft.com/beta/directory/onPremisesSynchronization" \
--query "value[0].features.{phs:passwordSyncEnabled, ssso:blockCloudObjectTakeoverThroughHardMatchEnabled}"
Entra as the federation hub
Every relying party trusts Entra and only Entra. AWS consumes Entra through IAM Identity Center, which federates via SAML/OIDC and maps Entra groups to AWS permission sets — AWS has zero local IAM users except break-glass, so an HR termination revokes AWS access on the next SCIM cycle. SaaS (ServiceNow, SAP SuccessFactors/S4, Workday) uses SAML/OIDC SSO with SCIM provisioning; the enterprise-app + claims pattern is the one in Entra SAML SSO with custom claims mapping. Clinical apps (Epic-class EHR, PACS/VNA viewers) SSO via SAML for the human sign-in but issue SMART on FHIR scoped tokens for PHI API access, so an app never holds blanket data access — only patient/*.read-style scopes, consented per launch and written to the immutable PHI audit log.
The control plane, end to end — AD DS as the source, cloud sync into the Entra hub, Conditional Access in the middle of every issuance, and federation out to AWS, SaaS and clinical relying parties:
Break-glass and the JML/credentialing lifecycle
Two cloud-only break-glass accounts (*.onmicrosoft.com, not synced from AD) are the emergency floor: permanently assigned Global Administrator, excluded from every Conditional Access policy and from PIM, credentialed with FIDO2 security keys split across two sealed physical safes, and wired to a Sentinel alert that fires on any sign-in. They are the only standing privileged accounts in the tenant — the full governance model is in Entra break-glass emergency access. Excluding them from CA and asserting the alert:
# Break-glass group is excluded from CA (see CA policy set below) and alerted on every use.
# Detection rule (Sentinel KQL) — any break-glass sign-in is a P1:
# SigninLogs | where UserPrincipalName in ("bg1@meridianhealth.onmicrosoft.com","bg2@...")
# | project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName
Joiner-Mover-Leaver (JML) is not an IT-only process in a hospital — it is fused to HR (Workday) and to clinical credentialing (the medical staff office: NPI, DEA, state licensure, board certification, and granted clinical privileges). A physician is not merely “hired”; they are credentialed and privileged to practise, and their access must not exceed their privileges. Meridian drives this with Entra ID Governance lifecycle workflows (see Entra lifecycle workflows for JML automation), with the credentialing system as a gating attribute on top of the HR feed.
| Lifecycle event | Authoritative source | Gate / condition | Automated action |
|---|---|---|---|
| Joiner (staff) | Workday hire record → AD DS | Start date reached | Provision account, baseline apps, licence, welcome; add to persona group |
| Joiner (clinician) | Workday + credentialing (NPI/DEA/licence/privileges) | Credentialing complete — else no clinical access | EHR/e-prescribing access scoped to granted privileges, not job title |
| Mover | Workday transfer / new privilege grant | Dept or privilege change | Recalculate dynamic persona groups; access review of retained rights |
| Leaver (planned) | Workday termination | Last-day timestamp | Disable sign-in, revoke sessions/tokens, reclaim licence, start data-retention hold |
| Leaver (emergency) | Security/HR manual trigger | Immediate | Real-time session revoke across Entra + AWS + SaaS via SCIM de-provision |
| Credential lapse | Credentialing system (licence expiry) | Licence/DEA expired | Auto-suspend e-prescribing + clinical scopes before expiry, notify med-staff office |
| Contractor / locum | Sponsor + access package | Time-boxed access package | Auto-expiry with sponsor recertification; no permanent standing access |
| Partner (referring org) | B2B invitation + access package | External identity, guest | Scoped to specific apps only; access review each cycle |
The credential-lapse row is the healthcare-specific one that generic IAM misses: a lapsed DEA or state licence must automatically suspend prescribing and clinical PHI scopes even though the person is still employed — driven by a lifecycle workflow triggered on the credentialing attribute, not by a helpdesk ticket.
Employee and clinician digital workplace
Meridian’s workforce is not one population but twelve personas, each with a different identity source, application set, device reality, and acceptable authentication friction. A radiologist reading from a diagnostic workstation, a nurse tapping between shared carts on a ward, a researcher in an isolated trials enclave, and a contact-centre agent taking calls from home cannot be served by one access policy — and the entire Conditional Access and Intune model downstream is keyed to these personas. Personas are materialised as dynamic groups (see Entra dynamic groups membership rules) computed from HR + credentialing attributes, so movement between roles re-buckets a user automatically. The workplace itself is M365 E5 (Exchange Online, Teams, SharePoint/OneDrive) plus the clinical estate, all SSO from Entra.
This persona/access matrix is the backbone the rest of Part 4 references — it is deliberately exhaustive:
| Persona | Identity source | Primary applications | Device class | Primary auth | CA persona group |
|---|---|---|---|---|---|
| Clinician (generic) | AD DS + credentialing | EHR, results, secure msg, Teams | Shared clinical WS | Badge tap-and-go + PIN | ca-persona-clinical |
| Nurse | AD DS + credentialing | EHR, eMAR, ADT, care mgmt | Shared cart / WOW | Badge tap-and-go + PIN | ca-persona-clinical |
| Physician | AD DS + credentialing | EHR, CPOE, e-prescribe, imaging | Physician mobile + WS | FIDO2 / Hello + tap | ca-persona-prescriber |
| Radiologist | AD DS + credentialing | PACS, RIS, VNA, dictation | Diagnostic WS (calibrated) | FIDO2 security key | ca-persona-imaging |
| Pharmacist | AD DS + credentialing | Pharmacy, eMAR, e-prescribe verify | Corp laptop / WS | FIDO2 / Hello | ca-persona-prescriber |
| Lab tech | AD DS + credentialing | LIS/RIS, analyzers, orders/results | Lab WS | Badge tap + PIN | ca-persona-clinical |
| Researcher | AD DS or B2B | Trials apps, de-id data, ML workspace | Corp laptop → enclave | FIDO2 + PIM to enclave | ca-persona-research |
| Contact-centre | AD DS | Scheduling, portal admin, CRM, Teams | Corp laptop (remote) | Phone/authenticator MFA | ca-persona-contactcenter |
| Corporate | AD DS | M365, SAP, finance, HR, BI | Corp laptop | Authenticator / Hello | ca-persona-corp |
| Contractor | Access package | Scoped project apps only | Corp or MAM BYOD | MFA, time-boxed | ca-persona-contractor |
| Partner (referring) | B2B guest | XCA/XDS exchange, specific apps | Their device | Cross-tenant MFA | ca-persona-partner |
| Admin | AD DS (separate admin account) | Azure/AWS portals, Intune, security | Privileged Access WS | FIDO2 + PIM JIT | ca-persona-admin |
Fast, secure authentication on shared clinical workstations
The defining workplace problem in a hospital is the shared workstation: a ward cart or workstation-on-wheels (WOW) used by dozens of clinicians per shift, where a full username/password sign-in per patient encounter is clinically unacceptable (seconds matter, hands are gloved) — yet a generic ward login shared by everyone destroys the per-user audit trail that HIPAA and the medical record demand. Meridian resolves this with tap-and-go: an Imprivata OneSign-class proximity-badge broker sits in front of the session, and a badge tap (optionally + PIN for the first tap of a shift) unlocks in ~2 seconds — but the identity asserted to Entra and the EHR is the individual clinician’s UPN, never a shared account. Walk away and the session auto-locks; the next clinician taps and gets their session. Audit stays per-person; speed stays clinical.
| Shared-WS auth pattern | Speed | Audit fidelity | How it maps to Entra | Best fit |
|---|---|---|---|---|
| Tap-and-go (Imprivata-class) | ~2s badge tap | Per-individual UPN in assertion | Imprivata brokers, SSO federates to Entra/EHR | Wards, WOWs, high-turnover carts |
| Entra shared device mode | Fast sign-out/in | Per-session, device shared | Native Entra SharedDeviceMode on the endpoint |
Frontline handhelds, some carts |
| Windows Hello for Business | PIN/biometric | Per-individual, strong | Cert/key on device, phishing-resistant | Assigned/physician workstations |
| FIDO2 security key | Tap key | Per-individual, phishing-resistant | Native Entra FIDO2 | Radiology diagnostic WS, admin |
| Kiosk (auto-login, restricted) | Instant | Device-level only — no PHI | Kiosk account, no PHI apps | Patient check-in, wayfinding |
The rule that makes this safe: kiosks that auto-login carry no PHI applications at all (check-in, wayfinding only), while anything that reaches the EHR asserts an individual identity. Diagnostic and physician workstations skip badge-only and use phishing-resistant Windows Hello or FIDO2, because those personas reach prescribing and imaging where the FIDO2 passwordless rollout baseline applies.
Workforce, device posture, Conditional Access, then apps — the flow every clinician sign-in takes, with tap-and-go on the left preserving individual audit and session controls protecting PHI on the right:
Every workplace application is fronted by Entra SSO with automated provisioning, so a single JML event propagates everywhere without per-app administration:
| Application | SSO protocol | Provisioning | CA authentication context | Notes |
|---|---|---|---|---|
| Epic-class EHR | SAML → SSO; SMART on FHIR for APIs | Feed + break-glass EHR access | c1 PHI-bulk (phishing-resistant) |
Tap-and-go brokered; per-user audit to chart |
| PACS / VNA viewers | SAML / OpenID | Group-based | c1 PHI-bulk |
Zero-footprint, no PHI cached on endpoint |
| M365 E5 (Exchange/Teams) | Native Entra | Licence via group | Baseline + MAM on mobile | Teams for clinical secure messaging |
| ServiceNow (ITSM) | SAML | SCIM | Baseline | Change/incident for the estate |
| Workday (HR) | SAML/OIDC | Source of truth (outbound) | Corp persona | Drives JML upstream |
| SAP S/4HANA | SAML | SCIM/manual | Corp persona | Finance/supply |
| Integration engine admin (Rhapsody/Mirth) | SAML + PIM | Manual, privileged | c2 admin activation |
Interface engine console is privileged |
Conditional Access and PIM policy model
Conditional Access is where zero trust becomes concrete: every sign-in is scored on signals (user risk, sign-in risk, device state, application sensitivity, network location) and the policy set converts that score into grant controls (what must be true — MFA, compliant device, authentication strength) and session controls (how long and how constrained — sign-in frequency, persistent browser, download limits). Meridian runs CA as a numbered, persona-targeted set using authentication context, following the pattern in Conditional Access at scale with personas and authentication context, and every policy ships report-only first — the discipline from deploy baseline CA policies in report-only — so a misfire is caught in What If and the sign-in logs before it locks out a hospital.
Signals in, grant and session controls out, and a separate stricter path for administrators who hold no standing privilege and must activate just-in-time:
The Conditional Access policy set
This is the enforced policy set — persona-scoped, authentication-context-aware, and layered so the controls are additive:
| ID | Policy | Target | Key condition | Grant | Session |
|---|---|---|---|---|---|
| CA001 | Baseline MFA all users | All users, all apps | Exclude break-glass | Require MFA | — |
| CA002 | Block legacy authentication | All users | Legacy auth clients | Block | — |
| CA003 | Require compliant/hybrid device | All users, all apps | — | Compliant OR hybrid-joined | — |
| CA004 | PHI apps — phishing-resistant | c1 PHI-bulk (EHR, PACS, FHIR) |
Clinical/imaging personas | Authentication strength: phishing-resistant | Sign-in freq 8h; no persistent browser |
| CA005 | PHI on unmanaged — no download | PHI apps, unmanaged device | Device not compliant | Grant web-only | App-enforced: block download |
| CA006 | Admin portals — phishing-resistant | c2 admin (Azure/AWS/Intune/Security) |
ca-persona-admin |
Phishing-resistant MFA | Sign-in freq 4h; no persist |
| CA007 | PIM activation — auth context | c2 role-activation context |
Privileged roles | FIDO2 auth strength | — |
| CA008 | Sign-in risk | All users | Risk = high | Require MFA + password change or block | — |
| CA009 | User risk | All users | User risk = high | Require secure password change | — |
| CA010 | Mobile — app protection | iOS/Android, PHI + mail | BYOD | Require approved app + APP policy | — |
| CA011 | Named/blocked locations | All users | Sign-in from blocked country | Block | — |
| CA012 | EU data residency | ca-persona-* EU staff, EU apps |
Access to EU-resident apps | Compliant + EU region | — |
| CA013 | Research enclave | ca-persona-research |
Trials/ML workspace | Compliant + PIM-activated | Sign-in freq 1h; no download off-enclave |
| CA014 | B2B partners | ca-persona-partner guests |
External identities | Cross-tenant MFA + compliant (their tenant) | Sign-in freq per session |
| CA015 | Contractors time-boxed | ca-persona-contractor |
Scoped apps only | MFA + compliant/MAM | Sign-in freq 4h |
Authentication strength is the mechanism behind rows CA004/006/007 — it maps a resource sensitivity to an allowed method list, so PHI and privileged access simply cannot be reached with a phishable factor:
| Authentication strength | Allowed methods | Applied to | Personas |
|---|---|---|---|
| Phishing-resistant MFA (built-in) | FIDO2, Windows Hello, cert-based | PHI-bulk (c1), admin (c2), PIM activation |
Physician, radiologist, pharmacist, admin, researcher-in-enclave |
| MFA (standard) | Authenticator (number match), FIDO2, Hello | Baseline all-user, corp apps | Corporate, contact-centre, clinical (via tap+MFA registration) |
| Passwordless | Authenticator phone sign-in, FIDO2, Hello | Corp modern | Corporate opt-in |
Assigning the built-in phishing-resistant strength to a PHI policy via Graph:
# The built-in "Phishing-resistant MFA" authentication strength has a well-known id.
# A CA policy references it in grantControls.authenticationStrength.id:
az rest --method POST \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
--headers "Content-Type=application/json" \
--body '{
"displayName": "CA004 - PHI apps require phishing-resistant MFA",
"state": "enabledForReportingButNotEnforced",
"conditions": {
"users": { "includeGroups": ["<ca-persona-clinical>","<ca-persona-imaging>"],
"excludeGroups": ["<break-glass-group>"] },
"applications": { "includeApplications": ["<epic-app-id>","<pacs-app-id>","<fhir-api-id>"] }
},
"grantControls": {
"operator": "OR",
"authenticationStrength": { "id": "00000000-0000-0000-0000-000000000004" }
},
"sessionControls": {
"signInFrequency": { "value": 8, "type": "hours", "isEnabled": true },
"persistentBrowser": { "mode": "never", "isEnabled": true }
}
}'
Note state: enabledForReportingButNotEnforced — every policy lands in report-only, is validated against real sign-ins (and troubleshooting CA sign-in log policy blocks), then flipped to enabled.
Identity Protection risk and PIM just-in-time
Rows CA008/009 are driven by Entra ID Protection, which scores risk from leaked credentials, impossible travel, anomalous tokens and more; the tuning discipline is in ID Protection risk-based policies. Risk becomes an automated response, not a report:
| Risk signal | Level | Automated response | Rationale |
|---|---|---|---|
| Sign-in risk | High | Block or require phishing-resistant MFA + fresh session | Stolen-token / impossible-travel on PHI is contained live |
| User risk | High | Require secure password change at next sign-in | Compromised credential remediated before broad access |
| Leaked credentials | Any | Force reset + revoke sessions | Known-bad password cannot ride existing tokens |
| Anomalous privileged sign-in | Medium+ | Step-up + Sentinel P1 alert | Admin accounts get zero benefit of the doubt |
Privileged access uses no standing admin — every high-privilege role (Global Admin, Privileged Role Admin, Intune Admin, Security Admin, plus the cloud-infra roles federated to AWS) is eligible only through PIM, activated just-in-time with MFA, justification, approval for Tier-0, and a hard time cap; the model follows PIM for roles with approval workflows and the broader PIM/PAM architecture. Activation itself is gated by CA007 (FIDO2). The role settings:
| Role | Eligible group | Max activation | Approval | Activation requires | Access review |
|---|---|---|---|---|---|
| Global Administrator | pim-global-admin |
4h | Yes (2 approvers) | FIDO2 + justification | Monthly |
| Privileged Role Admin | pim-priv-role-admin |
4h | Yes | FIDO2 + justification | Monthly |
| Security Administrator | pim-sec-admin |
8h | No | FIDO2 + justification | Quarterly |
| Intune Administrator | pim-intune-admin |
8h | No | FIDO2 + justification | Quarterly |
| AWS infra (via IAM IC) | pim-aws-infra |
8h | Yes for prod | FIDO2 + ticket | Quarterly |
| Research enclave admin | pim-research-admin |
2h | Yes | FIDO2 + IRB reference | Per study |
Creating an eligible (not active) role assignment and tightening its activation policy via Graph:
# Make a user ELIGIBLE for Intune Administrator (no standing access):
az rest --method POST \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilityScheduleRequests" \
--headers "Content-Type=application/json" \
--body '{
"action": "adminAssign",
"principalId": "<admin-account-objectId>",
"roleDefinitionId": "3a2c62db-5318-420d-8d74-23affee5d9d5",
"directoryScopeId": "/",
"scheduleInfo": { "startDateTime": "2026-07-08T00:00:00Z",
"expiration": { "type": "afterDuration", "duration": "P365D" } }
}'
# Then the roleManagementPolicy rules enforce: activation ≤ PT8H, require FIDO2 (auth context c2),
# require justification, and alert on activation → routed to Sentinel.
Endpoint, UEM and workstation management
A device is a first-class zero-trust signal: Conditional Access rows CA003/005/010 all consume device compliance, so endpoint management is not a productivity nicety — it is the gate that keeps unmanaged, unhealthy or personal devices away from PHI. Meridian manages endpoints with Microsoft Intune, and the design principle is one enrolment/compliance profile per device class, each emitting a compliance fact that CA turns into an allow/deny; the compliance-to-CA linkage is exactly endpoint Conditional Access with device compliance filters. The hard healthcare twist is that a large fleet — biomedical devices — cannot take a management agent at all, and those are handled by network isolation, never by CA.
Each device class, how it enrols, and where its compliance verdict lands:
| Device class | OS | Enrolment method | Ownership | Management profile | Compliance → CA |
|---|---|---|---|---|---|
| Corp laptops | Windows 11 | Autopilot (zero-touch) | Corporate | Full MDM, BitLocker, Defender | Compliant → all apps |
| Corp laptops | macOS | ABM/ADE auto-enrol | Corporate | Full MDM, FileVault, platform SSO | Compliant → all apps |
| Shared clinical WS | Windows 11 | Autopilot + shared device mode / kiosk | Corporate | Kiosk/multi-user, tap-and-go, auto-lock | Compliant → clinical apps |
| Physician mobile (corp) | iPadOS/iOS | ADE (supervised) | Corporate | Full MDM, per-app VPN, Epic Haiku | Compliant → PHI mobile |
| Physician mobile (BYOD) | iOS/Android | MAM-WE (no enrolment) | Personal | App-protection policy only | Protected app → mail/EHR container |
| Patient check-in kiosk | Windows/Android | Kiosk auto-login | Corporate | Single-app kiosk, no PHI apps | Device-only; no PHI grant |
| Biomed / medical device | Embedded/legacy | None — cannot enrol | Clinical engineering | Defender for IoT + NAC segmentation | Not in CA path — network-isolated |
Corp Windows uses Autopilot for zero-touch provisioning (see Intune Autopilot zero-touch); macs auto-enrol through Apple Business Manager with FileVault and platform SSO (Intune macOS management); and personal phones use MAM app-protection without enrolment (Intune MAM app protection for BYOD) so a nurse’s own phone gets a protected container (encrypted, no copy-out, remote-wipeable) around Teams and the EHR mobile app — Meridian never takes control of the personal device, only the corporate data on it.
Compliance policies as the CA signal
Compliance is a small set of hard facts per class; fail any and the device flips non-compliant and CA cuts it from PHI automatically:
| Compliance setting | Corp laptop | Shared clinical WS | Corp mobile | BYOD (MAM) |
|---|---|---|---|---|
| Disk encryption | BitLocker required | BitLocker required | Device encryption | App-level encryption |
| Min OS version | Enforced (patch ring) | Enforced | Enforced | App-enforced |
| Antivirus / EDR | Defender for Endpoint on | Defender on | — | — |
| Threat level (MTD) | ≤ Medium | ≤ Low | ≤ Low | ≤ Low |
| Jailbreak/root | N/A | N/A | Blocked | Blocked |
| Firewall / secure boot | Required | Required | N/A | N/A |
| PIN/complexity | Required | Session PIN | Required | App PIN |
| Non-compliance action | Retire after grace | Immediate block | Block PHI | Block container |
Defining a Windows compliance policy that becomes the CA signal, via the Intune Graph API:
# Compliance policy: BitLocker + secure boot + Defender + min OS → emits "compliant" for CA003.
az rest --method POST \
--url "https://graph.microsoft.com/v1.0/deviceManagement/deviceCompliancePolicies" \
--headers "Content-Type=application/json" \
--body '{
"@odata.type": "#microsoft.graph.windows10CompliancePolicy",
"displayName": "Win11 Clinical - Compliant baseline",
"bitLockerEnabled": true,
"secureBootEnabled": true,
"defenderEnabled": true,
"osMinimumVersion": "10.0.22631.0",
"deviceThreatProtectionEnabled": true,
"deviceThreatProtectionRequiredSecurityLevel": "medium",
"scheduledActionsForRule": [{
"ruleName": "PasswordRequired",
"scheduledActionConfigurations": [
{ "actionType": "block", "gracePeriodHours": 24 }
]
}]
}'
Device classes on the left, one enrolment and compliance path in the middle, the CA gate, and the biomedical fleet split off into network isolation because it can never carry an agent:
Biomedical and unpatchable medical devices
The most healthcare-specific endpoint problem: FDA-cleared medical devices — infusion pumps, MRI/CT consoles, legacy modality workstations, patient monitors — frequently cannot be patched or agent-managed without voiding certification, and many run end-of-life operating systems. They are ungovernable by Intune and must never be in the Conditional Access path. Instead they are contained:
| Control | Mechanism | What it enforces |
|---|---|---|
| Discovery + inventory | Defender for IoT / clinical asset system | Every biomed device profiled and classified (FDA class, OS, CVEs) |
| Passive monitoring | Defender for IoT sensors (no agent) | Anomaly/threat detection without touching the device |
| Microsegmentation | NAC + firewalled VLANs per device class | A pump can reach only its server, never the EHR or the internet |
| Least connectivity | Explicit allow-list flows | Modality → PACS only; monitor → gateway only |
| Compensating controls | Virtual patching at the firewall/IPS | Known CVE traffic blocked at the segment boundary |
The rule is absolute: an unpatchable pump on a segmented VLAN that is compromised cannot route to patient data or to prescribing — the network, not the endpoint agent, is the control, and it is engineered so device fragility never becomes a PHI exposure.
Global edge and ingress
Everything so far protects the workforce. The patient-facing surface — the digital front door (patient portal, MyChart-class app, scheduling, telemedicine intake, and the public FHIR APIs) — faces the open internet and must be defended at a global edge before a request ever reaches an origin. Meridian fronts these with Azure Front Door Premium for Azure-hosted portals and AWS CloudFront for AWS-hosted apps, each with a WAF in prevention mode and health-and-geo-aware DNS, and — the non-negotiable rule — the origin has no public inbound path of its own: the app and API are reachable only through the edge. The Front Door configuration follows Azure Front Door Standard/Premium routing and caching, the WAF baseline web application firewall edge protection, and the origin-cloaking discipline mirrors multicloud origins with WAF/CDN origin cloaking.
Patient resolves through health-aware DNS to the nearest residency-correct edge, the WAF inspects, and only the edge — over Private Link — can reach an origin that is otherwise dark to the internet:
The edge stack, per cloud, is symmetric in intent:
| Layer | Azure (US portals) | AWS (EU / AWS-hosted) | Purpose |
|---|---|---|---|
| CDN / edge | Front Door Premium | CloudFront | Global anycast, TLS 1.2+ termination, caching |
| WAF | Front Door WAF (Premium) | AWS WAF on the distribution | OWASP + bot + custom rules, prevention mode |
| DNS / routing | Traffic Manager (geo/priority) | Route 53 (geolocation + health) | Residency + failover routing |
| Origin link | Private Link to app | VPC origin / origin access control | No public origin IP |
| Origin identity | X-Azure-FDID + service tag |
Custom header secret + OAC | Origin trusts only the edge |
| Certificate | Managed cert on custom domain | ACM cert | HTTPS-only |
WAF policy and origin lockdown
The WAF runs managed rule sets in prevention mode (it blocks, it does not merely log) plus custom rules tuned for a patient front door — rate-limiting the login and appointment endpoints against credential-stuffing, and geo-fencing where a portal is region-scoped:
| Rule | Type | Action | Why |
|---|---|---|---|
| OWASP core rule set | Managed | Block | SQLi/XSS/RCE against the portal + API |
| Bot manager | Managed | Block/challenge | Scrapers and credential-stuffing bots |
| Rate limit — login/booking | Custom | Block over threshold | Stop login-flood and appointment-scalping |
| Geo-fence (EU portal) | Custom | Block non-EU | Residency + reduce attack surface |
| Known-bad IP / anomaly | Managed (reputation) | Block | Cut noise before origin |
| Body size / method limits | Custom | Block | Reject malformed/oversized requests |
Origin lockdown is the control that makes a leaked origin URL worthless. Front Door Premium reaches the app over Private Link (the app has no public IP), and the app additionally validates the X-Azure-FDID header and restricts inbound to the AzureFrontDoor.Backend service tag, so a direct curl to any discovered origin address is refused:
| Technique | Cloud | How it is enforced | Defeats |
|---|---|---|---|
| Private Link origin | Azure | Front Door → app over Private Link; no public IP | Direct origin reachability entirely |
X-Azure-FDID header check |
Azure | App rejects requests without the tenant’s Front Door ID | Requests via a different Front Door profile |
| Service-tag restriction | Azure | NSG/app allows only AzureFrontDoor.Backend |
Direct internet hits to the origin |
| Origin Access Control (OAC) | AWS | CloudFront signs origin requests; S3/ALB requires it | Direct S3/ALB access |
| Custom header secret | AWS | ALB rule requires a secret header only CloudFront sends | Bypassing CloudFront |
| mTLS to origin | Both | Origin requires the edge’s client cert | Any non-edge caller |
Provisioning a Private Link origin behind Front Door Premium and enabling WAF prevention:
# Front Door Premium origin bound to the app via Private Link (no public inbound on the app):
az afd origin create --resource-group mh-plat-connectivity --profile-name mh-fd-patient \
--origin-group-name portal-og --origin-name portal-eastus2 \
--host-name mh-eus2-portal-prod.azurewebsites.net --origin-host-header mh-eus2-portal-prod.azurewebsites.net \
--enable-private-link true \
--private-link-resource "$(az webapp show -n mh-eus2-portal-prod -g mh-lz-telemed --query id -o tsv)" \
--private-link-location eastus2 --private-link-sub-resource-type sites \
--priority 1 --weight 1000 --enabled-state Enabled
# WAF policy in PREVENTION mode with the managed OWASP + bot rule sets:
az network front-door waf-policy create --resource-group mh-plat-connectivity \
--name mhPatientWaf --sku Premium_AzureFrontDoor --mode Prevention
az network front-door waf-policy managed-rules add --resource-group mh-plat-connectivity \
--policy-name mhPatientWaf --type Microsoft_DefaultRuleSet --version 2.1
az network front-door waf-policy managed-rules add --resource-group mh-plat-connectivity \
--policy-name mhPatientWaf --type Microsoft_BotManagerRuleSet --version 1.0
DNS health routing and data residency
DNS is where GDPR data residency and regional failover are enforced before a packet reaches an edge. EU patients must resolve only to EU-resident origins (West Europe / eu-west-1) and US patients to US origins; a region that fails its health probe is dropped from rotation within seconds. Meridian layers Traffic Manager (Azure, geographic + priority routing) and Route 53 (geolocation + health-checked failover):
| Routing goal | Mechanism | Behaviour |
|---|---|---|
| EU-in-EU residency | Traffic Manager Geographic / Route 53 geolocation | EU client → EU edge/origin only; US → US |
| Nearest healthy region | Traffic Manager Performance / Route 53 latency | Lowest-latency healthy edge |
| Active/active failover | Health probes on both origins | Unhealthy region auto-removed |
| Planned failover | Priority/weighted records | Drain a region for maintenance |
A Route 53 health check that pulls a region out of rotation the moment its origin health endpoint fails:
# Health check the portal's health endpoint; failure removes the region from the geolocation record set.
aws route53 create-health-check \
--caller-reference mh-portal-euw1-$(date +%s) \
--health-check-config '{
"Type": "HTTPS",
"FullyQualifiedDomainName": "portal-eu.meridianhealth.org",
"ResourcePath": "/healthz",
"Port": 443,
"RequestInterval": 10,
"FailureThreshold": 3
}'
The active/active origins (portal and FHIR API run two-region in-country — East US 2 + Central US for the US, EU pair for GDPR) mean a full region loss is a routing change, not an outage: kill one region and the patient’s session continues on the other, the edge simply steers to the surviving healthy origin. The patient front door stays open, residency-correct, and un-bypassable.
Clinical systems architecture
Everything else in this document — the management-group tree, the ExpressRoute circuits, the Sentinel workspace — exists to keep the systems in this section running and their data private. For Meridian Health these are the applications a nurse, a physician or a pharmacist touches at the bedside, and when one of them stalls a clinician stops ordering, a lab result stops posting, or a medication stops being verified. That is why the clinical estate is architected differently from a corporate SaaS: the system of record (SoR) — the authoritative clinical database — is treated as the crown jewel and kept where its latency, licensing and support model demand, while a system of engagement (SoE) — the web, mobile and API tier clinicians and patients actually hit — is pushed to the cloud where elasticity, global reach and zero-downtime deployment live.
Meridian runs an Epic-class EHR. The record itself — Epic Chronicles, running on the InterSystems IRIS data platform — stays on-prem in the Ashburn and Chicago data centres, mirrored between them, because that is where the low-latency ECP (Enterprise Cache Protocol) app-server fabric, the operational-database licensing and the 24×7 vendor support model are anchored. What moves to Azure is the engagement surface: MyChart (the patient portal), the Hyperdrive / Hyperspace Web presentation tier for clinicians, Interconnect (Epic’s web-services and FHIR gateway), and the mobile back-ends for Haiku/Canto (clinician) and Rover (inpatient nursing). The cloud tier is a stateless projection of the record: lose an Azure region and you lose sessions, never the chart.
That split is the single most important decision in the clinical architecture, so state the responsibilities explicitly before anything else.
| Concern | System of Record (on-prem) | System of Engagement (Azure mh-lz-clinical) |
|---|---|---|
| What lives here | Epic Chronicles on IRIS; ODB; ancillary SoRs (Willow, Beaker, Radiant) | MyChart web, Hyperdrive Web, Interconnect (FHIR/HL7), mobile gateways |
| State | Authoritative, transactional, single source of truth | Ephemeral sessions, read caches, projections |
| Latency budget | Sub-millisecond ECP to app servers; kept co-located | Tens of ms to SoR over ExpressRoute; tolerant, cached |
| HA model | Synchronous IRIS mirror Ashburn↔Chicago; async DR | Active/active AKS across East US 2 + Central US behind Front Door |
| Failure blast radius | Catastrophic — protected at all costs | Session loss only; users re-authenticate and resume |
| Change cadence | Vendor-gated, quarterly, change-controlled | Continuous, blue/green, per-service |
| Data class | PHI — highest | PHI in transit + short-lived cache; no durable SoR copy |
The engagement tier reaches the record over dual ExpressRoute (East US 2 and West Europe), never the public internet, so the same private path that carries a MyChart appointment lookup carries a clinician’s chart open. The mechanics of those circuits — private peering, the /26 gateway subnets, the failover behaviour — are covered in ExpressRoute circuits and peering types; here it is enough that PHI has no route to 0.0.0.0/0 from any clinical subnet.
The clinical-systems inventory
Meridian runs 180+ applications; roughly 40 are clinical in the sense that a care team depends on them in real time. The table below is the load-bearing artifact of this whole section — it fixes, for every clinical system, its recovery tier (from the pinned RTO/RPO model), its HA model, its data class, and where it is hosted. Non-clinical corporate apps (SAP S/4HANA, HR) are out of scope here; they sit in mh-lz-corp.
| Clinical system | Epic/other module | Tier | Hosting | HA model | Data class |
|---|---|---|---|---|---|
| EHR — record (SoR) | Chronicles / IRIS | Tier-1 | On-prem Ashburn+Chicago | Sync IRIS mirror + async DR | PHI |
| EHR — engagement (SoE) | Hyperdrive Web | Tier-1 | Azure eus2+cus | Active/active AKS + Front Door | PHI (transit/cache) |
| Patient portal / digital door | MyChart | Tier-1 | Azure eus2+cus | Active/active AKS + Front Door + WAF | PHI |
| HIS / patient administration | Grand Central | Tier-1 | On-prem (SoR) | Mirror | PHI |
| ADT / registration | Prelude / ADT | Tier-1 | On-prem (SoR) | Mirror | PHI |
| Scheduling | Cadence | Tier-1 | On-prem + SoE cache | Mirror + AKS cache | PHI |
| CPOE / order entry | EpicCare Inpatient | Tier-1 | On-prem (SoR) | Mirror | PHI |
| Pharmacy | Willow Inpatient/Ambulatory | Tier-1 | On-prem (SoR) | Mirror | PHI |
| eMAR (med administration) | Rover / MAR | Tier-1 | On-prem + mobile GW (Azure) | Mirror + AKS gateway | PHI |
| e-Prescribing | Willow + Surescripts | Tier-1 | On-prem + SaaS network | Mirror + partner SLA | PHI |
| LIS (laboratory) | Beaker | Tier-1 | On-prem (SoR) | Mirror | PHI |
| RIS (radiology) | Radiant | Tier-1 | On-prem (SoR) | Mirror | PHI |
| Cardiology | Cupid | Tier-1 | On-prem (SoR) | Mirror | PHI |
| Emergency dept | ASAP | Tier-1 | On-prem (SoR) | Mirror | PHI |
| Telemedicine core | MyChart Video / partner | Tier-1 | Azure eus2+cus | Active/active | PHI |
| Care management / pop-health | Healthy Planet | Tier-2 | Azure eus2 | Zone-redundant + region DR | PHI |
| Revenue cycle — hospital | Resolute HB | Tier-2 | On-prem + Azure analytics | Mirror + ZR analytics | PHI + PCI |
| Revenue cycle — professional | Resolute PB | Tier-2 | On-prem + Azure analytics | Mirror + ZR analytics | PHI + PCI |
| Managed care / payer | Tapestry | Tier-2 | On-prem | Mirror | PHI + PII |
| Interoperability / HIE | Care Everywhere | Tier-1 | On-prem + mh-lz-integration |
Mirror + active/active engine | PHI |
| Clinical data warehouse | Caboodle / Cogito | Tier-2 | Azure eus2 (mh-lz-research) |
Zone-redundant | PHI (governed) |
Two reading notes. First, Tier-1 clinical systems are overwhelmingly on-prem SoR — the record, orders, results, meds and emergency care must survive a total cloud outage, so they never depend on Azure or AWS being up. Cloud earns Tier-1 only for the engagement surface (portal, telemedicine, mobile gateways) where active/active buys availability the on-prem mirror cannot. Second, data class drives controls, not tier — Resolute is “only” Tier-2 for recovery but carries PHI and PCI, so it inherits both HIPAA and card-industry segmentation.
Ancillary clinical systems: CPOE, pharmacy, LIS, RIS
The ancillaries are where orders become actions, and each is its own SoR that the EHR orchestrates over interfaces (the next section). The flow every clinician relies on is: CPOE captures the order → the order routes to the fulfilling ancillary (LIS for labs, Radiant/PACS for imaging, Willow for meds) → the result or medication comes back as a discrete, coded observation.
- CPOE (Computerized Provider Order Entry) lives in the EHR itself (EpicCare) and is Tier-1 because a blocked order is a blocked treatment. Orders fan out as HL7
ORM/OMLmessages through the integration engine. - Pharmacy (Willow) + eMAR + e-prescribing. Willow verifies and dispenses; the closed-loop eMAR (electronic Medication Administration Record, delivered at the bedside through Rover on a mobile gateway hosted in Azure) enforces the five rights by barcode-scanning patient wristband and medication against the active order. Outpatient e-prescribing leaves the estate over the Surescripts network (an external SaaS partner with its own SLA), which is why it appears with a partner dependency in the inventory. Controlled substances add EPCS (Electronic Prescribing of Controlled Substances) two-factor identity-proofing — a hard requirement that ties into Entra Conditional Access.
- LIS (Beaker) receives
ORMorders and returnsORUresults; auto-verification rules release normal results while abnormals hold for a tech. RIS (Radiant) owns the radiology order/scheduling/reporting workflow and drives the modality worklist and PACS covered in the imaging section.
Environment separation and PHI safety
Clinical software is validated software: you do not test a new medication-interaction rule in production. Meridian carries a full prod / non-prod ladder per landing-zone subscription (mh-lz-clinical-{dev,test,stage,prod}), and critically, non-production must never contain live PHI. Lower environments are seeded from de-identified or synthetic data; only production and a tightly-controlled, access-audited “prod-mirror” break-fix copy hold real charts.
| Environment | Subscription | Data | Who has access | Network |
|---|---|---|---|---|
| Dev | mh-lz-clinical-dev |
Synthetic only | Developers (broad) | Isolated spoke, no on-prem SoR route |
| Test / QA | mh-lz-clinical-test |
Synthetic / masked | QA + dev | Isolated spoke |
| Stage / validation | mh-lz-clinical-stage |
De-identified subset | Validation team (scoped) | Restricted route to SoR test instance |
| Prod | mh-lz-clinical-prod |
Live PHI | Clinicians + break-glass only | Full private path to SoR |
| Prod-mirror (break-fix) | mh-lz-clinical-prod (tagged) |
Live PHI (read) | Named engineers, time-boxed | Same as prod, audited |
For the production PHI data plane, three controls are non-negotiable and enforced by Azure Policy at the management-group root so no subscription can opt out:
- Private-only PaaS. Every PaaS dependency of a clinical system (Storage, SQL, Key Vault, Service Bus, the FHIR service) is reached through a Private Endpoint; public network access is denied and shared-key/SAS auth is off. The decision between service and private endpoints for this is laid out in Private Endpoint vs Service Endpoint, and the DNS plumbing in Private Link and Private DNS for PaaS.
- Customer-managed keys in a Managed HSM. PHI at rest is encrypted with CMKs in Key Vault Managed HSM (FIPS 140-2 Level 3), rotated on policy; the platform-managed default is not sufficient for the HITRUST control set. Key operations are themselves audited — see Key Vault: secrets, keys, certificates.
- Immutable PHI-access audit + break-the-glass. Every read of a chart is logged, and the log is written to append-only, immutable storage so it cannot be altered to hide inappropriate access — the HIPAA Security Rule’s audit-control requirement.
Break-the-glass deserves its own mechanism. In an emergency a clinician may need a record they have no standing relationship with (an unconscious trauma patient, a cross-facility transfer). Rather than grant broad standing access “just in case,” Meridian models emergency access as a time-boxed, alerting, review-mandatory elevation through Entra Privileged Identity Management (PIM):
| Break-glass property | Implementation | Why |
|---|---|---|
| Trigger | Clinician activates a PIM-eligible “Emergency chart access” role | No standing broad access to over-grant |
| Duration | Time-boxed (e.g. 4h), auto-expires | Access ends without a manual revoke |
| Justification | Mandatory free-text + ticket reference at activation | Creates the “why” record up front |
| Alerting | High-severity Sentinel incident on activation | Security + compliance see it in real time |
| Audit | Immutable log of activation + every chart touched | Reconstructable for OCR / 42 CFR Part 2 |
| Review | Mandatory post-hoc access review within 72h | Confirms the access was appropriate |
A minimal activation-and-alert wiring looks like this:
# requiring justification + MFA at activation.
az role assignment create \
--assignee-object-id "$CLINICIAN_GROUP" --assignee-principal-type Group \
--role "MH Emergency Chart Access" \
--scope "/subscriptions/$SUB_CLINICAL_PROD" \
--description "PIM-eligible only; activation audited"
# PIM policy (Graph): expiration PT4H, requireJustification, requireMfaOnActivation,
# and an activation alert routed to the Sentinel workspace.
// Sentinel: every break-glass activation in the last 24h, with who + why.
AuditLogs
| where OperationName == "Add member to role (PIM activated)"
| where TargetResources has "Emergency Chart Access"
| project TimeGenerated, InitiatedBy.user.userPrincipalName,
Justification = tostring(AdditionalDetails), Result
| order by TimeGenerated desc
The observability side of this — routing those activations and chart-access events into a monitored workspace with alerts — reuses the patterns in Azure Monitor & Application Insights for observability.
The clinical picture end to end: clinicians and patients hit the cloud engagement tier, which reads and writes the authoritative on-prem record over private links, while orders fan to the ancillary SoRs and every PHI touch lands in an immutable audit store.
Interoperability and integration architecture
Meridian’s 40 clinical systems and 140 supporting ones do not speak one language — they speak five. A single lab result may arrive as an HL7 v2 ORU from Beaker, be exposed to a mobile app as a FHIR Observation, be summarised into an IHE XDS.b document for an outside hospital, trigger an X12 837 claim to a payer, and post a DICOM structured report if it is an imaging study. The integration layer is the universal translator, and for a health system its reliability is patient safety: a dropped ORU is a missing result, a mis-mapped ADT is a patient merged into the wrong chart. This section is the busiest in the whole estate, and it lives in a dedicated subscription — mh-lz-integration — precisely so its blast radius and change cadence are isolated from the clinical apps it serves.
The core is an interface engine (an integration engine of the Rhapsody / Mirth Connect / Cloverleaf / Corepoint class; Meridian standardises on Rhapsody, fronting Epic’s own Bridges/Interconnect). It is not middleware in the abstract — it is a message router that receives HL7 v2 over MLLP (Minimal Lower Layer Protocol) on TCP, validates and transforms, persists, and forwards, with an application-level acknowledgement contract that makes at-least-once delivery survivable.
HL7 v2: the message-type map
HL7 v2 is still the workhorse — the overwhelming majority of real-time clinical messaging is v2, not FHIR. Every integration analyst carries this table in their head; here it is on paper. Direction is relative to the EHR (the hub).
| Type | Name | Key trigger events | Direction (vs EHR) | What it carries / use |
|---|---|---|---|---|
| ADT | Admit / Discharge / Transfer | A01 admit, A02 transfer, A03 discharge, A04 register, A08 update, A11/A13 cancel, A28/A31 person, A40 merge | EHR → all downstream | The master demographics + encounter feed; drives every other system’s patient context |
| SIU | Scheduling Information Unsolicited | S12 new, S13 reschedule, S14 modify, S15 cancel, S26 no-show | Cadence → ancillaries | Appointments; keeps LIS/RIS/telemed calendars in sync |
| ORM / OML | Order message | O01 (ORM), O21 (OML) | CPOE → LIS/RIS/pharmacy | Orders — labs, imaging, meds; the “do this” message |
| ORU | Observation Result Unsolicited | R01 | LIS/RIS/devices → EHR | Results — discrete coded observations, reports, waveforms |
| DFT | Detailed Financial Transaction | P03 | Charge capture → Resolute | Charges → revenue cycle; drives billing |
| MDM | Medical Document Management | T02 original, T04 status, T06 addendum, T08 replace, T11 cancel | Transcription/docs → EHR | Clinical documents (notes, dictations) + their lifecycle |
| MFN | Master Files Notification | M02 staff, M05 location, M08 test master, M10 test batteries | Master data → subscribers | Keeps shared code sets (providers, locations, orderables) aligned |
Every one of these is answered by an ACK (acknowledgement) message: the receiver returns MSA-1 = AA (Application Accept), AE (Application Error) or AR (Application Reject), keyed by the sender’s MSH-10 message control ID. The sender holds the message until it sees that application ACK — a TCP ack is not enough, because the receiver’s database may reject a message the network delivered fine.
A concrete ADT^A01 (admit) as it crosses the wire — pipe-delimited, MLLP-framed:
MSH|^~\&|GRANDCENTRAL|MH-ASHBURN|RHAPSODY|MH-INT|20260708T141530||ADT^A01^ADT_A01|MSG00001847|P|2.5.1
EVN|A01|20260708141500|||^SMITH^JORDAN^^^^^^MH^^^^^PROV
PID|1||MRN7788341^^^MH^MR||DOE^JANE^A^^^^L||19780412|F||2106-3|221 OAK ST^^ASHBURN^VA^20147^USA||^PRN^PH^^^703^5550142|||M|||123-45-6789
PV1|1|I|MED2^0214^01^MH-ASHBURN^^^^^MED SURG||||9876543^ATTENDING^PAT^^^^MD|||MED||||ADM|A0|||9876543^ATTENDING^PAT^^^^MD|INP|VN00458821|SELF||||||||||||||||||||MH-ASHBURN|||||20260708141500
Read it the way an analyst does: MSH names sender/receiver applications and the message type/version; EVN timestamps the event; PID is the patient (MRN7788341 in the MH MRN assigning authority, note the SSN in PID-19 — PHI that must be masked in non-prod); PV1 is the encounter (inpatient I, med-surg unit, attending provider, visit number VN00458821). A single mis-set PID-3 assigning authority here is how a patient gets merged into the wrong chart — which is why the engine validates identity before it forwards.
The interface engine and its interface domains
The engine is deployed active/active across East US 2 and Central US in mh-lz-integration, with the on-prem Rhapsody/Bridges tier bridging to the SoR. Its job decomposes into channels (a.k.a. routes/interfaces), each a source→transform→destination pipeline. The engine’s non-negotiable property is persist-before-ACK: it writes every inbound message to a guaranteed-delivery store before it ACKs the source, so a crash after ACK can never strand an in-flight order, and the same store powers point-in-time replay of any channel after an outage.
| Engine component | Responsibility | Meridian implementation |
|---|---|---|
| Communication point (in) | Terminate MLLP/TCP, frame messages | Rhapsody comm point, TCP :2575, mTLS |
| Route / channel | Filter, map, transform, enrich | Per interface (ADT-to-LIS, ORU-to-EHR, …) |
| Message store | Guaranteed delivery + replay buffer | Persistent queue, encrypted, 30–90d retention |
| Transformer | v2↔v2 remap, v2↔FHIR, code translation | JavaScript/Grammar maps; terminology service |
| Communication point (out) | Deliver + await application ACK | MLLP/HTTPS to destination, retry on AE/AR |
| Error / DLQ | Quarantine poison messages | Dead-letter store + operator redrive |
| Monitor | Throughput, latency, backlog, ACK rates | Metrics → Azure Monitor + Sentinel |
Like the clinical apps, interfaces run a prod/non-prod domain separation — you never point a test modality at the production result feed:
| Interface domain | Subscription | Endpoints | Data |
|---|---|---|---|
| Prod | mh-lz-integration-prod |
Live source/destination AE + MLLP endpoints | Live PHI |
| Stage | mh-lz-integration-stage |
Vendor-conformance + validation endpoints | De-identified |
| Test | mh-lz-integration-test |
Loopback + simulator endpoints | Synthetic |
| Dev | mh-lz-integration-dev |
Developer sandboxes | Synthetic |
FHIR R4, SMART on FHIR and the API gateway
Where v2 handles real-time internal messaging, FHIR R4 is the API front door for modern apps, patient access (the ONC/CMS interoperability rules), and analytics. Meridian runs the managed Azure Health Data Services FHIR service (the successor to Azure API for FHIR) in mh-lz-integration, fronted by API Management which terminates SMART on FHIR authorization. The engine keeps the FHIR store current by transforming v2 events into FHIR resources on write.
Access is never anonymous. SMART on FHIR layers OAuth2 on FHIR: an app does an authorization-code + PKCE flow, receives a scoped, short-lived, audience-bound token, and can only touch what the scope allows. The gateway (APIM) validates the token, enforces rate limits and logs the call; the FHIR service enforces resource-level authorization. APIM’s role and tiers are covered in API Management tiers and architecture.
| FHIR concern | Endpoint / mechanism | Meridian control |
|---|---|---|
| Capability discovery | GET /metadata (CapabilityStatement) |
Public metadata, no PHI |
| Read a resource | GET /Patient/{id}, /Observation?... |
US Core profiles; patient/*.read scope |
| Patient everything | GET /Patient/{id}/$everything |
Patient-access app, patient-scoped token |
| Bulk export (pop-health) | GET /Group/{id}/$export (async, NDJSON) |
system/*.read, backend service, to a private container |
| Write-back | PUT/POST (transaction bundle) |
user/*.write, provenance recorded |
| Auth | SMART OAuth2 auth-code + PKCE / client-credentials | Entra as IdP; audience-bound tokens |
| Transport | HTTPS only, via APIM, Private Endpoint to FHIR svc | mTLS internal; no public FHIR endpoint |
A patient-access read, as an app performs it after the SMART handshake:
# Token already obtained via SMART auth-code+PKCE, audience = the FHIR service.
curl -sS 'https://fhir.mh-eus2-int-prod.example/Observation?patient=Patient/abc123&category=laboratory&_sort=-date&_count=20' \
-H 'Authorization: Bearer eyJ...' -H 'Accept: application/fhir+json'
# APIM validates scope (patient/Observation.read), rate-limits, and logs the call id;
# the FHIR service returns a US Core Bundle. No token ⇒ 401 at the gateway, never reaching PHI.
DICOM, IHE profiles and cross-organisation exchange
Two more languages sit alongside v2 and FHIR. DICOM is imaging’s wire and file format (detailed in the imaging section). IHE profiles standardise cross-enterprise exchange — how Meridian shares a record with an outside hospital or a regional Health Information Exchange (HIE), which Epic surfaces as Care Everywhere.
| IHE profile | Full name | What it does at Meridian |
|---|---|---|
| XDS.b | Cross-Enterprise Document Sharing | Register + share clinical documents (CCDs) via a registry/repository |
| PIX / PDQ | Patient Identifier Cross-ref / Demographics Query | Resolve a patient’s identity across facility MRNs (the MPI) |
| PIXm / PDQm | FHIR variants of PIX/PDQ | Same, exposed as FHIR $match / Patient search |
| XCA | Cross-Community Access | Query + retrieve documents across HIE communities via gateways |
| XDS-I.b | XDS for Imaging | Share imaging manifests (KOS) + WADO retrieve of the pixels |
| ATNA | Audit Trail & Node Authentication | Node auth (mTLS) + the audit record for every exchange |
| CT | Consistent Time | NTP-sync all nodes so audit timestamps reconcile |
X12 EDI for payers
Everything financial with a payer is X12 EDI, exchanged through a clearinghouse, not FHIR. These are the transactions that get Meridian paid and confirm a patient is covered before care.
| Transaction | Name | Direction | Use |
|---|---|---|---|
| 270 / 271 | Eligibility & Benefit Inquiry / Response | Meridian → payer / payer → Meridian | “Is this patient covered, for what?” — checked at scheduling/registration |
| 276 / 277 | Claim Status Inquiry / Response | Meridian → payer / payer → Meridian | “Where is my claim?” |
| 278 | Services Review (prior auth / referral) | Meridian ↔ payer | Authorisation before a procedure |
| 834 | Benefit Enrollment & Maintenance | Employer/payer → Meridian (Tapestry) | Membership enrollment for managed-care lines |
| 835 | Claim Payment / Remittance Advice (ERA) | Payer → Meridian | Payment + adjudication detail; posts to Resolute |
| 837 | Health Care Claim (P / I / D) | Meridian → payer | The claim itself — Professional, Institutional, Dental |
| 820 | Premium Payment | Payer/employer ↔ Meridian | Premium remittance for managed-care |
Each interchange is acknowledged: a TA1 confirms the envelope was well-formed and a 999 (implementation acknowledgement) reports syntax acceptance per segment — the EDI analogue of the HL7 ACK.
Event mesh, resilient queue and replay
Not every consumer wants a point-to-point HL7 feed. For fan-out — a new result that should notify pop-health, a CDS engine, and an analytics pipeline at once — Meridian publishes normalised clinical events onto an event mesh (Azure Service Bus topics for command/transactional semantics, Event Hubs where high-throughput streaming/device telemetry needs partitioned ordering). The trade-off between topics and queues is walked through in Service Bus queues vs topics, and partitioned streaming in Event Hubs partitions and consumer groups.
The rule that keeps an at-least-once mesh safe is the same rule from the interface engine: persist, retry with backoff, dead-letter, and replay — and consumers must be idempotent because duplicates will happen.
| Resilience control | Mechanism | Setting at Meridian |
|---|---|---|
| Guaranteed accept | Engine persists before ACK; Service Bus durable | No message lost on crash |
| Ordering where needed | Service Bus sessions keyed by patient/encounter | Per-patient event order preserved |
| Retry | Exponential backoff on transient delivery failure | Max delivery count = 10 |
| Dead-letter (DLQ) | Poison messages to the topic/queue DLQ | With reason + error, never dropped |
| Redrive | Operator replays from DLQ after fixing the map | Controlled, rate-limited |
| Channel replay | Engine message-store point-in-time replay | Rebuild a downstream after an outage |
| Idempotency | Consumer dedups on MSH-10 / event id |
Duplicate delivery ⇒ no-op |
Inspecting and draining the dead-letter path operationally:
# How many poison messages are parked, and peek the reason before redriving.
az servicebus topic subscription show \
--namespace-name mh-eus2-int-prod-sbns --topic-name clinical-results \
--name pophealth --query "countDetails.deadLetterMessageCount"
# Peek the DLQ (entity path .../$DeadLetterQueue), read DeadLetterReason,
# fix the transform, then redrive from the DLQ at a throttled rate.
End-to-end traceability
With five languages and a mesh in the middle, “did that result actually reach the chart?” must be answerable. Meridian threads the HL7 MSH-10 control ID (and a generated correlation ID for FHIR/event writes) through every hop — engine, mesh, FHIR write — so a single observation is traceable source→consumer, and nightly reconciliation counts (messages sent vs applied per interface) prove nothing was silently lost. That trace ID is the same one that lands in the immutable audit store, closing the loop between “delivered” and “who saw it.”
Sources emit HL7 v2 into the engine, which persists, transforms and forwards — to legacy destinations directly, and to modern consumers through the FHIR gateway and event mesh — with a dead-letter queue catching poison messages and a message store enabling replay.
Imaging architecture
Imaging is Meridian’s largest single data class — ~2.3 PB and growing across 9 imaging centres and 14 hospital radiology departments — and its most latency-sensitive read: a radiologist reading a stroke CT cannot wait on a spinning-disk retrieve. It is also the most vendor-entangled: replace a PACS (Picture Archiving and Communication System) and you do not want to migrate 2.3 PB of pixels. That single fact drives the whole design — a Vendor-Neutral Archive (VNA) holds the authoritative, standards-pure copy, and the PACS becomes a replaceable reading application in front of it.
| Imaging component | Role | Meridian hosting |
|---|---|---|
| Modalities | CT, MR, US, XR, mammo, angio — acquire images | On-prem, segmented clinical VLANs per site |
| Modality Worklist (DMWL) | Feeds the ordered study to the modality | From RIS/Radiant via the engine |
| MPPS | Modality Performed Procedure Step (start/complete) | Modality → RIS |
| DICOM router / gateway | Route, compress, TLS, store-and-forward | On-prem appliance per site → cloud over Private Link |
| PACS | Diagnostic reading, hanging protocols, workflow | Azure mh-lz-imaging eus2 (reading) |
| RIS (Radiant) | Orders, scheduling, reporting | On-prem SoR (see clinical) |
| VNA | Canonical, vendor-neutral archive | Azure Blob (Hot→Archive) in mh-lz-imaging |
| Zero-footprint viewer | Web/EHR image viewing, no local cache | Azure eus2+cus, active/active |
| AI / CAD | Triage, detection (e.g. stroke, nodules) | mh-lz-research GPU, reads from VNA |
Modality worklist first, then pixels
The workflow starts before the scan. The modality queries the DICOM Modality Worklist (DMWL) — a C-FIND against the worklist SOP class, populated from Radiant orders through the engine — so the technologist selects the ordered study and the patient/accession identity is stamped onto the images at acquisition, not hand-typed at the console (hand-typing is how images end up on the wrong patient). As the scan proceeds, MPPS reports procedure started and completed back to RIS, so the department board reflects reality.
DICOM services: C-STORE, C-MOVE and DICOMweb
DICOM’s classic DIMSE services run over TCP (default port 104, commonly 11112), addressed by AE Title (Application Entity). The four that matter:
| Service | Direction | Use at Meridian |
|---|---|---|
| C-ECHO | SCU → SCP | “DICOM ping” — verify connectivity/association |
| C-STORE | SCU → SCP | Push images: modality → router → PACS/VNA |
| C-FIND | SCU → SCP | Query studies (and the modality worklist) |
| C-MOVE / C-GET | SCU ↔ SCP | Retrieve a study to a destination AE (prior-fetch, viewer) |
Alongside the classic DIMSE services, Meridian exposes the archive over DICOMweb (the RESTful DICOM) for modern viewers and cloud services: STOW-RS (store), WADO-RS (retrieve), QIDO-RS (query). The zero-footprint viewer and AI services speak DICOMweb; the modalities speak classic DIMSE to the local router.
Key operational notes for the modality↔router↔archive path:
# C-STORE (push) — modality/router stores to the VNA's DICOM endpoint.
# Association negotiates presentation contexts (SOP class + transfer syntax,
# e.g. JPEG-2000 / JPEG-LS lossless for compression). Called AE = archive.
storescu -aet MH_CT01 -aec MH_VNA vna.mh-eus2-img-prod.internal 11112 image.dcm
# C-MOVE (retrieve) — pull a study to a named destination AE (e.g. prior-fetch
# to the reading PACS). C-MOVE routes to a REGISTERED AE by title, which is why
# destination AE registration is a governance step, not a free-for-all.
movescu -aet MH_PACS -aec MH_VNA -aem MH_PACS \
-k QueryRetrieveLevel=STUDY -k StudyInstanceUID=1.2.840.113619.2.55... \
vna.mh-eus2-img-prod.internal 11112
# All associations are mTLS; endpoints resolve to Private Endpoints only.
Two design consequences fall out of C-MOVE specifically: retrieval targets a registered AE title (so a rogue destination cannot pull studies), and because modalities carry frozen, often-unpatchable firmware, they never route to the internet — the local router is their only egress, over Private Link/PrivateLink to the cloud archive.
PACS reads, the VNA is canonical
The split of duties is the crux of the imaging architecture: PACS is where radiologists read (fast cache, hanging protocols, reporting integration), but the VNA is the source of truth. Studies are archived once into the VNA in standards-pure DICOM; the PACS holds a working copy of recent and relevant priors. This is what makes a PACS swap survivable — the 2.3 PB never moves.
Storage tiers and lifecycle
The VNA sits on Azure Blob (S3 on the AWS side), and the economics only work because access is wildly skewed: a study is read intensely for days, occasionally for a year (comparison priors), then almost never — but must be retained for years or decades. So a lifecycle policy ages objects across tiers by last-access, matching cost to reality. The tier semantics (minimum durations, retrieval latency, rehydration) are detailed in Blob access tiers: hot, cool, cold, archive.
| Tier | Azure / AWS | Retrieval | Min duration | Meridian use |
|---|---|---|---|---|
| Hot | Blob Hot / S3 Standard | Milliseconds | None | New studies + likely priors, ≤90 days |
| Cool / warm | Blob Cool / S3 Standard-IA | Milliseconds (higher $/read) | 30 days | Studies 90 days–1 year |
| Cold | Blob Cold / S3 Glacier Instant | Milliseconds (higher still) | 90 days | Studies 1–3 years, rarely read |
| Archive | Blob Archive / S3 Glacier Deep | Hours (rehydrate) | 180 days | Long-tail retention to legal limit |
The cold/archive tiers are set immutable (WORM) — Blob immutability policies / S3 Object Lock — for the legal retention window, so neither ransomware nor a mis-scoped delete can destroy a study inside its mandated life. A representative Azure management policy:
{
"rules": [
{
"enabled": true,
"name": "imaging-lifecycle",
"type": "Lifecycle",
"definition": {
"filters": { "blobTypes": ["blockBlob"], "prefixMatch": ["studies/"] },
"actions": {
"baseBlob": {
"tierToCool": { "daysAfterLastAccessTimeGreaterThan": 90 },
"tierToCold": { "daysAfterLastAccessTimeGreaterThan": 365 },
"tierToArchive": { "daysAfterLastAccessTimeGreaterThan": 1095 }
}
}
}
}
]
}
Access-time tracking must be enabled on the account for daysAfterLastAccessTimeGreaterThan to work — otherwise a prior pulled for comparison would keep aging as if untouched. Rehydration from Archive is asynchronous (hours), so the viewer treats an archived study as “retrieving,” and a prior-fetch service pre-warms likely-needed priors from the study order so the radiologist rarely waits.
Retention and legal hold
Retention is regulatory, varies by jurisdiction and study type, and is longer than most engineers expect — which is exactly why the immutable window is policy-driven, not guessed.
| Study / record | Typical retention | Driver |
|---|---|---|
| Adult diagnostic imaging | 7–10 years (state-dependent) | State medical-record law |
| Paediatric imaging | To age of majority + years (often 21+) | Minor’s extended limitation period |
| Mammography (MQSA) | ≥10 years (≥5 if no priors retained) | Federal MQSA rule |
| Radiation dose records | Long-term per state / accreditation | Dose-tracking mandates |
| EU studies (GDPR residency) | Per member-state; stored West Europe only | GDPR data residency |
| Litigation / legal hold | Indefinite until released | Overrides the lifecycle expiry |
A legal hold overrides the lifecycle engine entirely: a held study cannot be tiered-to-expiry or deleted regardless of age, implemented as a legal-hold tag on the immutable container. EU-acquired imaging is pinned to West Europe storage for GDPR residency and never replicated to a US region.
Zero-footprint viewing and EHR integration
Clinicians view through a zero-footprint (ZFP) HTML5 viewer that streams WADO-RS and renders in the browser — no DICOM cached on the endpoint, so a lost laptop or a contractor’s tablet leaks nothing. The viewer runs active/active in East US 2 and Central US. Crucially, it launches in-context from the EHR: a clinician opens the patient’s chart in Hyperdrive, clicks the imaging study, and the viewer opens that patient’s images via SSO — no separate login, no patient re-selection (the re-selection that causes wrong-patient errors). Diagnostic radiologists still read on the full PACS workstation; the ZFP viewer serves the referring physicians, the ED, and the wards.
Pixels flow C-STORE from modality through the site router into the cloud, are archived once into the VNA and aged across Blob tiers by lifecycle policy, and are streamed back on demand to zero-footprint viewers launched in-context from the EHR.
Telemedicine and digital care
Meridian’s telemedicine platform is a Tier-1, active/active service (RTO ≤30m, RPO ≤5m) that carries the same clinical weight as the EHR: a virtual visit produces a legal encounter, a prescription, a referral and a bill. It cannot be a bolted-on video app. It is a chain of services — scheduling, a virtual waiting room, digital intake and consent, an encrypted media plane, secure asynchronous messaging, and EHR write-back — each of which touches PHI and each of which must survive the loss of an entire Azure region. The platform lives in the mh-lz-telemed subscription (Azure) with a symmetric footprint in the mh-telemed-prod account under the Workloads/Corp OU (AWS), fronted by Azure Front Door and AWS CloudFront so a patient in New Jersey and a clinician in Chicago meet on the nearest healthy edge.
The platform is not one product but a set of cooperating planes. The signaling and application plane is stateless HTTP/WebSocket you can scale and fail over like any web tier; the media plane is UDP/SRTP that behaves nothing like HTTP and needs its own HA and NAT-traversal design; and the clinical-integration plane writes structured data back into the Epic-class EHR over FHIR. Keeping those three planes distinct is the single most important design decision, because they fail differently and scale differently. The table below is the component map every team works from.
| Capability | Azure service | AWS service | PHI? | Tier / HA |
|---|---|---|---|---|
| Edge + WAF | Front Door Premium + WAF | CloudFront + AWS WAF | In transit | Global anycast |
| API / auth | API Management (internal VNet) | API Gateway + Lambda authorizer | Tokens | Multi-region A/A |
| Scheduling | AKS microservice + Cosmos DB | EKS + DynamoDB global table | Yes | A/A, RPO ≤5m |
| Virtual waiting room | AKS + SignalR / Web PubSub | EKS + API Gateway WebSocket | Minimal | A/A |
| Intake + e-consent | App Service + FHIR service | Fargate + HealthLake | Yes | A/A |
| Video (SFU) | AKS-hosted SFU + TURN | EKS-hosted SFU + TURN | In transit (SRTP) | Per-region, ICE re-home |
| Secure messaging | Service Bus + FHIR Communication | SQS/SNS + HealthLake | Yes | A/A, store-and-forward |
| EHR write-back | Azure Health Data Services (FHIR R4) | AWS HealthLake (FHIR R4) | Yes | A/A via interface engine |
| Patient identity | Entra External ID (CIAM) | Cognito federated to Entra | Identifiers | Global |
Meridian’s virtual front door reuses the same patient identity and digital-front-door patterns proven in the Healthcare patient portal on AWS with HIPAA and CIAM build — telemedicine is one more authenticated surface behind the same Entra External ID tenant, never a separate identity island.
The encrypted media plane: SFU, TURN and DTLS-SRTP
Consumer video toolkits default to peer-to-peer mesh, which is unacceptable here for three reasons: it exposes patient IP addresses to the clinician (and vice-versa), it collapses under multi-party or poor networks, and it gives you no server-side point to record, transcode or lawfully intercept for compliance. Meridian therefore relays all media through a Selective Forwarding Unit (SFU) — a media server that receives each participant’s encrypted stream once and forwards it to the others without decoding the payload. Media is WebRTC with DTLS-SRTP: keys are negotiated per-session over DTLS, and no media key ever transits the signaling channel. The SFU sees SRTP packets it forwards; with insertable streams / end-to-end encryption enabled for high-sensitivity behavioral-health visits, the SFU cannot even decrypt the frame.
The hard part is NAT traversal from hospital networks and cellular carriers that block UDP. Meridian runs coturn TURN servers in each region, reachable on TCP/TLS 443 so the call tunnels out of the most restrictive network as if it were HTTPS. The media path and its decisions:
| Concern | Choice | Why for healthcare |
|---|---|---|
| Topology | SFU (not mesh/MCU) | No P2P IP leak; server-side recording/audit point; scales to consults |
| Media encryption | DTLS-SRTP, keys per session | PHI in transit; keys never touch signaling |
| Behavioral health | Insertable-streams E2EE | SFU relays but cannot decrypt (42 CFR Part 2 sensitivity) |
| NAT traversal | coturn, turns: on 443 |
Survives hospital UDP blocks + carrier CGNAT |
| Codec | VP8/H.264 + Opus, simulcast | Downshift layers on low bandwidth, not a frozen call |
| Region HA | Per-region SFU, ICE restart | Region loss re-homes signaling; ICE re-negotiates path |
| Recording | Opt-in, server-side, CMK to Blob/S3 | Consent-gated; immutable, encrypted at rest |
A minimal, production-shaped coturn config that forces TLS relay and short-lived credentials (the app mints ephemeral TURN credentials via the REST API pattern so no static secret ships to clients):
listening-port=3478
tls-listening-port=443
fingerprint
use-auth-secret
static-auth-secret=${TURN_REST_SECRET} # from Key Vault / Secrets Manager
realm=telehealth.meridianhealth.org
cert=/etc/turn/fullchain.pem
pkey=/etc/turn/privkey.pem
no-tlsv1
no-tlsv1_1
cipher-list="ECDHE+AESGCM:CHACHA20"
no-multicast-peers
denied-peer-ip=10.0.0.0-10.255.255.255 # no relay into the corp/clinical supernet
total-quota=1200
The denied-peer-ip line matters: it stops a malicious client from using the public TURN relay as a pivot into the 10.0.0.0/12 private estate — the relay may only reach the internet-facing SFU, never the clinical VLANs.
Two walkthrough sentences for the diagram: a patient authenticates through Front Door and API Management, the app checks consent, and only then is a media session negotiated against the regional SFU with TURN fallback. Every clinical artifact the visit produces writes back to the EHR over FHIR R4.
Scheduling, consent and EHR write-back
A virtual visit is bracketed by two integration events. On the front end, scheduling is driven by HL7 v2 SIU messages (S12 new appointment, S14 modification, S15 cancellation) flowing from the EHR through the integration engine into the telemedicine scheduling service, so the patient’s app and the clinician’s in-EHR worklist show the same slot. On the back end, everything the encounter generates is written as FHIR R4 resources through Azure Health Data Services / AWS HealthLake, then reconciled into the chart. The write-back map is the contract between the video team and the clinical-informatics team:
| Visit artifact | FHIR R4 resource | Trigger / notes |
|---|---|---|
| The encounter itself | Encounter (class = VR virtual) |
Opened on admit, closed on end; carries start/stop |
| Consent to be seen + record | Consent |
Captured pre-visit; blocks start until active |
| Clinical note | DocumentReference + Composition |
Signed note; PDF/CDA attachment optional |
| e-Prescription | MedicationRequest |
Routed to pharmacy via NCPDP SCRIPT downstream |
| Referral / order | ServiceRequest |
To specialist worklist or lab/imaging |
| Async patient message | Communication |
Secure inbox; threaded to the encounter |
| Vitals from RPM device | Observation |
Linked from the RPM path (next section) |
The write-back is idempotent and transactional. The service posts a FHIR transaction bundle so the note, prescription and encounter close either all succeed or all roll back — a partial write that closes the encounter but loses the note is a clinical-safety event. A trimmed bundle:
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{ "request": { "method": "PUT", "url": "Encounter/enc-8841" },
"resource": { "resourceType": "Encounter", "id": "enc-8841",
"status": "finished", "class": { "code": "VR", "display": "virtual" },
"subject": { "reference": "Patient/mrn-40192" },
"period": { "start": "2026-07-08T15:02:00Z", "end": "2026-07-08T15:19:00Z" } } },
{ "request": { "method": "POST", "url": "DocumentReference" },
"resource": { "resourceType": "DocumentReference", "status": "current",
"type": { "coding": [{ "system": "http://loinc.org", "code": "34109-9" }] },
"context": { "encounter": [{ "reference": "Encounter/enc-8841" }] } } },
{ "request": { "method": "POST", "url": "MedicationRequest" },
"resource": { "resourceType": "MedicationRequest", "status": "active",
"intent": "order", "subject": { "reference": "Patient/mrn-40192" },
"encounter": { "reference": "Encounter/enc-8841" } } }
]
}
The e-consent and intake flow runs before any media is negotiated: the patient completes intake forms and signs a consent that is persisted as a Consent resource; the virtual waiting room (a SignalR / API Gateway WebSocket channel) holds them in a waiting state until the clinician clicks Admit. This ordering is enforced server-side — the signaling service refuses to issue an SFU join token until it reads an active Consent for that encounter, so “start the video before consent” is impossible by construction, not by UI discipline.
Not every digital encounter is synchronous video. Meridian’s secure messaging plane carries asynchronous e-visits, provider-to-provider e-consults and patient questions as FHIR Communication resources on a Service Bus / SNS+SQS backbone with store-and-forward durability, so a message queued while a clinician is off-shift is delivered, threaded to the right encounter and answered within the SLA — never lost, never emailed in the clear. Store-and-forward is also how Meridian serves tele-dermatology and tele-radiology, where a high-resolution image and a structured questionnaire are submitted once and read later, decoupling the patient’s upload from the specialist’s read.
Finally, the platform is engineered for the worst network the patient has. Simulcast lets the SFU drop to a 180p layer on a congested LTE uplink instead of freezing; the mobile clients prefer audio continuity over video; and TURN-over-443 keeps the call up inside hospitals that block everything but HTTPS. The HA and PHI controls that make this a Tier-1 service:
| Control | Implementation | Requirement it meets |
|---|---|---|
| Active/active regions | Front Door + Cosmos/DynamoDB global tables | RTO ≤30m, RPO ≤5m |
| No public PHI endpoint | APIM/API GW internal; PE to FHIR | HIPAA §164.312 transmission |
| CMK everywhere | Key Vault / KMS CMK on Blob, DB, recordings | HITRUST, encryption at rest |
| Media E2EE (opt) | Insertable streams for behavioral health | 42 CFR Part 2 |
| Immutable audit | Every join/admit/write to Log Analytics + Sentinel / CloudTrail | PHI access audit |
| Break-glass | Time-boxed elevated access, alerted | Emergency access with audit |
Medical devices, IoT and remote patient monitoring
Meridian runs three overlapping fleets that all speak “IoT” but demand very different handling: remote patient monitoring (RPM) devices in patients’ homes (blood-pressure cuffs, glucometers, pulse oximeters, weight scales), connected clinical devices inside hospitals (bedside monitors, infusion pumps, ventilators, imaging modalities), and biomedical assets the network merely tracks (wheelchairs, pumps, portable ultrasounds). The security posture is inverted from normal IT: many of these endpoints are FDA-cleared as a unit and cannot be patched, re-imaged or have an agent installed without voiding their clearance. You cannot harden the host, so the entire strategy is identity at the edge and containment on the wire.
The RPM path runs in the mh-lz-integration subscription and the mh-iot-prod account; hospital device telemetry is bridged through edge gateways at each of the 14 hospitals so proprietary and serial-attached devices reach the cloud without being on the internet.
Device identity, attestation and lifecycle
Every device that publishes telemetry must have a cryptographic, per-device identity — never a shared fleet key. Meridian provisions identity through Azure Device Provisioning Service (DPS) and AWS IoT Core with X.509 certificates, TPM/secure-element backed where the hardware allows. Provisioning is zero-touch: the device presents its birth certificate to DPS/IoT Core, is attested against an enrollment group, and is issued its operational identity and hub assignment. A revoked certificate drops the device instantly and fleet-wide. The lifecycle every device class moves through:
| Stage | Action | Control |
|---|---|---|
| Enroll | Register in DPS group / IoT Core CA | X.509 birth cert; enrollment group scoped |
| Attest | Prove identity + integrity at first connect | TPM/secure element; deny unknown |
| Operate | Publish telemetry to its own topic | Per-device policy (least privilege) |
| Update | Twin/shadow desired-state config | Signed; no arbitrary remote exec |
| Rotate | Certificate rotation before expiry | Automated; short-lived where possible |
| Quarantine | Anomaly → move to holding scope | NAC + IoT policy revoke |
| Decommission | Revoke cert, wipe cloud identity | Audit-logged; asset record updated |
The least-privilege connection policy is where most real breaches are stopped. A device may publish only to its own telemetry topic and subscribe only to its own command topic — it can never read another device’s data or publish on a shared topic. A real AWS IoT Core policy that pins every action to the connecting client’s certificate ID (${iot:Connection.Thing.ThingName} / ${iot:ClientId}):
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": ["iot:Connect"],
"Resource": "arn:aws:iot:us-east-1:*:client/${iot:Connection.Thing.ThingName}",
"Condition": { "Bool": { "iot:Connection.Thing.IsAttached": "true" } } },
{ "Effect": "Allow", "Action": ["iot:Publish"],
"Resource": "arn:aws:iot:us-east-1:*:topic/dt/rpm/${iot:Connection.Thing.ThingName}/telemetry" },
{ "Effect": "Allow", "Action": ["iot:Subscribe"],
"Resource": "arn:aws:iot:us-east-1:*:topicfilter/cmd/rpm/${iot:Connection.Thing.ThingName}/#" },
{ "Effect": "Deny", "Action": ["iot:Publish","iot:Subscribe","iot:Receive"],
"Resource": "arn:aws:iot:us-east-1:*:topic/dt/rpm/*/telemetry",
"Condition": { "StringNotEquals": {
"iot:Connection.Thing.ThingName": "${iot:ClientId}" } } }
]
}
The explicit Deny is the safety net: even if a policy variable is mis-templated, no device can ever publish onto another device’s topic. On the Azure side the equivalent guarantee comes from per-device SAS/X.509 auth plus IoT Hub message routing, which fans telemetry to the right sink based on message properties:
-- IoT Hub route: critical vitals to the alerting Event Hub, everything else to the lake
-- Route 1 (endpoint: eh-clinical-alerts)
$body.alertClass = 'critical' OR temperature > 39.4 OR $body.spo2 < 88
-- Route 2 (endpoint: adls-raw) fallback / true
Edge gateways and secure ingest
Home RPM devices connect over the patient’s own broadband/cellular; hospital devices — especially serial, DICOM or HL7-only equipment — connect through an edge gateway running Azure IoT Edge or AWS IoT Greengrass on ruggedized hardware in the facility. The gateway does four jobs: it terminates the proprietary/legacy protocol and normalizes to FHIR/JSON, it buffers and store-forwards through WAN outages so an OR never loses telemetry, it enforces the segmentation boundary for locked devices, and it runs local ML for latency-critical alerts. Meridian’s gateway modules follow the same deployment pattern documented in Azure IoT Edge: deploying modules with the gateway pattern. The two ingest planes compared:
| Dimension | Azure (IoT Hub + DPS) | AWS (IoT Core) |
|---|---|---|
| Device identity | X.509 / TPM via DPS enrollment groups | X.509 via CA-registered Things |
| Protocols | MQTT, AMQP, HTTPS | MQTT, MQTT-over-WSS, HTTPS |
| Edge runtime | IoT Edge (modules, offline) | Greengrass v2 (components, offline) |
| Routing | Message routing → Event Hubs/Storage | Rules engine → Kinesis/Timestream/S3 |
| Digital twin | Device Twin / DTDL | Device Shadow |
| Hot analytics | Stream Analytics / TSI | Kinesis Data Analytics / Timestream |
| Private ingress | Private Link + Private Endpoint | VPC endpoints + IoT Core private |
Firmware and configuration updates are the highest-risk operation on a medical fleet, so Meridian treats them as governed, signed, staged rollouts, never remote shells. Desired state is expressed through the device twin / shadow: the platform sets a target firmware version and config as desired properties, the device pulls and applies it in a maintenance window, and the twin reconciles reported against desired — there is no command channel that can execute arbitrary code. Updates are cryptographically signed, canaried to a small ring first, and gated entirely for FDA-locked classes where the vendor, not Meridian, owns the update. That turns “patch the fleet” from a fan-out of SSH sessions into an auditable, reversible state change.
The critical control: segmenting unpatchable, FDA-locked devices
This is the part that separates a healthcare landing zone from a generic IoT platform. A hospital’s clinical VLAN can contain thousands of devices running frozen, decade-old, un-patchable operating systems that are legally forbidden to touch. The only viable strategy is deny-by-default microsegmentation enforced by NAC: every device is profiled and fingerprinted at connect by Network Access Control (Cisco ISE / Aruba ClearPass / Forescout), placed on a purpose-built VLAN, and given an allow-list of exactly the flows it needs — its edge gateway, its vendor update server, a time source — and nothing else. East-west traffic between devices is denied, so a compromised infusion pump cannot scan or pivot to the bedside monitor beside it. The device-class → risk → control matrix Meridian’s biomed and network teams govern to:
| Device class | Example | Patchable? | Primary risk | Required control |
|---|---|---|---|---|
| RPM / home | BP cuff, glucometer, SpO2 | Vendor OTA | Impersonation, spoofed vitals | Per-device X.509, DPS attest, TLS |
| Bedside monitor | ECG, pulse-ox, telemetry | Rarely | Data integrity, availability | Isolated VLAN, gateway-only egress |
| Infusion pump | Smart IV pump | FDA-gated | Dosing tamper, lateral spread | Deny-by-default microseg, no east-west |
| Ventilator / life-support | ICU ventilator | No | Availability = patient safety | Dedicated VLAN, allow-list, no internet |
| Imaging modality | CT/MR/US (DICOM) | Slowly | Legacy OS, large PHI egress | DICOM gateway, VNA-only, segmented |
| Lab analyzer | Chemistry/hematology | No | Legacy Windows, HL7 in clear | ASTM/HL7 to LIS via gateway, VLAN |
| Biomed asset (RTLS) | Tagged wheelchair/pump | N/A | Loss, hygiene, utilization | Passive RTLS/BLE tracking, no data plane |
The reason microsegmentation is the primary control here — not endpoint hardening, not patching — is that the endpoint is immovable. You accept that the host is vulnerable and you make its blast radius a single VLAN with allow-listed egress. NAC provides the enforcement and the continuous profiling: if a “GE bedside monitor” MAC suddenly starts speaking SMB or beaconing to the internet, NAC quarantines it to a holding VLAN and raises an alert, and the matching IoT identity is revoked.
Clinical alert routing and biomed tracking
Telemetry forks into two decoupled paths at ingest: a low-latency clinical alerting path and a throughput-oriented analytics path, so a heavy dashboard query can never delay a life-safety alert. Alerting is scored in-stream (Stream Analytics / Kinesis Data Analytics) against clinical thresholds and routed by severity, with de-duplication and escalation so a clinician is paged once, not fifty times. Severity drives the route and the SLA:
| Severity | Example trigger | Route | Target latency |
|---|---|---|---|
| Critical | SpO2 < 88, asystole, pump occlusion | EHR flowsheet + on-call pager + nurse station | < 5 s |
| High | Sustained tachycardia, low battery on life-support | EHR alert + care-team channel | < 30 s |
| Medium | Out-of-range home BP trend | Care-management worklist | Minutes |
| Info | Routine reading, device heartbeat | Lake → trending only | Batch |
Two sentences for the diagram: home and hospital devices publish telemetry that reaches the cloud only through the edge gateway, while unpatchable FDA-locked devices sit in a deny-by-default segment that can talk to the gateway and nothing else. Attested per-device identity is verified at ingest, and abnormal vitals route to clinicians in seconds.
The biomed asset fleet is deliberately kept on a passive data plane — RTLS/BLE tags report location and utilization to a tracking service, but a tracked wheelchair has no telemetry identity and no path into the clinical data plane. Keeping asset tracking physically and logically separate from device telemetry is what prevents “we added Wi-Fi tags to 9,000 assets” from becoming 9,000 new attack surfaces.
Data platform and analytics
Every clinical, imaging, device and business system eventually drains into one governed data platform so Meridian can run population health, quality reporting, revenue-cycle analytics, imaging AI and research — without letting PHI sprawl into ungoverned copies. The platform is a lakehouse: an object-storage data lake organized into zones, a warehouse for served marts, and a governance layer that catalogs and enforces access across both. It lives in the mh-lz-clinical and mh-lz-research subscriptions on Azure (ADLS Gen2 + Synapse/Fabric + Microsoft Purview) and the mh-analytics-prod account on AWS (S3 + Lake Formation + Redshift/Athena), and it extends the HIPAA data-platform blueprint in HIPAA healthcare data platform on Azure with an explicit research-safe zone.
Lake zones: raw, curated, governed, research-safe
Data moves through four zones with strictly tightening access and strictly loosening identifiability going the wrong way is impossible. Raw is the immutable, PHI-bearing system of record you can always re-derive from; curated is conformed and de-duplicated but still fully identified; governed/gold is the served, access-controlled mart layer; and research-safe is the only de-identified zone, and the only one from which data crosses a trust boundary. This mirrors the medallion pattern from ADLS Gen2 medallion: bronze, silver, gold, with a fourth privacy-defined zone bolted on the end.
| Zone | Azure | AWS | Contains | Access | Retention |
|---|---|---|---|---|---|
| Raw / Bronze | ADLS container raw |
s3://mh-lake-raw |
Immutable, full PHI, as-ingested | Platform SPN only | 7y+ (WORM) |
| Curated / Silver | ADLS curated |
s3://mh-lake-curated |
Conformed, PHI, quality-checked | Data engineers (JIT) | Per policy |
| Governed / Gold | Synapse / Delta marts | Redshift / Iceberg | Served marts, row/col ACL | Analysts by role | Per mart |
| Research-safe | ADLS research-safe |
s3://mh-lake-research |
De-identified / pseudonymized | Approved researchers | Per protocol |
Ingestion is batch + CDC through Azure Data Factory / AWS Glue and streaming through Event Hubs / Kinesis, landing HL7 v2, FHIR bulk-export ($export NDJSON), DICOM metadata, and X12 claims. Raw is write-once and CMK-encrypted; the 2.3 PB of imaging lands via the VNA into tiered Blob/S3 with lifecycle to cool/archive, following the imaging-archive lifecycle in Medical imaging PACS/DICOM archive on AWS.
De-identification and pseudonymization
The gate between the governed zone and the research-safe zone is a de-identification pipeline that implements HIPAA §164.514 — either Safe Harbor (remove the 18 identifier categories) or Expert Determination (statistical proof of low re-identification risk). Crucially, de-identification is not deletion: identifiers that research needs re-linked (to return a trial result to a clinician) are pseudonymized — replaced with a token whose linkage key lives in a separate, differently-governed vault. The de-id mapping Meridian’s pipeline applies, field-class by field-class:
| HIPAA identifier | Transform | Method |
|---|---|---|
| Names | Remove | Drop column |
| Geo < state | Generalize | Truncate ZIP to 000 if pop < 20k |
| Dates (birth, admit, DC) | Date-shift | Per-patient random offset, consistent |
| Ages > 89 | Aggregate | Bucket to “90+” |
| MRN, SSN, account # | Pseudonymize | HMAC-SHA256(id, key) → token |
| Phone, fax, email | Remove | Drop |
| Device / serial IDs | Pseudonymize | Tokenize |
| IP, URL | Remove | Drop |
| Biometric, face photo | Remove / defang | Drop or pixel de-id |
| Free-text notes | NLP redaction | De-id NER (Azure/Comprehend Medical) |
Rendered as the pipeline’s declarative mapping (the same spec drives the Spark/Glue job and is itself version-controlled and audited):
# de-id-policy.yaml (research-safe promotion)
method: safe_harbor
salt_secret: "@KeyVault(mh-eus2-research-kv/deid-hmac)" # separate custodian
fields:
patient_name: { action: drop }
mrn: { action: pseudonymize, algo: hmac-sha256 }
ssn: { action: pseudonymize, algo: hmac-sha256 }
birth_date: { action: date_shift, unit: days, jitter: 365, per: patient }
zip: { action: generalize, keep: 3, floor_pop: 20000 }
age: { action: bucket, cap: 90 }
clinical_note: { action: nlp_redact, model: comprehend-medical-phi }
linkage:
keep_token: true # re-identifiable by key custodian only
key_store: mh-research-kms # NOT accessible to researchers
The NLP redaction step matters more than any single structured field: free-text clinical notes are where names, phone numbers and family details hide, and Azure Health Data Services de-identification / Amazon Comprehend Medical PHI detection catch them before the note reaches the research-safe zone.
Governance: Purview, Lake Formation, lineage and fine-grained access
Governance is an overlay, not a stage. Every zone is scanned, classified and access-controlled by Microsoft Purview (Azure) and AWS Lake Formation (AWS), which together answer the two questions an auditor always asks: where did this field come from (lineage) and who can see it (access). Access is row- and column-level and attribute-based — a diabetes researcher sees pseudonymized labs, never names or MRNs — enforced by LF-tags on AWS and Purview data-owner policies on Azure. The capability comparison:
| Capability | Microsoft Purview | AWS Lake Formation |
|---|---|---|
| Catalog + scan | Automated scans, classification rules | Glue Data Catalog + crawlers |
| PHI classification | Built-in + custom classifiers | Custom + Macie for S3 PHI |
| Lineage | Column-level across ADF/Synapse/Fabric | Table-level; column via integrations |
| Fine-grained access | Data-owner policies, RBAC | LF-tags, row/column/cell filters |
| Row/column security | Synapse RLS/CLS + policies | LF cell-level + data filters |
| Cross-account/tenant share | Data Share | LF cross-account grants |
On AWS the enforcement is tag-based access control (LF-TBAC): you tag data with LF-tags, then grant roles access to tag values, so a new PHI table is protected the instant it is cataloged — no per-table grant needed. Real LF-tag definition and a scoped grant:
# Define governance tags, tag the labs table PHI, grant researchers de-identified columns only
aws lakeformation create-lf-tag --tag-key sensitivity --tag-values public internal phi
aws lakeformation create-lf-tag --tag-key zone --tag-values raw curated gold research
aws lakeformation add-lf-tags-to-resource \
--resource '{"Table":{"DatabaseName":"clinical","Name":"labs"}}' \
--lf-tags '[{"TagKey":"sensitivity","TagValues":["phi"]},
{"TagKey":"zone","TagValues":["research"]}]'
# Researchers may read only rows/cols tagged zone=research (de-identified), never sensitivity=phi raw
aws lakeformation grant-permissions \
--principal '{"DataLakePrincipalIdentifier":"arn:aws:iam::…:role/mh-research-analyst"}' \
--permissions SELECT \
--lf-tag-policy '{"ResourceType":"TABLE","Expression":[
{"TagKey":"zone","TagValues":["research"]}]}'
The Azure equivalent registers the lake in Purview and runs a scheduled, scoped scan that classifies PHI and builds lineage. A scan definition (REST/CLI-shaped) that Purview runs against the curated container on a schedule:
{
"kind": "AdlsGen2Msi",
"properties": {
"scanRulesetName": "AdlsGen2-PHI",
"scanRulesetType": "Custom",
"collection": { "referenceName": "mh-clinical", "type": "CollectionReference" },
"credential": { "referenceName": "purview-msi", "credentialType": "ManagedIdentity" },
"resourceTypesFilter": {
"adlsGen2": { "resources": ["/subscriptions/…/mh-eus2-clinical-adls/curated"] } },
"recurrence": { "frequency": "Week", "interval": 1, "startTime": "2026-07-09T02:00:00Z" }
}
}
Finally, AI/ML workspaces get PHI only through this governed plane. Azure Machine Learning and Amazon SageMaker mount data via private endpoints with just-in-time, audited grants; training datasets carry lineage; and models are scanned so no raw identifier leaks into an artifact. The Azure ML workspace is wired exactly as in Azure Machine Learning workspace anatomy, but pointed at the research-safe datastore, never curated PHI. The analytics/AI surface per cloud:
| Workload | Azure | AWS | PHI access |
|---|---|---|---|
| SQL analytics | Synapse / Fabric Warehouse | Redshift / Athena | Gold, row/col ACL |
| Lakehouse / Spark | Databricks / Fabric | EMR / Glue / Athena | Curated (JIT) |
| ML training | Azure ML (PE, no egress) | SageMaker (VPC-only) | Research-safe |
| Clinical NLP | Health Data Services de-id | Comprehend Medical | In de-id pipeline |
| Imaging AI | Azure ML + de-id DICOM | SageMaker + de-id DICOM | Research-safe |
Two sentences for the diagram: sources ingest through batch and streaming into an immutable raw zone, are promoted through curated to governed marts, and only a de-identified research-safe copy crosses the boundary. Purview and Lake Formation catalog, trace lineage and enforce row/column access across every zone as an overlay.
Research and clinical trials
Meridian’s research institute runs observational studies, clinical trials and a genomics program — and it is the domain most likely to become a compliance breach, because it mixes external investigators, novel tools and a strong incentive to move data around. The controlling principle is hard isolation: research runs in dedicated subscriptions and accounts with no network path back to clinical production, works only on de-identified data, and can export nothing without review. The blast radius of a compromised researcher credential must stop at the research boundary and never reach live PHI.
Isolation architecture
Research gets its own mh-lz-research subscription under the mh management-group tree and its own mh-research-prod account under a dedicated Workloads/Research OU — deliberately separate from Workloads/Clinical. There is no VNet peering, no Transit Gateway route and no Private Link from research to clinical prod; the only data movement is the one-way de-identification pipeline that writes into research. Guardrails are enforced by Azure Policy / SCPs at the group/OU level so the isolation cannot be undone by a subscription owner. The isolation controls:
| Control | Azure | AWS |
|---|---|---|
| Account boundary | mh-lz-research subscription |
mh-research-prod account, Research OU |
| No lateral network | No peering; deny-peering Policy | No TGW attachment; SCP deny |
| Deny clinical data pull | Private Link one-way; RBAC | LF grants one-way; SCP |
| Egress control | No public IP; NSG + FW egress-deny | No IGW; VPC endpoints only |
| Independent keys | Research-only Key Vault + HSM | Research-only KMS CMK |
| Policy guardrails | MG-scoped Azure Policy | OU-scoped SCP |
A representative Azure Policy that denies any VNet in the research subscription from peering back into the clinical estate — the single guardrail that keeps isolation from silently eroding:
{
"policyRule": {
"if": {
"allOf": [
{ "field": "type", "equals": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings" },
{ "field": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings/remoteVirtualNetwork.id",
"contains": "mh-lz-clinical" }
] },
"then": { "effect": "deny" }
}
}
Dataset approval, researcher access and export audit
No dataset is visible to a researcher until an IRB-approved protocol and a Data Use Agreement (DUA) are recorded, and access is then time-boxed and scoped to a cohort. The request-to-access workflow is itself the audit evidence — every approval, denial, scope and expiry is logged. The workflow stages:
| Stage | Actor | System of record |
|---|---|---|
| Protocol + IRB approval | PI, IRB | eIRB; protocol ID |
| DUA / data-use terms | Legal, DUA office | Contract + scope |
| Dataset request | Researcher | Data-access portal |
| De-id + cohort build | Data engineering | De-id pipeline → research-safe |
| Access grant (scoped, timed) | Data governance | Purview policy / LF grant |
| Analysis | Researcher | Sealed ML workspace |
| Export review | Data governance | Small-cell + re-id risk check |
| Expiry / revoke | Automated | Grant TTL; access review |
Between the clinical estate and the researcher sits an honest-broker function — a governance role backed by the de-id pipeline, not a person with a spreadsheet — that holds the linkage keys, builds the cohort, and hands the researcher only the pseudonymized extract their protocol authorizes. The researcher never sees the key; the key custodian never runs the analysis; and returning a result to a treating clinician requires a separate, logged re-identification request. That separation of duties is what makes it defensible to give external investigators real analytic power over Meridian’s data.
The analysis environment is a sealed workspace — Azure ML / SageMaker with no outbound internet, private endpoints only, and genomics tooling (GATK, bcftools, cohort VCF stores) running in-place. Data comes to the compute; the researcher cannot pull a cohort down to a laptop or push it to a personal bucket. Every extract that does leave is reviewed against small-cell suppression and re-identification risk, then immutably logged. A representative export-audit query (KQL against the workspace’s diagnostic logs) that surfaces anyone attempting a bulk download:
StorageBlobLogs
| where AccountName == "mhresearchsafe"
| where OperationName in ("GetBlob","ReadFile")
| summarize bytes = sum(ResponseBodySize), ops = count() by CallerIpAddress, Identity=tostring(AuthenticationHash), bin(TimeGenerated, 1h)
| where bytes > 500000000 or ops > 5000 // bulk pull threshold
| order by bytes desc
Genomics and 42 CFR Part 2
Genomic data raises the stakes because a genome is inherently re-identifiable — you cannot truly de-identify it, so it is always handled as pseudonymized-and-consented, in the sealed workspace, under the specific consent that authorized its collection. Separately, behavioral-health and substance-use records fall under 42 CFR Part 2, which is stricter than HIPAA: it requires consent for most disclosures — including for treatment — and prohibits re-disclosure. Meridian tags Part 2 data end-to-end so it gets an extra consent-scoped gate everywhere it flows. The distinction researchers and engineers must internalize:
| Aspect | HIPAA | 42 CFR Part 2 |
|---|---|---|
| Scope | All PHI | SUD/behavioral records from Part 2 programs |
| Use for treatment | Permitted (TPO) | Generally needs patient consent |
| Re-disclosure | Per minimum-necessary | Prohibited without consent; notice required |
| Consent granularity | Broad | Specific, revocable, per-disclosure |
| Segmentation | Access control | Data must be tagged + gated separately |
| Research use | De-id or waiver | De-id or explicit consent; extra scrutiny |
Two sentences for the diagram: clinical PHI crosses into an isolated research subscription/account only through a one-way de-identification pipeline, with re-identification keys held by a separate custodian. Datasets become visible only after IRB/DUA approval, analysis happens in a sealed workspace with no internet egress, and every export is reviewed and audited.
The reason this architecture lets researchers work freely is precisely that it is sealed at the edges: hard account isolation, de-identification before landing, separated linkage keys, approval-gated datasets, and export audit together mean the institute can move fast inside the boundary because nothing crosses it un-reviewed. That is the trade every research-heavy IDN must make — maximal analytic freedom on de-identified data, zero tolerance for an ungoverned copy leaving the perimeter.
Multi-layer security model
A health system cannot treat security as a perimeter it draws around the estate, because the estate has no perimeter left to draw: a nurse charts from a personal phone at a bedside, a radiologist reads studies from home, a payer pulls an X12 remittance across a partner link, and an unpatchable infusion pump on a ward VLAN speaks HL7 to an interface engine three hops away. The model Meridian Health builds is therefore founded on a single uncomfortable assumption — every layer must hold even after the layer outside it has already failed — and it is sharpened by one fact that finance and logistics do not share: the asset being protected is protected health information, and the law does not merely prefer that you guard it, it prescribes what happens when you do not. A breach of unsecured PHI over 500 records triggers HHS notification inside sixty days; 42 CFR Part 2 puts an extra lock on substance-use and behavioral-health records; and a HITRUST CSF assessor will ask you to evidence each control, not describe it. So the seven layers below are engineered so that a gap is a missing metric an auditor can see, not a matter of opinion.
The identity layer is the new perimeter. Every request — clinician, service, device, partner — is authenticated at Microsoft Entra ID with Conditional Access evaluating user, device, location and sign-in risk on each call, and federated into AWS through Entra-as-IdP so one identity plane governs both clouds. The measurable controls are MFA on 100% of identities, phishing-resistant factors (FIDO2/Windows Hello for Business) for all privileged and clinical-writer roles, and zero standing privilege enforced through PIM just-in-time elevation. Because a clinician denied access to a chart in an emergency is itself a patient-safety event, the identity layer carries a control the other domains lack: a break-the-glass path that grants emergency PHI access in seconds, captures a reason, and screams to the SOC in real time — access is never blocked, it is granted fast and logged loud. The device layer refuses to trust the credential alone: Intune compliance and Defender for Endpoint / CrowdStrike posture gate access so only a healthy, managed, attested endpoint — or a Privileged Access Workstation for admin paths — passes, and the biomedical-device fleet that cannot run an agent is handled by the network layer instead of pretended into compliance.
The network layer assumes a foothold already exists and denies lateral movement: hub-and-spoke with default-deny, Azure Firewall Premium and AWS Network Firewall for central IDPS and TLS inspection, and the standing rule that there is no unrestricted east-west traffic — clinical, imaging, research, corporate and integration are separate segments, and an unpatchable pump is NAC-quarantined into a microsegment that can reach exactly one interface engine and nothing else. The workload layer constrains a compromised runtime: Defender for Containers and GuardDuty EKS-protection gate images and behaviour, admission control (Wiz/Gatekeeper) blocks non-compliant pods, and criticals are remediated inside a 7-day critical / 30-day high SLA. The application layer protects each service at its own front door — Application Gateway WAF v2 and AWS WAF in OWASP-prevention mode, plus FHIR/HL7 schema validation and mutual TLS on every partner API, so a malformed FHIR bundle or an oversized DICOM payload is rejected at the edge. The data layer is the last line and makes PHI useless to whoever holds it: encryption everywhere with customer-managed keys in Key Vault Managed HSM and AWS CloudHSM (FIPS 140-3), Private Link on every PHI PaaS service, and an immutable, WORM audit of every PHI access retained seven years. Across all six sits the monitoring layer, owned by the SOC: Microsoft Sentinel correlates signal from every layer and both clouds, Defender XDR and GuardDuty feed it, and CSPM holds a posture score at or above 85 — because an attack that defeats six controls silently is far worse than one that trips an alert.
The control model below is the operational heart of the posture. Every row names what the layer assumes, the concrete Azure and AWS control that answers it, the PHI-specific requirement that makes healthcare stricter than a generic enterprise, and the telemetry the SOC watches — so no layer is aspirational and none is unmonitored.
| Layer | Assume-breach premise | Azure control | AWS control | PHI-specific requirement | Telemetry signal |
|---|---|---|---|---|---|
| 1 · Identity | The network is hostile | Entra ID + Conditional Access, PIM JIT, phishing-resistant MFA | IAM Identity Center federated via Entra (OIDC/SAML), permission sets | Break-the-glass emergency access with reason capture + real-time alert | Risky sign-in, PIM activation, break-glass event |
| 2 · Device | The credential is stolen | Intune compliance + Defender for Endpoint, PAW for admins | Verified-access posture, SSM-managed instances | Unpatchable biomed devices excluded from trust, handled at network | Device-compliance %, non-compliant sign-in blocks |
| 3 · Network | A foothold exists inside | Azure Firewall Premium (IDPS/TLS), NSG/ASG default-deny, Private Link | Network Firewall (Suricata), SG/NACL default-deny, VPC endpoints | Clinical ≠ imaging ≠ research segments; NAC microseg for medical IoT | Firewall IDPS hits, NSG/flow logs, quarantine events |
| 4 · Workload | Malicious code is running | Defender for Containers, admission control, patch SLA 7d/30d | GuardDuty EKS + Malware Protection, image scanning (Inspector) | FDA-connected device images pinned + attested before deploy | Admission denials, runtime findings, unpatched-critical count |
| 5 · Application | Network controls were bypassed | App Gateway WAF v2 (OWASP prevention), mTLS, API Management | AWS WAF on ALB/API GW, mTLS, schema validation | FHIR/HL7/DICOM schema validation; consent + SMART-on-FHIR scopes | WAF blocks, schema-reject rate, mTLS handshake failures |
| 6 · Data | Everything above has fallen | CMK in Key Vault Managed HSM, Private Endpoint, immutable audit | CMK in KMS/CloudHSM, PrivateLink, S3 Object Lock audit | 100% PHI under CMK; 7-yr WORM PHI-access log; field-level for 42 CFR Part 2 | Key access logs, decrypt anomalies, PHI-access audit |
| 7 · Monitoring | Prevention is eventually defeated | Sentinel SIEM/SOAR, Defender XDR, Defender CSPM ≥ 85 | GuardDuty + Security Hub + Detective, Macie for PHI discovery | Immutable SIEM archive; PHI-access analytics; breach-clock detection | MTTD/MTTR, CSPM score, correlated incident count |
The tool estate that realises the monitoring and workload layers spans both clouds and is deliberately not symmetrical service-for-service — each provider’s native stack is used where it is strongest, and Sentinel is the single pane both feed. The mapping below is what a new analyst learns on day one, and what the Defender for Cloud CSPM rollout plugs into.
| Capability | Azure | AWS | What it catches for Meridian |
|---|---|---|---|
| CSPM / posture | Defender for Cloud (CSPM plan) + Secure Score | Security Hub (FSBP + HIPAA) + Config | Public PHI bucket, unencrypted disk, disabled Private Endpoint |
| CWPP / server + container | Defender for Servers/Containers | GuardDuty Runtime + Inspector | Crypto-miner in a clinical pod, vuln in a VNA viewer image |
| Threat detection (cloud) | Defender XDR + Defender for Cloud alerts | GuardDuty (VPC/DNS/S3/EKS/Malware) | Data exfil from a PHI store, C2 beaconing, anomalous S3 read |
| SIEM / SOAR | Microsoft Sentinel (both clouds’ logs) | (feeds Sentinel via connector) | Cross-cloud correlation, break-glass abuse, impossible travel |
| EDR / XDR endpoint | Defender for Endpoint / CrowdStrike | CrowdStrike / SSM | Ransomware precursor on a nurse workstation |
| Data discovery / DLP | Purview + Defender for Storage | Macie + Access Analyzer | Unclassified PHI in a research lake, over-shared dataset |
| Vuln management | Defender vuln assessment (MDVM) | Inspector (EC2/ECR/Lambda) | Log4Shell-class CVE in an integration adapter |
| Investigation graph | Sentinel entity behaviour + hunting | Detective | Lateral-movement path from portal to EHR ODS |
Posture is not a dashboard, it is a set of standards mapped to the frameworks Meridian answers to, so a failing control has a named regulatory consequence rather than a red tile. The HIPAA Security Rule, HITRUST CSF v11, NIST 800-66 and SOC 2 all resolve down to concrete cloud standards that Defender and Security Hub evaluate continuously.
| Posture control | Azure Defender CSPM | AWS standard | Framework driver | Auto-remediation |
|---|---|---|---|---|
| PHI store not publicly reachable | “Storage/SQL public access disabled” | S3/RDS FSBP + HIPAA controls | HIPAA §164.312(e); HITRUST 09.m | Deny policy + Config auto-fix |
| Encryption with CMK enforced | “CMK required” recommendation | KMS-CMK required controls | HIPAA §164.312(a)(2)(iv) | Azure Policy DeployIfNotExists |
| Audit logging immutable + retained | “Diagnostic settings to immutable” | CloudTrail + Object Lock controls | HIPAA §164.312(b); 42 CFR Part 2 | Enable + lock at vend time |
| MFA on privileged identities | Secure Score identity recs | IAM.* FSBP controls | HITRUST 01.b; NIST 800-66 | Conditional Access baseline |
| Network exposed to internet | “Restrict public network access” | EC2/ELB exposure controls | HIPAA §164.312© | NSG/SG guardrail SCP |
| Vulnerable images blocked pre-deploy | Defender for Containers gate | Inspector + ECR scan-on-push | HITRUST 10.k | Admission deny / pipeline fail |
Secrets and keys are the sharpest expression of the data layer, because in healthcare the answer to “who can read this record” must be only the covered entity, and that is achieved by holding the root of trust yourself. Every PHI datastore uses a customer-managed key, and the most sensitive classes — behavioral-health under 42 CFR Part 2, genomics from the research institute — escalate to a dedicated Managed HSM key with a narrower access policy and its own rotation cadence. Application secrets never live in config; they resolve at runtime from Key Vault or Secrets Manager against a managed identity, so a leaked deployment artifact contains a reference, not a credential.
| Secret / key type | Azure | AWS | Rotation | CMK / key ownership |
|---|---|---|---|---|
| PHI database TDE / storage keys | Key Vault Managed HSM (RSA-HSM) | KMS CMK (or CloudHSM-backed) | Annual + on-demand | Customer-managed, HSM-held |
| 42 CFR Part 2 / genomics data keys | Dedicated Managed HSM key, scoped RBAC | CloudHSM cluster key | 6-monthly | Customer-managed, isolated |
| App / service credentials | Key Vault secret + managed identity | Secrets Manager + IAM role | 30–90 day auto | Platform-managed store, CMK-encrypted |
| TLS / mTLS partner certs | Key Vault certificate + ACME | ACM / Secrets Manager | Auto (ACM) / 1-yr | Customer-managed |
| HL7/FHIR integration signing keys | Key Vault key + Managed Identity | KMS asymmetric CMK | Annual | Customer-managed |
| Break-glass account credentials | Key Vault + PIM-gated, sealed | Secrets Manager + isolated account | On use + re-seal | Customer-managed, offline copy |
The edge is the seventh place PHI is protected before a request ever reaches an application, and it does three jobs that overlap but are not the same: absorb volumetric attacks, enforce OWASP and healthcare schema rules, and rate-limit the abusable clinical endpoints (portal login, FHIR search, appointment booking) that a credential-stuffing or scraping campaign targets.
| Threat class | Azure edge control | AWS edge control | Clinical endpoint it protects |
|---|---|---|---|
| Volumetric / L3-4 DDoS | DDoS Protection (Network) + Front Door | Shield Advanced + CloudFront | Public portal, telemedicine ingress |
| OWASP L7 (injection/XSS) | App Gateway WAF v2 (CRS 3.2) | AWS WAF managed rules | FHIR API, portal, scheduling |
| Credential stuffing / bots | WAF bot protection + rate limit | WAF Bot Control + rate rules | Portal login, patient sign-up |
| API abuse / oversize payload | API Management policy + schema | API Gateway + WAF body limits | FHIR bundle POST, DICOMweb STOW |
| Partner spoofing | mTLS + IP allow-list | mTLS + Resource policy | Payer X12, HIE / IHE XCA gateway |
Multi-layer network security
Network security inherits the same assumption-of-breach discipline and expresses it as six independent controls, no one of which is trusted to be sufficient. First, the third-party global edge — CDN, authoritative DNS and WAF — absorbs volumetric and bot traffic and cloaks the regional origins. Second, the regional WAF at the cloud edge (Application Gateway WAF v2 / AWS WAF on the ALB) re-checks every request closer to the workload. Third, central inspection (Azure Firewall Premium / AWS Network Firewall) forces all north-south and inter-spoke traffic through IDPS and TLS inspection. Fourth, micro-segmentation with NSG/ASG and security-group/NACL default-deny contains a compromise to one tier. Fifth, private connectivity (Private Endpoint / PrivateLink) removes PHI data, key and FHIR services from the public internet entirely. Sixth, the host and workload controls hold if every network layer above is crossed. Two rules bind the design: there is no unrestricted east-west traffic anywhere, and all egress is policy-controlled and logged through the central firewalls.
The segmentation map is where healthcare diverges most from a generic enterprise, because the segments are not just tiers — they are data-sensitivity and regulatory boundaries. Clinical PHI, 2.3 PB of imaging, de-identified research data, corporate systems, the integration engine and the medical-device VLANs each get their own segment with its own supernet carved from the RFC1918 plan, and crossing a boundary is always an explicit, inspected, logged event.
| Segment | Azure CIDR (East US 2) | AWS CIDR (us-east-1) | Contains | Crosses boundary via | Data class |
|---|---|---|---|---|---|
| Clinical | 10.20.16.0/22 | 10.40.16.0/22 | EHR, ADT, CPOE, pharmacy, LIS | Firewall + Private Endpoint | Restricted-PHI |
| Imaging | 10.20.20.0/22 | 10.40.20.0/22 | PACS, VNA, DICOM routers, viewers | Firewall + Private Endpoint | Restricted-PHI |
| Integration | 10.20.24.0/22 | 10.40.24.0/22 | Rhapsody/Mirth engine, FHIR, X12 | Firewall + private MLLP | Restricted-PHI |
| Research | 10.20.28.0/22 | 10.40.28.0/22 | Lakehouse, trials, genomics, AI/ML | Firewall + de-ID gateway | Confidential (de-ID) |
| Corp / business | 10.20.32.0/22 | 10.40.32.0/22 | SAP, HR, revenue cycle, contact-center | Firewall | Confidential |
| Medical IoT / RPM | 10.20.36.0/22 | 10.40.36.0/22 | Pumps, monitors, biomed, edge gateways | NAC microseg → one engine | Restricted-PHI |
| Management | 10.20.0.0/24 | 10.40.0.0/24 | Bastion, jump, agents, PE subnets | Bastion-only inbound | Internal |
Azure network security (deep dive)
On Azure the controls compose around the Virtual WAN secured hub. Inbound customer traffic terminates at Application Gateway WAF v2 in the dedicated AppGatewaySubnet, running OWASP CRS 3.2 in Prevention mode with bot protection and per-URI rate limits — an application is never the first thing to see a raw request. Everything beyond it, north-south and east-west, is routed by User-Defined Routes through Azure Firewall Premium, whose IDPS and TLS inspection decrypt, inspect and re-encrypt lateral PHI traffic rather than waving it through; egress is governed by FQDN application rules so a clinical workload reaches only its named destinations. Within and between spokes, NSGs and Application Security Groups enforce default-deny: tiers are expressed as ASGs so rules reference intent (asg-clinical-app) rather than fragile CIDRs, and clinical is provably isolated from imaging and research — exactly the segmentation a HITRUST assessor asks you to evidence.
The clinical spoke’s inbound NSG reads top-to-bottom as an explicit allow-list with a deny backstop; the priorities and ASG references below are the real shape.
| Priority | Name | Dir | Source | Destination | Port | Action |
|---|---|---|---|---|---|---|
| 100 | allow-appgw-web | In | 10.20.0.0/26 (AppGw) | asg-clinical-web | 443 | Allow |
| 110 | allow-web-app | In | asg-clinical-web | asg-clinical-app | 8443 | Allow |
| 120 | allow-app-data | In | asg-clinical-app | asg-clinical-data | 1433 | Allow |
| 130 | allow-hl7-mllp | In | asg-integration | asg-clinical-app | 2575 | Allow |
| 140 | allow-bastion-mgmt | In | AzureBastionSubnet | asg-clinical-* | 22, 3389 | Allow |
| 4000 | deny-vnet-inbound | In | VirtualNetwork | VirtualNetwork | * | Deny |
| 4096 | deny-all-inbound | In | * | * | * | Deny |
The ASGs themselves make the rules readable and portable across every clinical spoke — membership is what a NIC joins, and the NSG never mentions an IP.
| ASG | Membership | Purpose |
|---|---|---|
asg-clinical-web |
EHR/portal web front-ends, AKS ingress NICs | Accepts only from App Gateway subnet |
asg-clinical-app |
EHR app tier, CPOE, pharmacy/eMAR services | East-west from web tier + integration only |
asg-clinical-data |
SQL private-endpoint NICs, HL7 DB hosts | Reachable from app tier only |
asg-integration |
Rhapsody/Mirth interface hosts | MLLP from named ADT/ORU feeds only |
asg-mgmt |
Jumpboxes, monitoring/backup agents | Bastion-only inbound |
Defining an ASG-referencing rule in Terraform keeps the intent legible and is the pattern every spoke module reuses:
resource "azurerm_network_security_rule" "web_to_app" {
name = "allow-web-app"
priority = 110
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_application_security_group_ids = [azurerm_application_security_group.web.id]
destination_application_security_group_ids = [azurerm_application_security_group.app.id]
source_port_range = "*"
destination_port_range = "8443"
resource_group_name = azurerm_resource_group.clinical.name
network_security_group_name = azurerm_network_security_group.clinical.name
}
Central inspection is a Firewall Policy whose rule-collection groups separate threat control from egress control, so a change to an allowed FHIR partner never touches the IDPS baseline. Threat-intel and IDPS run in Deny mode for high/critical signatures; egress is FQDN-scoped per segment.
| Rule collection group | Type | Priority | Action | Example rule |
|---|---|---|---|---|
rcg-threat |
(IDPS / TI) | 100 | Deny | IDPS high/critical + threat-intel Deny, TLS inspect on |
rcg-clinical-egress |
Application | 200 | Allow | asg-clinical-app → *.azurehealthcareapis.com:443, payer FHIR FQDNs |
rcg-integration-net |
Network | 300 | Allow | asg-integration → payer X12 endpoints TCP 5000 over VPN |
rcg-default |
— | 65000 | Deny | implicit deny-all egress (logged) |
PHI PaaS is reached only through Private Endpoints with public network access disabled on the resource itself, so a stolen connection string resolves to a private address inside Meridian’s network and no public route exists. The private DNS zones must be linked to the hub or resolution silently falls back to the public name — a classic failure the Key Vault firewall/RBAC recovery playbook walks through.
| PaaS service | PE subnet | Private DNS zone | Data class |
|---|---|---|---|
| Azure SQL (EHR ODS) | snet-pe-clinical /26 |
privatelink.database.windows.net |
Restricted-PHI |
| Storage (VNA blob) | snet-pe-imaging /26 |
privatelink.blob.core.windows.net |
Restricted-PHI |
| Health Data Services (FHIR) | snet-pe-clinical /26 |
privatelink.fhir.azurehealthcareapis.com |
Restricted-PHI |
| Service Bus (HL7 events) | snet-pe-integration /26 |
privatelink.servicebus.windows.net |
Restricted-PHI |
| Key Vault Managed HSM | snet-pe-shared /26 |
privatelink.vaultcore.azure.net |
Restricted (keys) |
Administrative access uses Azure Bastion in its own subnet, so no RDP or SSH port is ever exposed to a network the workload teams can route to, and every session is brokered and logged. The whole design is validated the way an auditor validates it: NSG flow logs, Firewall logs and VNet flow logs stream to Sentinel, and Traffic Analytics is used to prove there is no clinical-to-research or imaging-to-corp flow that policy did not intend.
AWS network security (deep dive)
AWS realises the identical intent in its own primitives. Customer traffic enters through an Application Load Balancer fronted by AWS WAF in the ingress VPC, running the same OWASP-prevention rule set as the Azure edge so both clouds present an equal bar. All traffic between VPCs and to the internet is steered by the regional Transit Gateway into a dedicated inspection VPC, where AWS Network Firewall runs in appliance mode — the mode that guarantees symmetric, stateful inspection so a long DICOM transfer’s return traffic is examined by the same engine that saw the request instead of dying asymmetrically mid-stream. Inside each VPC, security groups and NACLs enforce default-deny: security groups are stateful tier allow-lists that reference each other rather than CIDRs, and NACLs add a stateless subnet backstop. Clinical, imaging and research are separate VPCs, not subnets, so a blast radius stops at an account boundary.
| SG | Dir | Source / Dest | Port | Purpose |
|---|---|---|---|---|
sg-clinical-web |
In | sg-alb |
8443 | Portal / FHIR ingress from ALB only |
sg-clinical-app |
In | sg-clinical-web |
8080 | App tier from web tier only |
sg-clinical-data |
In | sg-clinical-app |
5432 | Aurora PostgreSQL from app tier only |
sg-integration |
In | sg-clinical-app |
2575 | HL7 v2 MLLP from app tier |
| all workload SGs | Out | (no 0.0.0.0/0) | — | Egress only via TGW → Network Firewall |
Referencing a source security group instead of a CIDR is the control that keeps the allow-list honest as instances churn:
resource "aws_security_group_rule" "app_from_web" {
type = "ingress"
security_group_id = aws_security_group.clinical_app.id
source_security_group_id = aws_security_group.clinical_web.id
from_port = 8080
to_port = 8080
protocol = "tcp"
description = "app tier accepts only web tier"
}
The NACL is the stateless subnet backstop — deliberately coarse, denying by default, allowing only intra-estate and named on-prem HL7 sources:
| Rule | Dir | CIDR | Port | Action |
|---|---|---|---|---|
| 100 | In | 10.40.0.0/12 | 8443 | Allow (intra-AWS estate) |
| 110 | In | 10.0.0.0/12 | 2575 | Allow (on-prem HL7 feeds) |
| 32767 | In | 0.0.0.0/0 | * | Deny |
| 100 | Out | 10.0.0.0/8 | * | Allow (to hub / inspection) |
| 110 | Out | 0.0.0.0/0 | 443 | Allow (post-inspection egress) |
Network Firewall runs a STRICT_ORDER stateful policy — the ordering that stops a Suricata pass from beating a drop and letting a denied domain leak — with HOME_NET pinned to the clinical supernet and SNI/TLS allow-rules for the FHIR and payer partners.
| Type | Rule | Action |
|---|---|---|
| Stateless | Fragmented / malformed packets | drop |
| Stateful (managed) | AWS threat-intel + abused-domains group | drop |
| Stateful (custom) | TLS SNI ∈ {payer FHIR, HIE XCA} FQDNs | pass |
| Stateful (default) | HOME_NET → any :443 not allow-listed |
drop_established |
Detection is continuous and org-wide: GuardDuty (VPC flow, DNS, S3, EKS, Malware and RDS-login protection) and Inspector feed Security Hub with the HIPAA and AWS FSBP standards, and findings route to the delegated-admin Security account, then on to Sentinel. GuardDuty findings are wired to graded automated responses so a high-severity PHI event does not wait for a human to notice.
| GuardDuty finding | Severity | Automated response |
|---|---|---|
UnauthorizedAccess:EC2/SSHBruteForce |
High | SSM automation swaps to isolation SG, snapshots for forensics |
Exfiltration:S3/ObjectRead.Unusual (PHI bucket) |
High | S3 public-block + notify Privacy Officer (breach-clock assessment) |
Backdoor:EC2/C&CActivity!DNS |
High | Quarantine ENI, page SOC, capture memory |
CredentialAccess:IAMUser/AnomalousBehavior |
Medium | Revoke session, force re-auth, review CloudTrail |
Policy:S3/BucketPublicAccessGranted |
High | Config rule auto-remediates public-access-block |
Administrative access uses AWS Systems Manager Session Manager rather than bastion hosts or open SSH — no inbound management ports, every session brokered, recorded and shipped to the immutable Log Archive. Enabling GuardDuty for the whole organisation from the delegated admin is a one-time control that auto-enrolls every new clinical account at vend time:
aws guardduty update-organization-configuration \
--detector-id "$DETECTOR" --auto-enable-organization-members ALL \
--features '[{"Name":"S3_DATA_EVENTS","Status":"ENABLED"},
{"Name":"EKS_RUNTIME_MONITORING","Status":"ENABLED"},
{"Name":"RDS_LOGIN_EVENTS","Status":"ENABLED"}]'
As on Azure, the two binding rules hold: no unrestricted east-west traffic between accounts or tiers, and all egress policy-controlled and logged through the central Network Firewall.
Zero-downtime release patterns
The mandate is unambiguous: clinical services carry no maintenance windows. There is no 2 a.m. Sunday when an emergency department stops admitting, no hour when an ICU monitor may go dark for a deploy, no window when a patient cannot refill a prescription through the portal. Zero-downtime is therefore a property the estate is engineered to preserve, not a deployment convenience, and it rests on two load-bearing ideas. The first is that deployment is not release: new code reaches production long before any patient traffic exercises it, decoupled by feature flags in Azure App Configuration / AWS AppConfig, so a new CPOE order set ships dark and is activated by a runtime flag flip for one clinic before it widens. The second is that every release is reversible: no change reaches a clinical tier unless it can be withdrawn in seconds — a flag off, traffic shifted back, a canary aborted — so reversibility is an entry condition, not an improvised contingency.
These principles are realised through a constrained set of patterns, each matched to a workload class and a clinical risk profile. The decisive healthcare nuance is bake time: a portal regression is a UX blip and can ramp in two hours, but a regression in the EHR write path or medication ordering is a patient-safety event and ramps over a day with the longest observation window and the strictest gate.
| Pattern | Mechanism | Azure implementation | AWS implementation | Best-fit clinical service | Rollback trigger |
|---|---|---|---|---|---|
| Blue-green | Parallel env, cutover | App Service deployment slots / AKS + AGIC | CodeDeploy blue-green, ALB target groups | Patient portal, FHIR API, VNA viewer | Swap back / shift target group |
| Canary | Weighted ramp + gates | Traffic Manager weights, Argo Rollouts + Flagger | CodeDeploy canary, ALB weighted routing | Telemedicine, scheduling | KPI breach → auto-abort |
| Rolling | Incremental replace | AKS/VMSS rolling, maxUnavailable |
EKS/ECS rolling update | Integration-engine workers | Probe fail → halt + revert |
| Feature flags | Runtime toggle | Azure App Configuration | AppConfig / LaunchDarkly | New CPOE order set, portal feature | Flag off |
| Expand-contract | Parallel schema | pgroll / EF Core additive migrations | Liquibase + Aurora | EHR ODS, ADT store | Stop dual-write, drop new |
| Health traffic shift | Probe-gated weight | Front Door / AGW health probe | ALB health + Route 53 | Every Tier-1 service | Unhealthy probe → drain |
Blue-green governs the patient-facing services: a fully provisioned green slot runs the new version alongside live blue, smoke-tested in isolation against a synthetic ADT feed, a FHIR read/write round-trip and a DICOM C-STORE before any patient traffic, then cut over by a slot swap that carries no cold start because the slot is warmed first. The App Service swap-with-preview is the concrete mechanism, and blue is kept warm as an instant rollback target:
az webapp deployment slot swap -g mh-lz-clinical-prod \
-n mh-eus2-portal-prod --slot green --target-slot production --action preview
az webapp deployment slot swap -g mh-lz-clinical-prod \
-n mh-eus2-portal-prod --slot green --target-slot production --action swap
Canary and progressive delivery apply where traffic can be split by weight — telemedicine and scheduling ramp 5→25→50→100 with a bake per step, each gated on the KPIs that actually matter clinically. The gate is automatic and refuses to promote unless App Insights holds p95 latency, error rate, HL7 v2 ACK rate and FHIR API success within budget for the whole bake window; a drop in ACK rate means downstream systems are rejecting messages and the release holds. A guardrail breach shifts weight back with no human in the critical path. The per-service strategy below is the operational contract each clinical team signs.
| Service | Tier | Pattern | Traffic shift | Gate KPI | Rollback | Window |
|---|---|---|---|---|---|---|
| EHR write / CPOE / eMAR | T1 | Blue-green + dark + long bake | 5→25→50→100 over 24h | HL7 ACK, order-reject, p95 | Swap to blue | None (dark) |
| Patient portal | T1 | Canary | 5→50→100 over 2h | Login success, FHIR 2xx | Shift to blue | None |
| Telemedicine core | T1 | Canary + session drain | 10→50→100 | Video-join %, WebRTC ICE | Drain + shift | None |
| Integration engine | T1/T2 | Rolling + replay | Node-by-node | Queue depth, ACK, replay lag | Halt node, replay | None |
| Imaging / VNA viewer | T1 | Blue-green | 10→100 | Study-open latency, C-STORE | Swap | None |
| Analytics / BI | T2 | Rolling | n/a | Job success rate | Redeploy prev | Off-hours OK |
Underpinning every stateful service is the discipline that most often defeats zero-downtime ambition: schema change. Meridian never takes a lock-acquiring migration against a live EHR or ADT store. Instead it applies expand/contract (parallel-change): expand the schema additively, dual-write old and new shapes while backfilling history, run both in parallel until the new path is proven, then contract by removing the old structure once nothing reads it. Each step is an additive, backwards-compatible, sub-50ms metadata operation, so application code and schema deploy independently and each tolerates the other’s previous version.
| Phase | Action | Application behaviour | Reversible? |
|---|---|---|---|
| Expand | Add nullable column / table / index additively | Reads and writes old shape | Yes — drop the new object |
| Dual-write | Write both old and new shapes | Both populated, old authoritative | Yes — stop writing new |
| Backfill | Batch historical rows into new shape (idempotent) | Old still authoritative | Yes — rerun or discard |
| Migrate reads | Flip read path to new shape behind a flag | New authoritative | Yes — flag back to old |
| Contract | Drop old column after a bake period | New shape only | No — final, one-way |
The common thread is traffic shifting plus health-based rollback for every major release: whether the unit of change is a blue-green slot, a canary weight or an active-active region, the release advances by moving a controlled proportion of traffic and retreats automatically the moment a clinical health signal degrades — and because deployment is separated from release by flags, even an already-deployed change is neutralised in seconds without redeploying anything.
Active-active multi-region data topology
Zero-downtime release depends on a data topology that contains failure as readily as it contains change, and healthcare forces a harder problem than most because the datastores have irreconcilable consistency needs. The EHR system of record demands that a committed clinical order is durable and correct — a silently dropped write is a missed medication; portal and telemedicine engagement state can tolerate eventual convergence for the sake of low-latency multi-region writes; and 2.3 PB of imaging is write-once and simply needs to arrive. So the topology splits by consistency need rather than treating storage as one homogeneous thing, and the organising rule for the record of any clinical action is region-pin the writer: each patient’s EHR writes go to their home region, so two regions never edit the same record at once and the cleanest possible conflict-avoidance holds for the data that matters most. Meridian runs this across the Azure East US 2 / Central US pair and the AWS us-east-1 / us-west-2 pair, mirroring the patterns in Azure multi-region active-active design and AWS multi-region active-active.
Each datastore is matched to its need, and the table below is the load-bearing reference: what replicates, how, in which topology, at what RPO, and — the question people forget until an incident — how a conflict is resolved.
| Datastore | Data domain | Replication mechanism | Topology | RPO | Conflict handling | Consistency |
|---|---|---|---|---|---|---|
| Azure SQL / Aurora Global | EHR ODS, ADT, results | Auto-failover group / Aurora Global DB | Active-passive (RW region-pinned) | ≤5m (0 planned switchover) | Single-writer, region-pin | Strong in-region |
| Cosmos DB (multi-write) | Portal prefs, scheduling holds, care-plan cache | Multi-region write | Active-active | ~0 | LWW on _ts or custom stored-proc merge |
Session / bounded-staleness |
| DynamoDB global tables | Telemedicine session, notifications, feature state | Stream-based replication | Active-active | Sub-second | LWW per item | Eventual |
| Blob GRS / S3 CRR | VNA imaging (2.3 PB DICOM) | Async object replication | Active-passive | Minutes (RA-GRS) | Immutable objects — no conflict | Eventual + reconciliation |
| Service Bus / SQS geo-DR | HL7 / FHIR event mesh | Paired namespace alias / cross-region | Active-passive | Near-0 | Idempotent replay by message id | At-least-once |
| Key Vault MHSM / KMS MRK | Encryption keys | HSM replication / multi-region keys | Active-active | 0 | n/a (same key material) | Strong |
| Entra ID / AD DS | Identity | Native multi-master / global | Active-active | 0 | Multi-master convergence | Eventual → strong |
The system-of-record path deserves its own detail because its failover is the one clinicians feel. Azure SQL auto-failover groups and Aurora Global Database replicate the EHR/ADT store to the secondary region; a planned switchover is RPO 0, an unplanned failover loses at most the replication lag, which is why the Tier-1 target is RPO ≤5m and why lag is alarmed at RPO/2. Creating the failover group is a single declarative step, and the read-write listener is what the application connects to so a failover is transparent to connection strings:
az sql failover-group create -n mh-ehr-fog -g mh-lz-clinical-prod \
--server mh-eus2-ehr-sql --partner-server mh-cus-ehr-sql \
--failover-policy Automatic --grace-period 1 \
--add-db mh-ehr-ods mh-ehr-adt
# app connects to mh-ehr-fog.database.windows.net (RW listener) — never the raw server
The engagement stores are genuinely active-active, and the discipline there is knowing exactly where last-writer-wins is safe and where it is lethal. For Cosmos multi-region write, LWW on _ts is fine for a portal preference or a scheduling hold, but anything order-like uses a stored-procedure custom-merge policy and the conflicts feed is monitored for silent drops. DynamoDB global tables are LWW per item — perfect for a telemedicine session token or a notification counter, and categorically never the record of a clinical action. The per-domain routing contract makes the boundary explicit:
| Domain | Store | Region model | Write routing | Conflict rule | RPO |
|---|---|---|---|---|---|
| EHR ODS / clinical documentation | Azure SQL failover group | A-active / B-standby | Region-pin by patient home org | None (single writer) | ≤5m |
| ADT / interoperability | Aurora Global | A-writer / B-reader | Pinned to primary | None | ≤5m |
| Patient portal state | Cosmos multi-write | Active-active | Nearest region | LWW / custom merge | ~0 |
| Telemedicine session | DynamoDB global | Active-active | Nearest region | LWW per item | Sub-second |
| Imaging / VNA | Blob GRS / S3 CRR | Active-passive | Primary ingest region | Immutable, reconciled | Minutes |
| Integration events | Service Bus geo-DR | Active-passive | Primary namespace | Idempotent replay | ~0 |
| Research lake | ADLS Gen2 / S3 governed | Active-passive | Primary | Object-versioned | ≤1h |
Imaging is the outlier and the largest: the VNA replicates by Blob GRS / S3 CRR, which copies new DICOM objects only, asynchronously, and never replicates a lifecycle transition or a delete marker by default. So a reconciliation job compares per-modality study counts between regions each day, surfacing a missed object before a radiologist reaches for a study that is not there. Across every store, the governing rule is the same one covered in depth in multi-region data replication strategies: replication lag is your live RPO, and if lag breaches the tier objective, failing over means losing committed clinical data — a trade to be decided before the incident, not during it.
Disaster recovery and resiliency
Resiliency is engineered, not assumed, and the engineering begins by classifying every service into a recovery tier and designing each tier to a stated objective drawn straight from the operating model. A flat DR posture either over-invests in a sandbox that can tolerate a day’s recovery or, far worse, under-protects the emergency department. Each strategic region is paired with a DR region in the same geography and cloud — East US 2 ↔ Central US, us-east-1 ↔ us-west-2, West Europe holding EU-in-EU residency — so a regional loss has a pre-built, in-jurisdiction destination that respects HIPAA and GDPR data-residency boundaries. The tier definitions are the contract, and healthcare’s numbers are tighter at the top than most industries because a 30-minute EHR outage is measured in delayed diagnoses.
| Tier | Scope / examples | RTO | RPO | DR strategy | Cost posture |
|---|---|---|---|---|---|
| T0 | Identity, DNS, network + security control plane, privileged access | ≤15 min | ≈0 | Active-active across regions | High (always-on) |
| T1 | EHR, ADT, results, imaging-core, medication/CPOE, emergency, patient-portal, telemedicine-core | ≤30 min | ≤5 min | Warm standby + active-active edge | High |
| T2 | Business apps, analytics, revenue cycle, support | ≤4 h | ≤1 h | Pilot-light | Medium |
| T3 | Dev, sandbox, non-critical reporting | ≤24 h | ≤24 h | Backup-restore | Low |
The recovery strategy is differentiated by tier rather than applied uniformly, because the cheapest posture that meets the objective is the right one — the RTO/RPO fundamentals behind these choices are laid out in BC/DR RTO/RPO fundamentals. Tier-0 and the Tier-1 edge run active-active and recover by traffic shift; the Tier-1 clinical core runs warm standby with synchronous SoR replication so promotion is a fast, controlled switchover; Tier-2 runs pilot-light with data replicated and compute scaled on demand; Tier-3 restores from immutable backup.
| Strategy | How it recovers | Azure | AWS | Tiers |
|---|---|---|---|---|
| Active-active | Traffic shift, no restore | Front Door + Traffic Manager, multi-write stores | Route 53 + global tables | T0, T1 edge |
| Warm standby | Promote a running replica | SQL failover group + ASR-replicated compute | Aurora Global + pre-scaled ECS/EKS | T1 core |
| Pilot-light | Scale minimal footprint on failover | ASR minimal + replicated data | AMI + replicated RDS, scale-out | T2 |
| Backup-restore | Restore from vault | Azure Backup + LTR | AWS Backup | T3 |
Backups are where healthcare’s retention and immutability requirements bite hardest — PHI must be retained for years, and the backup itself must be immutable so ransomware cannot encrypt or delete it and use it as a re-entry path. Meridian pairs replication (for RPO) with immutable, vault-locked backups (for integrity and ransomware recovery), following the pattern in AWS Backup DR strategies.
| Workload | Azure | AWS | Retention | Immutability |
|---|---|---|---|---|
| EHR / ADT database | LTR backup + failover group | Aurora backtrack + cross-region snapshot | 7-yr PHI | Immutable vault |
| VNA imaging (2.3 PB) | Blob GRS + immutability policy | S3 CRR + Object Lock (compliance mode) | Per retention schedule (yrs) | WORM Object Lock |
| VMs / AKS / EKS | Azure Backup + ASR | AWS Backup + EBS snapshot | 30–90d + LTR | Vault-lock immutable |
| Integration config | GitOps repo + Backup | AWS Backup + git | Indefinite | Branch protection + lock |
| Identity | AD DS backup, isolated recovery forest | (Entra native) | 30d | Air-gapped recovery forest |
A recovery objective is only as credible as the last test that proved it, so DR testing is a standing obligation, not an annual ceremony. Meridian runs a layered cadence — automated fault injection continuously, per-tier failover quarterly, a full-region game-day annually — and adds a healthcare-specific drill the others lack: the EHR downtime exercise, where clinicians practise charting in read-only downtime mode so a real failover does not find them improvising on paper.
| Test | Scope | Frequency | Tool | Pass criteria |
|---|---|---|---|---|
| Chaos / fault injection | Dependency, AZ, instance loss | Monthly | Azure Chaos Studio / AWS FIS | Graceful degrade, no cascade |
| Per-tier DR failover | One tier to DR region | Quarterly | ASR / AWS Backup + runbook | Meets tier RTO/RPO |
| EHR downtime drill | Read-only clinical mode | Quarterly | Clinical + IT joint | Clinicians chart offline, reconcile |
| Backup restore test | Random workloads | Monthly | Backup restore | Integrity + boot pass |
| Full-region game-day | Lose an entire region | Annual | FIS + ASR orchestration | Tier-0/1 within target |
| Ransomware recovery | Immutable restore, clean rebuild | Semi-annual | Vault-lock restore + IaC | Clean-state attestation |
Disaster recovery runbooks
A recovery target nobody has rehearsed is a guess dressed as a commitment. RTO/RPO numbers are meaningful only when a named owner has executed the procedure that meets them against a realistic scenario, recently enough that the runbook reflects the current estate. Meridian’s orchestration builds on the pattern in DR orchestration with Site Recovery and ServiceNow: each runbook names its trigger, its steps, its single accountable owner, the tier target it must meet, and — the step most often omitted — how recovery is validated before the incident is declared closed. Recovery always proceeds in tier order, because nothing else can come back until identity, DNS and the network control plane are up.
The Tier-0 runbook runs first and fastest; its failure blocks every runbook below it, which is why identity and connectivity are active-active with RPO≈0. The AD forest recovery runbook covers the deepest identity-loss case behind this.
| Scenario | Trigger | Procedure steps | Owner | Target | Validation |
|---|---|---|---|---|---|
| Identity / access-plane failover | Entra / AD DS or CA plane degraded in primary | Confirm break-glass path; fail CA + PIM plane to secondary region; promote private DNS resolver; verify Entra-to-AWS federation intact | Identity & Security | T0 · 15m / ≈0 | Clinician + admin auth succeeds in DR; CA/PIM enforced; break-glass re-sealed |
| Network / security control failover | Loss of hub, firewall, or an ExpressRoute / Direct Connect on-ramp | Verify BGP withdrew the failed path; confirm second circuit carrying load; shift vWAN / TGW hub; confirm firewall policy active in secondary | Network & Connectivity | T0 · 15m / ≈0 | End-to-end reachability on surviving circuits; IDPS active; no SPOF remaining |
The Tier-1 runbooks are the ones that touch patient care directly, and each clinical domain fails over differently — the EHR promotes a database, imaging re-points a DICOM router, the integration engine replays a durable queue, and the digital front door shifts edge weight. The unifying validation is no lost clinical action and no duplicate: idempotency keys and message-id reconciliation prove that a promoted region neither dropped an order nor double-posted one.
| Scenario | Trigger | Procedure steps | Owner | Target | Validation |
|---|---|---|---|---|---|
| EHR / ADT core failover | Loss of the region hosting the EHR system of record | Invoke EHR read-only downtime mode so clinicians keep charting; confirm SQL failover-group / Aurora Global synced (RPO ≤5m); az sql failover-group set-primary to secondary; re-point app config to RW listener; reconcile in-flight ADT by message id |
Clinical Platform | T1 · 30m / 5m | Charting resumes; zero lost orders; ADT stream reconciled; no duplicate postings |
| Imaging / VNA failover | Loss of the imaging region | Fail VNA to RA-GRS / S3 CRR replica; re-point DICOM router, modality worklist and zero-footprint viewer to DR endpoint; run per-modality study-count parity reconciliation | Imaging / PACS | T1 · 30m / mins | Studies open; C-STORE / C-FIND succeed; parity report clean; no missing priors |
| Integration-engine failover | Loss of the region hosting the interface engine | Activate paired Service Bus namespace (alias flip); restart Rhapsody / Mirth in DR; replay from durable queues by message id; confirm HL7 v2 ACKs and FHIR subscriptions resume | Integration | T1 · 30m / ~0 | ACKs flowing; zero duplicate messages (idempotency); all feeds green; X12 partners reconnected |
| Portal + telemedicine failover | Loss of a region for patient-facing channels | Shift Front Door / Route 53 weight to surviving region; scale hot capacity; drain active video sessions gracefully; reconcile scheduling holds and secure messages by idempotent replay | Digital Front Door | T1 · 30m / ~0 | Portal login + video join succeed; p95 within tolerance; no double-booked slots |
Failing over is only half the discipline; failing back is where estates that never rehearse it corrupt data by reversing replication carelessly. Failback is a deliberate, low-census-window operation gated on the primary being healthy and attested clean — never re-entering a compromised environment.
| Step | Action | Guard / gate |
|---|---|---|
| 1 | Confirm primary region healthy, patched, and attested clean | No reintroduced compromise (ransomware case) |
| 2 | Reverse-replicate the delta accumulated in DR back to primary | Lag < tier RPO before proceeding |
| 3 | Switch writers back during a low-census window | Clinical safety officer sign-off |
| 4 | Validate reconciliation across orders, results, studies | Order / result / study parity reports clean |
| 5 | Re-seal break-glass, restore normal routing, close incident | Full audit trail complete; lessons logged |
Finally, a runbook is executed by people under pressure, so the roles and communications are pre-assigned — a health-system DR incident has clinical, privacy and safety dimensions no other industry carries, and the breach-clock and patient-safety roles are as load-bearing as the platform ones.
| Role | Responsibility | Escalates to |
|---|---|---|
| Incident Commander | Declares DR, owns the go/no-go decision, runs the bridge | CISO / CMIO |
| Clinical Safety Officer | Activates downtime procedures, protects patient safety | Chief Medical Officer |
| Privacy Officer | Runs HIPAA breach assessment (60-day clock), Part 2 review | Legal / Compliance |
| Platform / Network / Identity leads | Execute the per-domain runbooks in tier order | Incident Commander |
| Communications lead | Staff notifications, patient-facing status, regulator liaison | Executive sponsor |
Application onboarding for 180+ applications
A landing zone that only the platform team can drive is a bottleneck, not a platform. Meridian Health runs 180+ applications across clinical, imaging, telemedicine, research and corporate estates, and every one of them will at some point need a subscription or account, a network segment, private endpoints, keys, policy, a pipeline and a go-live sign-off. Do that by hand, per app, and you get two failure modes at once: the platform team becomes a ticket queue that takes weeks per app, and — far worse in a HIPAA shop — every hand-built environment drifts, so no two PHI workloads are configured the same way and the auditor finds a different gap in each. The answer is a paved road: a self-service, opinionated onboarding path that makes the compliant configuration the default and the easy one, so app teams move fast precisely because they are not allowed to deviate on the controls that matter.
The paved road is a pipeline of its own. An owner requests through the service catalogue; the request is classified; a landing pattern is selected from a fixed menu; Terraform vends the environment with guardrails already inherited; and a go-live gate verifies the evidence before production traffic is allowed. Nothing about the app’s code is decided here — only the shape of the ground it lands on.
| Stage | Input | What happens | Output | Owner |
|---|---|---|---|---|
| 1. Intake | Owner request in ServiceNow / Jira | Structured form captures tier, data class, region, integrations | A validated onboarding record | App owner + platform |
| 2. Classify | Intake record | Assign Tier-0…3 (RTO/RPO) and data class (PHI, 42 CFR Part 2, PII, public) | Tier + class tags on the workload | Platform + security |
| 3. Landing pattern | Tier + class + archetype | Pick a pattern from the fixed catalogue (see archetype table) | A module selection + parameters | Platform architect |
| 4. Provision | Pattern + parameters | Terraform vends subscription/account, spoke/VPC, PE subnet, keys | A baselined environment | Automation (pipeline) |
| 5. Guardrails | Vended environment | Policy/SCP, CMK, private endpoints, audit inherited from the mgmt group/OU | A compliant-by-default landing zone | Automation (inherited) |
| 6. Go-live | Deployed workload | Verify guardrail compliance, DR evidence, BAA/DPIA, runbook | Production release | Change board + owner |
Intake is where you buy back the audit. The form is short but every field drives an automated decision downstream, so an owner cannot accidentally under-scope a PHI system. The fields below are the minimum Meridian collects; each maps to a Terraform variable or a policy assignment, never to a human judgement call made later.
| Intake field | Example value | What it drives |
|---|---|---|
| Workload name | clinical-web |
Resource naming mh-<region>-<workload>-<env>-<type> |
| Business tier | Tier-1 | RTO ≤30 m / RPO ≤5 m defaults: zone-redundancy, replica region, backup policy |
| Data class | PHI + 42 CFR Part 2 | Private endpoints, CMK, immutable audit, break-glass logging (mandatory) |
| Data residency | US-in-US | Allowed-regions policy: eastus2 + centralus only, blocks EU/global |
| Archetype | EHR-integrated web app | Landing-pattern selection from the catalogue |
| Integrations | HL7 v2 ADT, FHIR R4 | Integration-spoke peering + interface-engine onboarding |
| Cloud | Azure | Management-group placement mh-lz-clinical |
| Environments | dev, UAT, staging, prod | One subscription/account per environment |
Tier and data class are separate axes and both are hard gates. Tier drives resilience and cost; data class drives security and privacy. A Tier-3 research sandbox that nonetheless holds identifiable PHI still gets private endpoints and CMK even though it does not get active/active. Conflating the two is the classic mistake — teams give a “low-tier” app weak security because it is “not important,” and it turns out to be reading the EHR.
| Tier | RTO / RPO | Example Meridian apps | Landing defaults the tier stamps |
|---|---|---|---|
| Tier-0 | ≤15 m / ≈0 | Entra, DNS, network control, PAM, core security | Multi-region active/active, platform subs only, no app-team write |
| Tier-1 | ≤30 m / ≤5 m | EHR-adjacent, ADT/results, imaging-core, medication, patient portal, telemedicine-core | Two-region active/active in-country, zone-redundant, sync replica, 5-min RPO backup |
| Tier-2 | ≤4 h / ≤1 h | Business analytics, scheduling support, back-office | Single region + zone-redundant, async DR, hourly backup |
| Tier-3 | ≤24 h / ≤24 h | Dev/sandbox, non-critical reporting | Single zone, daily backup, no DR commitment |
| Data class | Trigger | Non-negotiable controls |
|---|---|---|
| PHI / ePHI | Any protected health information | Private Link/PrivateLink only, CMK (HSM), immutable PHI-access audit, no public IP, BAA on file |
| 42 CFR Part 2 | Behavioral-health / SUD records | All PHI controls plus stricter consent segmentation and disclosure logging |
| Genomic / research | Identifiable research data | PHI controls plus de-identification pipeline + dataset-approval + export audit |
| PII (non-PHI) | Staff/corporate personal data | Encryption at rest/in transit, RBAC, GDPR handling for EU subjects |
| Public | Marketing, public docs | Standard baseline; still no secrets, still scanned |
The archetype-to-landing-pattern catalogue
The heart of the paved road is a closed menu. An owner does not design a network; they pick an archetype, and the archetype deterministically selects a landing pattern, a cloud/region and a control set. Ten patterns cover the entire Meridian estate; anything that does not fit is an architecture-review exception, not a self-service request.
| Application archetype | Example at Meridian | Landing pattern | Cloud / region | Key controls |
|---|---|---|---|---|
| EHR-integrated clinical web | Care-team portal reading Epic | Tier-1 spoke + App Gateway/WAF + PE to SQL/Storage | Azure EUS2 + CUS (A/A) | PHI, CMK, break-glass audit |
| HL7/FHIR integration service | ADT/ORU router to downstreams | Integration spoke + interface engine + event mesh | Azure mh-lz-integration |
Message trace, replay DLQ |
| Imaging / PACS / VNA | Vendor-neutral archive | Imaging spoke + hot/cool/archive Blob/S3 lifecycle | Azure imaging + AWS us-east-1 | DICOM TLS, 2.3 PB tiering |
| Telemedicine real-time | Virtual-visit video + intake | Tier-1 spoke + media edge + low-latency front door | Azure telemed A/A | Encrypted video, UX monitoring |
| Patient portal / digital front door | Public scheduling + messaging | Internet-facing spoke + WAF + PE to backend | Azure + Front Door | PHI, DDoS, bot protection |
| Research / clinical-trials analytics | Trial cohort lakehouse | Research spoke + de-id zone + isolated workspace | AWS Research OU | Trial isolation, export audit |
| Batch / revenue-cycle | 837/835 claims processing | Tier-2 spoke + queue + scheduled compute | Azure corp / AWS Workloads | X12 handling, PHI-in-transit |
| Legacy IaaS / COTS | Vendor appliance, no PaaS | Lift-and-shift spoke + Ansible-configured VMs | Azure/AWS per licence | Microseg, patch pipeline |
| Medical-device / IoT ingest | RPM telemetry, biomed | Edge gateway + IoT ingest + segmented device VLAN | Azure IoT / AWS IoT | Device identity, NAC, FDA seg |
| Internal corp SaaS-style | HR self-service app | Tier-2 corp spoke + Entra SSO | Azure mh-lz-corp |
PII, GDPR for EU staff |
The Azure onboarding pattern. For an Azure workload the pipeline resolves the archetype to a single module call against the private registry. The tier and data class are inputs, not code the app team writes — the module expands them into zone-redundancy, replica regions, a private-endpoint subnet carved from the 10.20.0.0/12 supernet, a CMK, and management-group placement so guardrails are inherited the instant the subscription is vended.
module "clinical_web_lz" {
source = "app.terraform.io/meridian-health/landing-zone/azurerm"
version = "~> 4.2" # pinned; module fixes ship as new tags
workload = "clinical-web"
environment = "prod"
tier = "tier-1" # → zone-redundant, A/A EUS2+CUS, 5-min RPO
data_class = "phi" # → private endpoints, CMK, immutable audit
location = "eastus2"
dr_location = "centralus"
address_space = ["10.20.16.0/22"] # /22 spoke from the Azure supernet
pe_subnet = "10.20.16.0/26" # /26 private-endpoint subnet, PHI PaaS only
management_group = "mh-lz-clinical" # inherits deny-public-IP, require-CMK, region-lock
connect_to_vwan = true # peers into the regional Virtual WAN hub
}
The intake record is the pipeline’s parameter source; the release pipeline runs plan → gates → apply and finishes at a go-live gate that will not open until guardrail compliance, DR-test evidence and the BAA/DPIA reference are attached. The whole path is idempotent — re-running never double-provisions — so an owner can safely re-request to add an environment.
The intake ticket feeds a tiering and data-class decision, a Terraform run vends the spoke with its private endpoint and inherited policy, and only a green go-live gate promotes to production.
The AWS onboarding pattern. The same intake drives AWS through Account Factory for Terraform (AFT). Here the unit of isolation is the account — one mh-<purpose>-<env> per app-environment — placed into the OU that carries the right Service Control Policies. The account customization runs a PHI baseline (VPC from 10.40.0.0/12, Transit Gateway attachment, VPC endpoints, s3 BlockPublicAccess, a per-account KMS CMK) before the app team ever logs in.
# aft-account-requests/clinical-web-prod.tf
module "clinical_web_prod" {
source = "./modules/aft-account-request"
control_tower_parameters = {
AccountName = "mh-clinical-web-prod"
AccountEmail = "aws+clinical-web-prod@meridianhealth.org"
ManagedOrganizationalUnit = "Workloads/Clinical/Prod" # inherits clinical SCPs
SSOUserEmail = "cloud-platform@meridianhealth.org"
SSOUserFirstName = "Clinical"
SSOUserLastName = "WebProd"
}
account_tags = {
Tier = "tier-1", DataClass = "phi", CostCenter = "cc-clinical-apps", Region = "us-east-1"
}
account_customizations_name = "phi-workload-baseline" # VPC, endpoints, KMS, Config rules
}
Intake feeds Account Factory, which vends the account into Workloads/Clinical/Prod, the baseline stamps the VPC, PrivateLink endpoints and CMK, SCPs plus AWS Config apply as preventive-and-detective rails, and the same cloud-agnostic go-live gate applies.
Keeping the two clouds symmetric is deliberate: an app owner sees one intake, one tier model, one data-class model and one go-live bar regardless of destination, which is the single biggest reason a control never gets silently skipped “because AWS is different this time.” Ownership and timelines are made explicit so the road has a known throughput.
| Onboarding activity | App team | Platform | Security | Change board | Target SLA |
|---|---|---|---|---|---|
| Submit intake | R/A | C | I | — | same day |
| Classify tier + data class | C | R | A | I | 1 business day |
| Vend landing zone (Terraform) | I | R/A | C | — | 2 hours (automated) |
| Guardrail attestation | I | R | A | — | inherited (instant) |
| Deploy workload | R/A | C | I | — | app team’s cadence |
| Go-live sign-off | C | C | R | A | 3 business days |
Because 180+ apps cannot be migrated at once, onboarding runs in waves grouped by estate, so each cohort re-uses the same landing pattern and the platform team hardens one pattern before the next cohort arrives.
| Wave | Cohort | Approx apps | Landing patterns used | Duration |
|---|---|---|---|---|
| 1 | Platform + Tier-0 foundation | 8 | Identity/DNS/network/security | 6 weeks |
| 2 | Clinical Tier-1 (EHR-adjacent) | ~30 | EHR-web, HL7/FHIR integration | 10 weeks |
| 3 | Imaging + telemedicine | ~25 | Imaging/VNA, telemedicine real-time | 8 weeks |
| 4 | Revenue-cycle + business | ~45 | Batch/RCM, corp SaaS-style | 10 weeks |
| 5 | Research + analytics | ~20 | Research/trials, lakehouse | 8 weeks |
| 6 | Legacy IaaS + long tail | ~52 | Lift-and-shift, COTS appliances | 14 weeks |
SAP, ERP, HR and business platforms
Behind the clinical estate sits the business machine that keeps 14 hospitals solvent: SAP S/4HANA for finance and supply chain, the HR platform for 55,000 staff, and the revenue-cycle systems that turn care into claims. These are not clinical Tier-1 by patient-safety definition, but S/4HANA is unquestionably Tier-1 by business impact — a HANA outage stops payroll, procurement and general-ledger close — so it gets the full active/active-adjacent treatment, just tuned for a stateful, memory-resident database rather than a stateless web farm.
The defining constraint of a HANA landing zone is that HANA is an in-memory column store: the entire productive database lives in RAM, so the VM is sized by memory, not CPU, and resilience is built on HANA System Replication (HSR) rather than generic disk mirroring. Meridian runs HANA scale-up on memory-optimised, HANA-certified VMs, with synchronous HSR across availability zones for zero-data-loss in-region failover and asynchronous replication to the paired DR region.
| HANA workload | Azure VM (certified) | AWS instance (certified) | Memory | Use |
|---|---|---|---|---|
| Production HANA (primary) | Standard_M128s |
u-3tb1.56xlarge |
2–3 TB | Productive S/4HANA DB |
| HSR secondary (sync) | Standard_M128s |
u-3tb1.56xlarge |
2–3 TB | Zone-2 zero-loss replica |
| DR replica (async) | Standard_M64s |
u-6tb1 (right-sized) |
1–2 TB | Cross-region failover |
| Non-prod (UAT/QA) | Standard_M32ts |
r7i.16xlarge |
0.5–1 TB | Test refreshes |
| App tier (ASCS/PAS/AAS) | Standard_E16s_v5 |
r7i.4xlarge |
128 GB | NetWeaver app servers |
HA and DR are two different mechanisms, deliberately. In-region high availability protects against a zone or node failure with synchronous replication (a transaction is on both nodes before commit, so RPO ≈ 0); disaster recovery protects against a whole-region loss with asynchronous replication (RPO ≤5 min, small data-in-flight risk accepted in exchange for not throttling production on cross-region latency). The single-point-of-failure to eliminate first is not HANA at all — it is the SAP central-services layer (ASCS) and its enqueue replication (ERS), which must be clustered.
| Layer | HA mechanism | DR mechanism | RTO | RPO |
|---|---|---|---|---|
| HANA DB | HSR sync to zone-2 + cluster auto-failover (SAPHanaSR) | HSR async to Central US, orchestrated failover | ≤30 m | ≈0 (HA) / ≤5 m (DR) |
| ASCS / ERS | Pacemaker (Linux) / WSFC (Windows) cluster, load-balanced VIP | Rebuilt in DR region from config | ≤30 m | 0 |
| App servers (PAS/AAS) | ≥2 instances behind SAP Web Dispatcher | Scale out in DR | ≤30 m | 0 (stateless) |
| Backups | — | Backint to immutable, geo-redundant vault (WORM + legal hold) | ≤4 h restore | log-backup interval |
Secure connectivity for SAP is stricter than a typical web app because the blast radius includes finance and HR PII. Nothing in the SAP estate carries a public IP; Fiori is the only front door and it is fronted by WAF and Entra SSO.
| Path | Control |
|---|---|
| End-user → Fiori | Application Gateway WAF_v2 + Entra ID SSO + Conditional Access; no direct SAP GUI from internet |
| On-prem → SAP | ExpressRoute private peering into the corp spoke; SAProuter/SNC where GUI is required |
| SAP → HANA | Private subnet only, TLS/SNC, no cross-spoke exposure |
| SAP → integration | Private Link to the integration spoke; all interfaces brokered, none point-to-point |
| Admin access | Bastion + PAM (just-in-time), all sessions recorded |
The reason SAP sits inside this document at all is integration — S/4HANA is not an island, it exchanges master data, charge capture and remittance with the clinical and revenue-cycle estate. Meridian brokers every one of these through the integration spoke so there is never a direct, untraceable link between finance and a clinical system holding PHI.
| Interface | Protocol | Source → target | Pattern |
|---|---|---|---|
| Charge capture | HL7 v2 DFT → X12 837 | EHR → SAP → payer | Interface engine maps DFT to claim |
| Remittance | X12 835 | Payer → SAP AR | Auto-posting to sub-ledger |
| Patient/guarantor master | HL7 v2 ADT ↔ FHIR Patient | EHR ↔ SAP | Bi-directional, PIX/PDQ identity match |
| GL / cost postings | IDoc / BAPI | SAP ↔ analytics lakehouse | Event to the data platform |
| Supply / implant usage | IDoc | OR system → SAP MM | Consumption drives replenishment |
| HR → identity | SCIM / IDoc | HR → Entra ID | Joiner-mover-leaver provisioning |
A single module encapsulates the whole SAP landing zone so it is provisioned through the same paved road as everything else — the tier, HSR mode and DR region are parameters, not bespoke architecture.
module "sap_s4hana" {
source = "app.terraform.io/meridian-health/sap-s4hana/azurerm"
version = "~> 2.1"
sid = "MHP"
environment = "prod"
location = "eastus2"
dr_location = "centralus"
hana = {
vm_sku = "Standard_M128s" # 2 TB, HANA-certified, memory-optimised
zones = [1, 2] # HSR SYNC across availability zones
replication_mode = "sync" # RPO ≈ 0 in-region
dr_replication = "async" # RPO ≤5 min to Central US
backint_vault = "immutable" # WORM + legal hold
}
app_tier = {
ascs_ers_cluster = true # Pacemaker/WSFC, load-balanced VIP
pas_aas_count = 4
}
fiori = { app_gateway_waf = "WAF_v2", entra_sso = true }
}
HR and the wider ERP surface follow the same rules: HR is the authoritative source for joiner-mover-leaver, feeding Entra ID over SCIM so an offboarded clinician loses EHR access the same day. The full business-platform inventory maps cleanly onto the existing landing patterns.
| Platform | Role | Landing | Tier / class |
|---|---|---|---|
| SAP S/4HANA | Finance, supply chain, GL | mh-lz-corp SAP module, A/A + DR |
Tier-1 / PII |
| HR platform | Workforce, payroll, JML source | Corp spoke, Entra-integrated | Tier-1 / PII |
| Revenue cycle / claims | 837/835 processing | Integration + batch pattern | Tier-1 / PHI |
| Procurement / MM | Purchasing, implant tracking | SAP MM + IDoc to OR | Tier-2 / PII |
| Corporate BI | Finance/ops analytics | Lakehouse (governed zone) | Tier-2 / mixed |
For the detailed HANA HA/DR build — HSR modes, cluster fencing and Backint retention — Meridian’s reference is the SAP S/4HANA on Azure HA/DR and backup architecture; the broader landing-zone framing lives in enterprise architecture for SAP on Azure.
Fiori enters through WAF and Entra SSO, the app tier clusters ASCS/ERS, HANA replicates synchronously across zones and asynchronously to Central US with immutable Backint backups, and the platform posts to the clinical and finance estate over the brokered integration bus.
Terraform and Ansible multi-stage CI/CD
Everything above — the landing zones, the SAP module, the guardrails — exists as code, and code needs a factory. Meridian runs a single infrastructure delivery platform on Terraform (provisioning) and Ansible (in-guest configuration), driven through multi-stage pipelines with policy gates, so that 180+ apps share one pipeline shape and one control set instead of 180 hand-rolled build definitions.
The foundation is repository topology. Modules, applications and the pipeline definition itself are separated so each has its own release cadence and blast radius. A fix to the spoke module is a new semantic-version tag consumed on each app’s own schedule, not a copy-paste into 180 repos.
| Repo type | Contains | Release mechanism | Consumed by |
|---|---|---|---|
| repo-per-module | One reusable module (spoke, PE, AKS, policy set) | Git tag vX.Y.Z → private registry / Artifacts feed |
App infra repos pin ~> X.Y |
| repo-per-app | One app’s infra/ + azure-pipelines.yml |
Branch → env promotion | The app’s own pipeline |
| pipeline-templates | Shared YAML stage templates | Git tag, extends reference |
Every app repo |
| policy | OPA/Conftest + Sentinel policy sets | Tag → gate consumption | The gate stage |
| ansible-roles | Idempotent, molecule-tested roles | Galaxy/Artifacts collection | Config stage |
One pipeline template, every app. Each app repo is thin: it extends the central template and passes parameters. Changing a gate — say, adding a new IaC scanner — is one pull request to the template repo, not a fleet-wide edit.
# app repo: azure-pipelines.yml — the whole pipeline is a parameterised extend
resources:
repositories:
- repository: templates
type: git
name: platform/pipeline-templates
ref: refs/tags/v3.2.0 # pin the template version, upgrade deliberately
extends:
template: terraform/multistage.yml@templates
parameters:
workload: clinical-web
tfWorkingDir: infra
environments: # dev/UAT auto, staging/prod gated
- { name: dev, apply: auto, pool: mh-runners-nonprod }
- { name: uat, apply: auto, pool: mh-runners-nonprod }
- { name: staging, apply: manual, pool: mh-runners-prod, approvers: [platform-leads] }
- { name: prod, apply: manual, pool: mh-runners-prod, approvers: [cab], changeRecord: true }
The template expands into stages that every workload runs identically — validate, plan, gate, apply, configure — with the promotion behaviour driven by the parameters above.
| Stage | Does | Gate | On failure |
|---|---|---|---|
| validate | terraform fmt/validate, module version check |
Syntax + pinned versions | Block, no plan |
| plan | terraform plan -out, IaC + cost scan |
tfsec, checkov, OPA, Infracost cap | Block; plan artifact not approvable |
| apply | terraform apply <plan> on private runner |
Env approval check | Block; no infra change |
| configure | ansible-playbook against the new hosts |
Molecule-tested roles, idempotence | Block; environment marked failed |
| verify | Smoke tests + guardrail attestation | Compliance evidence | Block promotion |
The apply and config stages share one template body — Terraform lands the infrastructure, Ansible finishes it in-guest, and both run through the same environment approval so infra and config promote as one change rather than two disjoint tools that drift apart.
# platform/pipeline-templates :: terraform/multistage.yml (abridged)
stages:
- stage: plan
jobs:
- job: plan
pool: ${{ parameters.pool }}
steps:
- script: terraform init -backend-config=env/$(name).tfbackend # remote state per env
- script: tfsec . --minimum-severity HIGH # IaC scan (gate)
- script: checkov -d . --compact # IaC scan (gate)
- script: terraform plan -out=$(name).tfplan
- script: conftest test $(name).tfplan --policy $(policyDir) # OPA policy gate
- script: infracost breakdown --path . | infracost comment # cost gate
- stage: apply
dependsOn: plan
jobs:
- deployment: apply
environment: mh-$(name) # approval + change-record checks live here
pool: ${{ parameters.pool }}
strategy:
runOnce:
deploy:
steps:
- script: terraform apply $(name).tfplan # apply the reviewed plan
- script: >
ansible-playbook -i inventory/$(name)_azure_rm.yml
site.yml --tags "cis,agents,app" # OS/app config + hardening
Environment promotion is dev → UAT → staging → prod, and the same artifact — the reviewed Terraform plan and the built app image — moves across all four, so production runs exactly what UAT and staging tested rather than a fresh rebuild. Non-prod auto-applies for speed; staging and prod require a human approval bound to the change-advisory board and a ServiceNow change record.
| Environment | Apply mode | Approver | Data | Runner pool |
|---|---|---|---|---|
| dev | Auto | None (PR review only) | Synthetic | mh-runners-nonprod |
| UAT | Auto | None | De-identified | mh-runners-nonprod |
| staging | Manual gate | Platform leads | Prod-like, masked | mh-runners-prod |
| prod | Manual gate | CAB + change record | Live PHI | mh-runners-prod |
Applies that touch PHI environments cannot run on cloud-hosted shared agents — they need to reach private endpoints and hold a workload identity, not a long-lived secret. Meridian runs self-hosted VMSS / managed runner pools inside the platform network, federated to the cloud via OIDC so there are no static keys, scaling to zero when idle.
| Runner pool | Where | Identity | Scale | Reaches |
|---|---|---|---|---|
mh-runners-nonprod |
Non-prod platform VNet | OIDC workload identity | 0→N on demand | Non-prod private endpoints |
mh-runners-prod |
Prod platform VNet | OIDC, prod scope only | 0→N, capped | Prod private endpoints, PHI |
mh-runners-ansible |
Config subnet | SSH/WinRM via vault creds | Fixed small pool | In-guest OS config |
Meridian’s module design, versioning and composition conventions follow Terraform module design, composition and versioning; the multi-project pipeline platform and VMSS agents are covered in an enterprise Azure DevOps platform with VMSS agents and the blue/green promotion model in a multi-stage CI/CD branching strategy; Ansible practice sits in idempotent Ansible collections and Molecule testing and dynamic inventory across AWS and Azure.
Versioned module and app repos feed one extends-based template, every environment runs the same plan → policy gate → apply on private runners with Ansible finishing config, and promotion is gated by approvals and a change record.
DevSecOps software supply chain
Provisioning securely is half the job; the software that lands on that infrastructure is the other half, and in a hospital a compromised build is a PHI breach waiting to happen. Meridian treats every artifact that touches patient data as untrusted until it has passed a full gate set — scanned at source, built reproducibly, signed with provenance, re-scanned in the registry, and admitted to production only if its signature and attestations verify. Given Meridian’s history of leaked database credentials in source control, secret scanning is not a nicety; a hit fails the build and pages security.
The gate set spans code, dependencies, secrets, containers, infrastructure, the pipeline itself, and the full test pyramid — every gate fails the build, never merely annotates it.
| Gate | Stage | Tool examples | Fails build on | Evidence produced |
|---|---|---|---|---|
| SAST | PR / commit | CodeQL, SonarQube, Semgrep | High/critical code flaw | Code-scan alerts |
| SCA | PR / commit | Dependabot, Grype, OWASP DC | Critical CVE in dependency | Dependency report |
| Secret scan | PR + pre-receive | gitleaks, GitHub secret scanning, TruffleHog | Any committed secret | Secret-scan alert (→ page) |
| IaC scan | plan | tfsec, checkov, KICS | Misconfig (public IP, no CMK) | Policy findings |
| Container scan | build + registry | Trivy, Defender for Containers, Grype | Critical (fixable) CVE | Vuln report + VEX |
| DAST | staging | OWASP ZAP, Burp Enterprise | High web vuln | Dynamic-scan report |
| Pipeline / CI scan | pipeline defn | GitHub Actions pinning, StepSecurity | Unpinned action, risky perms | Hardened-runner log |
| Functional test | build/UAT | xUnit/NUnit, pytest | Failing assertion, coverage < gate | Test + coverage report |
| API test | UAT | Postman/Newman, REST-assured, Pact | Contract break, 4xx/5xx | Contract results |
| UI / E2E | staging | Playwright, Cypress, Selenium | Broken critical journey | E2E run + screenshots |
| Load / performance | staging | k6, JMeter, Gatling | p95 latency / error-rate breach | Perf baseline |
| DB schema as code | apply | Flyway, Liquibase, sqlpackage | Failed/irreversible migration | Migration + rollback plan |
The security stage sits early and hard, and the build-and-sign stage produces the artifacts the rest of the chain verifies against.
- stage: security_gates
jobs:
- job: code_scan
steps:
- task: CodeQL@2 # SAST
- script: gitleaks detect --redact --exit-code 1 # secret scan → fails build + pages
- script: grype dir:. --fail-on high # SCA, fails on High+
- job: build_sign_sbom
dependsOn: code_scan
steps:
- script: docker build -t $ACR/clinical-web:$(tag) . # distroless, pinned digest
- script: trivy image --exit-code 1 --severity CRITICAL $ACR/clinical-web:$(tag) # scan
- script: syft $ACR/clinical-web:$(tag) -o spdx-json > sbom.json # SBOM
- script: cosign sign --yes $ACR/clinical-web:$(tag) # sign (keyless OIDC)
- script: cosign attest --yes --predicate sbom.json --type spdxjson $ACR/clinical-web:$(tag)
Signing and provenance turn “trust me” into “verify me.” Every image is signed keylessly (OIDC identity, no long-lived key to leak), an SBOM is attached as an attestation, and the cluster’s admission controller refuses to run anything whose signature and attestations do not verify against the trusted identity. This is what lets Meridian answer “are we exposed to CVE-X across 180 apps?” from SBOM data in minutes instead of a week of guesswork.
| Artifact | Sign with | SBOM / provenance | Verified at |
|---|---|---|---|
| Container image | cosign (keyless OIDC) | Syft SPDX + SLSA provenance attestation | Admission (Ratify/Kyverno/Gatekeeper) |
| Helm chart | cosign | Chart provenance | Deploy gate |
| Terraform module | Git signed tag | Module version + checksum | Registry pull |
| App package | Pipeline provenance | Build attestation | Pre-deploy |
Databases are code too. A schema change is the highest-risk deploy in a clinical system — a bad migration can lock an EHR table mid-shift — so Meridian manages schema as versioned, forward-only-with-rollback migrations gated exactly like application code, never applied by hand.
| Concern | Approach | Tool |
|---|---|---|
| Versioned migrations | Numbered, immutable, checked in | Flyway / Liquibase |
| Rollback | Paired down-migration or restore point | Flyway undo / snapshot |
| Drift detection | Compare live schema to repo | Liquibase diff / sqlpackage |
| Gate | Migration dry-run in staging before prod | Pipeline stage |
| PHI-table changes | Extra review + break-glass audit note | Change board |
Meridian’s DevSecOps reference architectures are a secure CI/CD supply chain with Vault and code scanning, SBOM consumption, VEX and admission verification, pipeline secret scanning and remediation, the secure container registry with ACR Tasks, registry signing and scanning with Harbor and Trivy, and the shift-left testing and quality-gate model.
A protected commit passes SAST, SCA and secret scanning, the build produces a distroless image signed with an SBOM, the registry re-scans against CVEs and VEX, and admission control admits only verified images into the production namespace.
Observability and SOC integration
A landing zone you cannot see is a landing zone you cannot run safely, and in healthcare “see” has an unusually wide meaning: not just CPU and error rates, but whether an HL7 ADT feed is flowing, whether a telemedicine visit will actually connect, and whether a clinician is exporting ten thousand records at 2 a.m. Meridian instruments everything with OpenTelemetry as the vendor-neutral collection layer, routes signals to the right per-cloud backend, and feeds security-relevant events into Microsoft Sentinel where a 24×7 SOC turns detections into owned, closable incidents.
The telemetry sources span four layers, and the two that generic architectures forget — clinical interface flow and patient-facing UX — are exactly the ones that matter most in a hospital.
| Source | Signal | Tool | Why it matters here |
|---|---|---|---|
| Application / APM | Traces, logs, RED metrics | OTel SDK → App Insights / X-Ray | Latency in the EHR path is a clinical risk |
| Infrastructure | Metrics, VM/host, K8s | Azure Monitor / CloudWatch | Capacity + saturation of PHI workloads |
| Network | Flow logs, DNS, firewall | NSG/VPC flow, Firewall logs | Lateral-movement + exfil detection |
| Interface / message flow | HL7 v2 / FHIR / X12 status | Interface-engine + OTel | A stuck ADT feed is a care-delivery outage |
| Patient portal / telemedicine | RUM, synthetics, video QoS | Front-end RUM + synthetic probes | A visit that won’t connect is a clinical incident |
| Identity / control plane | Sign-ins, admin actions | Entra + cloud audit → Sentinel | Break-glass and privilege abuse |
The collector is where cost and PHI are controlled. A gateway-mode OpenTelemetry Collector tail-samples (keep every error and slow trace, sample the rest), scrubs PHI out of span attributes before anything leaves the workload, and fans each signal out to the right sink. One instrumentation, many backends — swapping an APM tool never means re-instrumenting 180 apps.
# otel-collector-config.yaml (gateway mode, one per region)
receivers:
otlp: { protocols: { grpc: {}, http: {} } }
processors:
tail_sampling: # keep all errors + slow, sample the rest
policies:
- { name: errors, type: status_code, status_code: { status_codes: [ERROR] } }
- { name: slow, type: latency, latency: { threshold_ms: 1500 } }
- { name: sample, type: probabilistic, probabilistic: { sampling_percentage: 5 } }
transform/scrub_phi: # PHI must never enter telemetry
trace_statements:
- context: span
statements:
- delete_key(attributes, "patient.mrn")
- delete_key(attributes, "http.request.body")
exporters:
azuremonitor: { connection_string: "${APPINSIGHTS_CONNECTION_STRING}" }
awsemf: { region: us-east-1, namespace: "meridian/apps" }
service:
pipelines:
traces:
receivers: [otlp]
processors: [tail_sampling, transform/scrub_phi]
exporters: [azuremonitor, awsemf]
Each observability pillar has a defined destination and retention, tuned to both cost and the HIPAA six-year audit-log expectation for PHI access.
| Pillar | Azure destination | AWS destination | Retention |
|---|---|---|---|
| Traces | Application Insights | X-Ray / OTLP backend | 30–90 days |
| Metrics | Azure Monitor Metrics | CloudWatch Metrics | 90 days–13 months |
| App / infra logs | Log Analytics workspace | CloudWatch Logs | 90 days hot, archive after |
| PHI-access audit | Log Analytics (immutable) + archive | CloudTrail + immutable S3 | 6 years (HIPAA) |
| Security signals | Sentinel | Security Hub → Sentinel | Per policy |
Security-relevant telemetry converges on Sentinel, where analytics rules do the watching — no one stares at dashboards hoping to catch a breach. Data connectors ingest Entra, Defender, cloud control-plane and application audit logs; scheduled KQL rules and UEBA raise incidents on the specific abuse patterns a healthcare estate must catch.
| Detection | Rule basis | Primary data source |
|---|---|---|
| Abnormal bulk PHI export | KQL threshold vs role baseline | EHR audit logs |
| Impossible travel to EHR | UEBA / geo-velocity | Entra sign-in logs |
| Break-glass over-use | Emergency-access account activity | PAM + EHR audit |
| Disabled logging / CloudTrail | Control-plane tamper rule | Azure Activity / CloudTrail |
| Malware in PHI storage | Defender for Storage alert | Storage / S3 scan |
| Interface-engine failure spike | Message-age / error rate | Interface engine + OTel |
// Sentinel scheduled analytics rule: abnormal bulk PHI export from the EHR
let lookback = 1h;
let baseline = 50; // per-user/hour rows, tuned per clinical role
AppEhrAuditLogs
| where TimeGenerated > ago(lookback)
| where Action in ("Export", "BulkPrint", "ReportDownload")
| summarize Records = sum(RecordCount), Patients = dcount(PatientId)
by UserPrincipalName, bin(TimeGenerated, 10m)
| where Records > baseline * 5 // 5x the role's normal volume
| join kind=inner (
SigninLogs | where ResultType == 0
| project UserPrincipalName, IPAddress, Country = tostring(LocationDetails.countryOrRegion)
) on UserPrincipalName
| project TimeGenerated, UserPrincipalName, Records, Patients, IPAddress, Country
Detection without an owned response is theatre. Every Sentinel incident triggers a SOAR playbook that enriches and contains (revoke a token, isolate a host) and opens a ServiceNow incident with severity, on-call assignment and MTTR tracking — and for a confirmed PHI event, the SOC-to-privacy-officer handoff that starts the HIPAA breach-notification clock is written into the runbook, not improvised.
| Severity | Owner | Response SLA | Action |
|---|---|---|---|
| Sev-1 (active PHI breach / Tier-1 down) | SOC lead + IC + privacy officer | 15 min | SOAR contain, exec bridge, breach clock starts |
| Sev-2 (contained threat / degraded) | SOC analyst + app on-call | 1 hour | Investigate, remediate, ITSM incident |
| Sev-3 (policy / anomaly) | SOC analyst | 4 hours | Triage, tune rule, ticket |
| Sev-4 (informational) | Automated | Next business day | Auto-enrich, log, trend |
Meridian’s observability and SOC practice draws on production OpenTelemetry Collector pipelines, KQL for Azure Monitor and Log Analytics, and the incident-response runbooks, tabletops and cloud forensics that turn a Sentinel alert into a closed, evidenced case.
Applications, infrastructure and patient-facing UX emit OpenTelemetry, the collector tail-samples and scrubs PHI before routing to Log Analytics and CloudWatch, security signals feed Sentinel where the 24×7 SOC triages, and a detection fires a SOAR playbook and a ServiceNow incident.
Cost model and TCO
A design that cannot be costed cannot be approved, and a design that hides its recurring bill behind “cloud is elastic” gets cancelled at the first quarterly review. Every figure in this section is a steady-state monthly run cost — Year-2, post-migration, all 180+ applications landed — priced against public list rates for the pinned regions (East US 2, Central US, West Europe; us-east-1, us-west-2, eu-west-1) before the enterprise and committed-use discounts captured in the levers table. Money is shown in USD and INR at ₹86.0/USD — INR is Meridian Health’s board reporting currency. The model is split into seven cost pools, each with a named owner and a lever, because a pool nobody owns is a pool nobody defends when finance asks why the bill grew.
The pinned facts that drive the numbers: two-region active/active for Tier-1 (so Tier-1 compute is roughly doubled), 2.3 PB of imaging under lifecycle management, immutable PHI audit into Log Analytics + Sentinel (the single largest platform line), and a private-endpoint-only posture (hundreds of endpoints, each a small but real charge). Pricing basis and exclusions are stated up front so the model is auditable, not a magic number.
| Assumption | Value | Basis / note |
|---|---|---|
| FX rate | ₹86.0 / USD | Board reporting rate, 2026; re-baselined quarterly |
| Costing horizon | Year-2 steady state | Post-migration run rate, not the ramp |
| Pricing basis | Public list, pinned regions | Pre-discount; levers table applies EA/CUD/AHB |
| Coverage | Platform + all 4 tiers + imaging + egress + licensing | Shared landing zone and the workloads it hosts |
| Excluded | Epic/EHR app licensing, clinical device capex, WAN/SD-WAN circuits to sites, on-prem DC power | Owned by other budget lines; noted, not double-counted |
| Replication | GZRS for hot/cool imaging + geo for Tier-1 data | Cross-region transfer priced in the egress pool |
| Audit ingest | ~450 GB/day Log Analytics + Sentinel | HIPAA/HITECH immutable access logging, 180+ apps |
Monthly run cost by pool
The headline: ~$577,000/month (₹4.96 crore/month), or ~$6.92M/year (₹59.5 crore/year) at list. Tier-1 clinical workloads and the platform shared-services fabric together account for six of every ten rupees — exactly where an integrated delivery network should spend, and exactly where the reservations lever bites hardest.
| Cost pool | Owner | USD / mo | INR / mo | % of run |
|---|---|---|---|---|
| Platform / shared services | Cloud Platform | $148,000 | ₹1.27 Cr | 25.6% |
| Tier-1 clinical (active/active) | Clinical Apps | $206,000 | ₹1.77 Cr | 35.7% |
| Tier-2 business / analytics | Data & Business | $71,000 | ₹0.61 Cr | 12.3% |
| Tier-3 dev / sandbox | Cloud Platform | $19,000 | ₹0.16 Cr | 3.3% |
| Imaging storage 2.3 PB + VNA | Imaging | $27,500 | ₹0.24 Cr | 4.8% |
| Egress + inter-region/cloud | Network | $13,500 | ₹0.12 Cr | 2.3% |
| Licensing + third-party tooling | ARB / Security | $92,000 | ₹0.79 Cr | 15.9% |
| Total (list) | — | $577,000 | ₹4.96 Cr | 100% |
Platform / shared-services breakdown
This pool is the landing zone itself — the fabric every workload rents whether it sends one request or a million. The observability line dominates because immutable PHI-access logging across 180+ applications is not optional under HIPAA/HITECH, and Sentinel analytics is billed per GB ingested.
| Platform component | What it covers | USD / mo | INR / mo |
|---|---|---|---|
| Connectivity | Dual ExpressRoute 10G + dual Direct Connect 10G + transfer | $20,000 | ₹17.2 L |
| Network hubs | vWAN ×3 + AWS TGW ×2, Azure Firewall Premium secured hubs | $19,000 | ₹16.3 L |
| Observability | Log Analytics + Sentinel (~13.7 TB/mo) + CloudWatch/CloudTrail | $44,000 | ₹37.8 L |
| Security | Defender for Cloud plans + GuardDuty/Security Hub/Inspector/Macie | $23,000 | ₹19.8 L |
| Backup / DR | Azure Backup + RSV + ASR (Tier-1) + AWS Backup cross-region | $18,000 | ₹15.5 L |
| Private access | ~600 private endpoints, Private DNS, Bastion, NAT GW, Managed HSM | $15,000 | ₹12.9 L |
| Platform mgmt | Arc, Update Manager, Policy, DNS, small ops compute | $9,000 | ₹7.7 L |
| Subtotal | — | $148,000 | ₹1.27 Cr |
Per-tier workload compute and data
Active/active is the single biggest cost decision in the whole model: Tier-1 runs two live in-country regions plus an EU region for residency, so its compute and stateful data are provisioned roughly 2× versus a warm-standby design. That is the price of an RTO ≤30 min / RPO ≤5 min on EHR-adjacent, ADT, results, medication, emergency, portal and telemedicine-core services — and it is a deliberate, ADR-recorded trade (see ADR-002).
| Tier | Representative services | USD / mo | INR / mo |
|---|---|---|---|
| Tier-1 (active/active) | AKS clinical microservices, FHIR facade, SQL MI BC, Cosmos multi-write, APIM Premium, Front Door | $206,000 | ₹1.77 Cr |
| Tier-2 (single-region ZRS) | Synapse/Databricks, Power BI Premium, revenue cycle, claims, corp apps | $71,000 | ₹0.61 Cr |
| Tier-3 (spot + auto-stop) | Non-prod AKS/App Service, dev SQL, sandbox subs | $19,000 | ₹0.16 Cr |
| Subtotal | Tier-0 identity/security folded into platform | $296,000 | ₹2.55 Cr |
Imaging storage — the 2.3 PB question
Imaging is where naïve cost models explode: 2.3 PB on hot Blob is a $45k/month mistake. The Azure Blob access tiers hot/cool/cold/archive cost model is applied literally — a lifecycle policy on the Vendor-Neutral Archive demotes studies as they age past clinical-recall windows, so 60% of the corpus sits on cold/archive at a fraction of a cent per GB. Retrieval stays clinical because recent and relevant-prior studies live hot/cool; only deep archive pays a rehydration penalty, and worklist pre-fetch hides it.
| Tier | Share | Capacity | Unit $/GB-mo | USD / mo | INR / mo |
|---|---|---|---|---|---|
| Hot (GZRS) | 15% | 345 TB | $0.0196 | $6,762 | ₹5.8 L |
| Cool (GZRS) | 25% | 575 TB | $0.0115 | $6,613 | ₹5.7 L |
| Cold (LRS) | 20% | 460 TB | $0.0040 | $1,840 | ₹1.6 L |
| Archive (LRS) | 40% | 920 TB | $0.00099 | $911 | ₹0.8 L |
| Transactions + retrieval + index | — | — | — | $3,500 | ₹3.0 L |
| AWS S3 DR/research copy (~600 TB Glacier IR/DA) | — | — | — | $3,900 | ₹3.4 L |
| VNA runtime (DICOM router, zero-footprint viewers) | — | — | — | $3,600 | ₹3.1 L |
| Subtotal | 100% | 2.3 PB | — | $27,126 | ₹0.23 Cr |
Egress and data transfer
Egress is the line that surprises healthcare CFOs, because DICOM and FHIR payloads are large and telemedicine video is continuous. The design keeps clinician-to-VNA retrieval private (over Private Link, not internet), pushes patient-portal and telemedicine media through Front Door caching, and confines cross-cloud transfer to research and DR — so the internet-egress line stays modest against a 55,000-staff footprint.
| Transfer class | Path | USD / mo | INR / mo |
|---|---|---|---|
| Internet egress | Portal, telemedicine media (CDN-fronted), partner FHIR/X12 | $6,500 | ₹5.6 L |
| Inter-region (intra-cloud) | Active/active sync + geo-backup | $4,000 | ₹3.4 L |
| Cross-cloud | Azure ↔ AWS for research + DR copy | $3,000 | ₹2.6 L |
| Subtotal | — | $13,500 | ₹0.12 Cr |
Licensing and third-party tooling
These are the ISV and platform-tooling costs the hyperscaler bill hides — the interface engine, the cloud VNA/PACS subscription, the multicloud CNAPP, the clinical de-identification engine, and the IaC/GRC toolchain. They are amortized to a monthly figure from annual contracts.
| Item | Purpose | USD / mo | INR / mo |
|---|---|---|---|
| Interface engine (Rhapsody, HA, EU) | HL7 v2 / FHIR integration backbone | $22,000 | ₹18.9 L |
| Entra ID P2 / EMS E5 (security uplift portion) | PIM, CA, ID Protection, governance | $18,000 | ₹15.5 L |
| Cloud VNA + PACS subscription | Imaging system of record | $14,000 | ₹12.0 L |
| Multicloud CNAPP/CSPM (Wiz-class) | Posture beyond Defender across Azure+AWS | $9,000 | ₹7.7 L |
| Clinical de-identification engine | Research-safe pseudonymization | $6,500 | ₹5.6 L |
| IaC + secrets (Terraform Enterprise, Vault, GitHub Ent.) | Landing-zone vending + CI/CD | $7,500 | ₹6.5 L |
| DICOM router/gateway + zero-footprint viewer | Modality worklist + image delivery | $5,500 | ₹4.7 L |
| GRC / HITRUST automation | Continuous control evidence | $4,000 | ₹3.4 L |
| Telemedicine platform license | Encrypted video + scheduling + intake | $5,500 | ₹4.7 L |
| Subtotal | — | $92,000 | ₹0.79 Cr |
Cost-control levers
List price is the opening bid, not the bill. The multicloud FinOps unit-economics discipline and the Azure reservations vs savings plans decision framework drive a ~28% reduction at steady state — from $577k to roughly $415k/month (₹3.57 crore) — without touching a single SLA. Each lever below is quantified and owned; the Cloud-Ops FinOps pod reports actuals-vs-lever monthly to the ARB.
| Lever | Mechanism | Est. saving/mo | Owner |
|---|---|---|---|
| Compute reservations + savings plans (3-yr) | Cover stable Tier-1/2 compute | -$83,000 | FinOps |
| Right-sizing idle/oversized (Advisor + Cost) | Remove waste continuously | -$20,000 | Cloud-Ops |
| ISV private offers + EA/MCA discount | Marketplace + committed spend | -$16,000 | ARB / Procurement |
| Azure Hybrid Benefit (Windows + SQL) | Reuse on-prem licenses | -$14,000 | Platform |
| Non-prod spot + auto-shutdown | Tier-3 off nights/weekends | -$11,000 | Cloud-Ops |
| Log Analytics/Sentinel commitment tier | 300 GB/day commitment | -$9,000 | Security |
| Front Door caching + egress shaping | Cut repeat media egress | -$5,000 | Network |
| Storage lifecycle tuning | Sharpen cold→archive rules | -$4,000 | Imaging |
| Net levers | — | -$162,000 | — |
Rolled into a three-year total cost of ownership, the picture the board signs is one-time build plus a discounted, ramping run. Year-1 run is partial (workloads land through the waves), Years 2–3 are steady state with levers applied.
| TCO component | USD | INR |
|---|---|---|
| One-time: SI/migration (24 mo), HITRUST r2, ER/DX install, training | $11,000,000 | ₹94.6 Cr |
| Run Year-1 (~45% ramp, list) | $3,110,000 | ₹26.7 Cr |
| Run Year-2 (~90%, levered) | $4,480,000 | ₹38.5 Cr |
| Run Year-3 (100%, levered) | $4,980,000 | ₹42.8 Cr |
| 3-year TCO | ~$23.6M | ~₹202.6 Cr |
Bill of materials
The bill of materials is the buy-list — the exact services and products the roadmap provisions, split by platform so procurement, security review and the ARB can each work their column. It is intentionally implementable: every row names a real SKU or product, not a category. Where a component is dual-cloud (identity federation, DNS, backup), it appears on both cloud lines with its cross-cloud role noted.
Azure services
| Domain | Service / SKU | Role in the design |
|---|---|---|
| Governance | Management Groups, Azure Policy, Blueprints/Deployment Stacks | mh hierarchy, HIPAA/HITRUST guardrails as-code |
| Identity | Entra ID P2, Entra Connect (cloud sync), PIM, Conditional Access | Single IdP hub, PHS + Seamless SSO, JIT admin |
| Connectivity | ExpressRoute (2× 10G), Virtual WAN, Azure Firewall Premium, Private DNS | Hybrid backbone, secured hubs, private resolution |
| Private access | Private Link / Private Endpoints, Bastion, NAT Gateway | PHI PaaS with no public exposure |
| Compute | AKS, App Service, Container Apps, Functions, VM Scale Sets | Clinical microservices, portal, telemed, batch |
| Data | Azure SQL MI (Business Critical), Cosmos DB, PostgreSQL Flexible, Cache | ADT/results/meds stores, FHIR, session state |
| Integration | API Management Premium, Event Hubs, Service Bus Premium | FHIR/SMART gateway, event mesh, resilient queues |
| Imaging | Blob (GZRS + lifecycle), Azure NetApp Files | VNA archive tiers, viewer scratch |
| Analytics | Synapse/Fabric, Databricks, Purview, Power BI Premium | Lake zones, governance, research, BI |
| Security | Defender for Cloud, Microsoft Sentinel, Managed HSM, Key Vault | CSPM/CWPP, SIEM, FIPS 140-3 L3 CMK |
| Resilience | Azure Backup, Recovery Services Vault, Site Recovery | Tier-based backup + Tier-1 replication |
| Edge/IoT | Azure Arc, IoT Hub / IoT Operations | Device identity, edge gateway management |
AWS services
| Domain | Service | Role in the design |
|---|---|---|
| Governance | Organizations, Control Tower, SCPs, Config | OU tree Root>Security>Infrastructure>Workloads, guardrails |
| Identity | IAM Identity Center federated to Entra (OIDC/SAML) | No local IdP; Entra remains the hub (ADR-001) |
| Connectivity | Direct Connect (2× 10G), Transit Gateway, Route 53 Resolver | Hybrid backbone, hub routing, split-horizon DNS |
| Compute | EKS, ECS Fargate, Lambda, EC2 | Research + select imaging/analytics workloads |
| Data | RDS/Aurora, DynamoDB, S3 | Research datasets, DR imaging copy |
| Imaging/data | S3 (Glacier IR/Deep Archive), Lake Formation, Glue, Athena, Redshift | Imaging DR, governed research lake, de-id analytics |
| Security | GuardDuty, Security Hub, Inspector, Macie, KMS (CMK), Security Lake | Threat detection, PHI discovery, key mgmt, log lake |
| Resilience | AWS Backup, cross-region + cross-account copy | Research/imaging DR |
| AI/ML | SageMaker (isolated), Bedrock (guardrailed) | Clinical-trials modelling in a research-safe VPC |
On-premises, edge and third-party tooling
| Category | Product / component | Role |
|---|---|---|
| On-prem identity | AD DS forest corp.meridianhealth.org |
Source of truth synced to Entra |
| Clinical systems | Epic-class EHR, LIS/RIS, SAP S/4HANA, HR | Systems of record feeding the interface engine |
| Imaging on-prem | PACS, modalities, DICOM gateways | Feed the cloud VNA; edge cache at large hospitals |
| Interface engine | Rhapsody (HA pair, US + EU) | HL7 v2 / FHIR / X12 backbone (ADR-005) |
| Edge compute | Hospital edge gateways, NAC/microseg | Unpatchable device isolation, low-latency intake |
| Security tooling | Wiz-class CNAPP, PAM vault, email security | Multicloud posture, privileged access, phishing |
| De-identification | Clinical de-id / pseudonymization engine | Research-safe zone population (ADR-006) |
| Observability | Grafana/Datadog dashboards over Azure Monitor + CloudWatch | Single-pane clinical + platform SLOs |
| GRC / IaC | Terraform Enterprise, HashiCorp Vault, GRC/HITRUST automation | Vending, secrets broker, continuous evidence |
Operating model and RACI
A landing zone without a named operating model degrades into a shared folder of Terraform nobody dares touch. Meridian Health runs a platform-as-a-product model: a small number of durable, funded pods own named planes of the estate, publish golden paths, and are on the hook for their SLOs — while application teams consume the platform through self-service vending rather than tickets. The Architecture Review Board (ARB) is the standing authority that ratifies ADRs, arbitrates cross-pod trade-offs and holds the stage-gates.
| Pod / team | Charter | Primary SLOs | On-call |
|---|---|---|---|
| Cloud Platform | LZ vending, subs/accounts, IaC modules, Policy | Vend < 1 day, guardrail coverage ≥ 98% | Biz hours + escalation |
| Security | Sentinel/Defender, IR, PHI controls, break-glass | MTTA ≤ 15 min Sev-1, 0 unremediated criticals | 24×7 |
| Network | Hubs, ExpressRoute/DX, firewall, DNS, Private Link | Backbone availability ≥ 99.95% | 24×7 |
| Clinical Apps | Tier-1 EHR-adjacent, ADT, meds, portal, telemed | Tier-1 RTO ≤ 30m / RPO ≤ 5m | 24×7 |
| Integration | Interface engine, FHIR/APIM, event mesh | 0 lost messages, replay ≤ 15 min | 24×7 |
| Imaging | VNA, DICOM routing, lifecycle, viewers | Study retrieval P95 ≤ 3s (hot/cool) | 24×7 |
| Data & Research | Lake zones, de-id, trials isolation, governance | Dataset approval SLA, 0 un-audited exports | Biz hours |
| Cloud-Ops / FinOps | Run, patch, backup, cost, capacity | Backup success ≥ 99.9%, lever attainment | 24×7 |
| ARB | ADRs, standards, gate authority | Gate decisions ≤ 5 business days | Governance cadence |
The RACI below is the contract between those pods for the activities that cross boundaries — the exact places where “I thought you owned that” causes an outage or an audit finding. R = Responsible (does the work), A = Accountable (single owner, one per row), C = Consulted, I = Informed.
| Activity | Platform | Security | Network | Clinical-Apps | Integration | Imaging | Data-Research | Cloud-Ops | ARB |
|---|---|---|---|---|---|---|---|---|---|
| Landing-zone / subscription vending | A/R | C | C | I | I | I | I | R | I |
| Policy & guardrail baseline | R | A | C | I | I | I | C | I | C |
| Identity & Conditional Access | C | A/R | I | C | I | I | I | I | C |
| Network hub / ExpressRoute change | C | C | A/R | I | I | I | I | R | I |
| Private-endpoint / DNS onboarding | R | C | A | C | C | C | C | R | I |
| PHI encryption / CMK-HSM | R | A | I | C | I | C | C | R | C |
| Tier-1 failover / DR drill | C | C | R | A/R | R | R | I | R | I |
| Interface-engine (HL7/FHIR) change | I | C | I | C | A/R | C | C | I | C |
| Imaging lifecycle / VNA retention | I | C | I | I | C | A/R | C | R | C |
| De-identification / research release | I | C | I | I | C | I | A/R | I | C |
| SIEM / incident response (PHI breach) | C | A/R | C | C | C | C | C | R | I |
| Backup / restore verification | I | C | I | C | C | C | C | A/R | I |
| Cost / reservation / FinOps action | C | I | C | C | I | I | C | A/R | C |
| ADR ratification / standards | C | C | C | C | C | C | C | C | A/R |
| Break-the-glass emergency access | I | A/R | I | R | I | I | I | R | I |
Governance runs on a fixed cadence so decisions do not wait for a crisis, and so evidence for HITRUST/SOC 2 accrues continuously rather than in an audit-week scramble.
| Forum | Cadence | Chair | Decides |
|---|---|---|---|
| Architecture Review Board | Weekly | Chief Architect | ADRs, exceptions, stage-gates |
| Change Advisory Board | 2× weekly | Cloud-Ops lead | Tier-1 changes, freeze windows |
| Security & Compliance council | Monthly | CISO delegate | Risk acceptance, control posture |
| FinOps review | Monthly | FinOps lead | Lever attainment, showback/chargeback |
| Clinical safety board | Per go-live | CMIO delegate | Clinical safety case sign-off |
Migration and onboarding waves
Meridian Health does not “lift-and-shift 180 apps.” It executes six overlapping waves over ~24 months, each with an explicit entry gate (what must be true to start) and exit gate (what must be proven to finish and fund the next). The gates are hard: no gate, no money. The diagram is the whole program on one page — foundation and guardrails first, then the identity and network trust plane, the interoperability message plane, the first Tier-1 clinical go-lives, imaging and research at scale, and finally the transition to optimized run. Each numbered badge is a stage-gate the ARB holds.
| Wave | Months | Scope | Entry gate | Exit gate |
|---|---|---|---|---|
| W1 Foundation | M0–5 | MG/OU tree, Policy baseline, dual ER/DX, hubs | Funding + tenant/org secured | CIS+HITRUST scan clean, connectivity SLA proven (G1) |
| W2 Identity + Network | M4–9 | Entra hub, AWS federation, vWAN/TGW, Private Link, DNS | G1 passed | Sole IdP live, MFA 100%, PE-only PHI path (G2) |
| W3 Interoperability | M8–14 | Rhapsody, FHIR/APIM, event mesh, X12 | G2 passed | 4-wk parallel-run, 0 lost messages, traceability (G3) |
| W4 Clinical | M12–18 | Tier-1: ADT, results, meds, portal, telemed | G3 passed | Active/active drill RTO≤30m/RPO≤5m, safety case (G4) |
| W5 Imaging + Data + Research | M14–22 | VNA 2.3 PB, DICOM, lake zones, de-id, trials | G4 passed | VNA is SoR, C-FIND/C-MOVE parity, 0 un-audited export (G5) |
| W6 Run + Optimize | M20–24 | FinOps levers, SLO steady-state, HITRUST cert | G5 passed | SLOs green 90d, HITRUST r2, run-book handover (G6) |
Within a wave, every application follows the same onboarding runbook — the atomic unit the Cloud Platform pod vends and the app team consumes — so the 180-app backlog runs as a repeatable factory rather than 180 snowflakes.
| Step | Action | Owner | Gate to proceed |
|---|---|---|---|
| 1 | Intake: tier, PHI class, residency, dependencies | Clinical-Apps + ARB | Tier & data-class assigned |
| 2 | Vend landing zone (sub/account, network, PE, policy) | Cloud Platform | Guardrails green |
| 3 | Wire identity, CMK/HSM, secrets, private DNS | Security + Platform | No public endpoint, CMK bound |
| 4 | Connect feeds (HL7/FHIR/DICOM/X12) via engine | Integration | Parallel-run parity |
| 5 | Data migration + reconciliation | App team + Data | Row/message counts reconciled |
| 6 | Non-prod validation + security review | App team + Security | Pen-test / review pass |
| 7 | Cutover + hypercare + DR drill | Cloud-Ops + Clinical | SLO met, rollback rehearsed |
| 8 | Handover to run + FinOps tagging | Cloud-Ops / FinOps | Tags, budget, run-book complete |
Architecture decision records
Every load-bearing decision in this design is recorded as an ADR so that six months from now, when someone asks “why is AWS not allowed its own IdP?” the answer is a document, not a hallway argument. The table captures the decision, the context that forced it, the options weighed, the choice, and the consequence Meridian Health accepts. These ten are the spine; the ARB register holds the full set.
| ADR | Decision | Context | Options considered | Choice & rationale | Consequence / trade-off |
|---|---|---|---|---|---|
| 001 | Entra ID as the single identity hub | 55k staff, M365 E5, AD DS forest, AWS + SaaS estate | (a) Okta as broker (b) per-cloud IdP © Entra as hub | © — reuse E5, PHS+SSO, PIM; AWS federates via OIDC/SAML | One trust anchor to harden; AWS loses local IdP autonomy |
| 002 | Two-region active/active for Tier-1 | RTO ≤30m / RPO ≤5m on EHR-adjacent, ADT, meds | (a) active/passive (b) pilot-light © active/active | © — meets RPO with live replicas, no cold-start risk | ~2× Tier-1 compute; requires conflict-free data design |
| 003 | Cloud-hosted VNA as imaging system of record | 2.3 PB, 9 imaging centres, prior-study recall | (a) keep on-prem PACS SoR (b) hybrid © cloud VNA SoR | © — one archive, lifecycle economics, viewer anywhere | Migration risk; on-prem PACS becomes edge cache |
| 004 | Private-endpoint / Private Link mandate for PHI PaaS | HIPAA, no PHI on public endpoints | (a) service endpoints (b) firewall allow-lists © PE-only + deny public | © — deterministic, policy-enforced isolation | ~600 PEs, Private DNS complexity, cost per endpoint |
| 005 | Enterprise interface engine (Rhapsody) as backbone | HL7 v2 breadth, FHIR, X12, IHE, replay | (a) cloud-native iPaaS (b) build © Rhapsody HA | © — healthcare-grade parsing, HA, EU residency | License cost; specialist skills; not “just an API gateway” |
| 006 | Centralized de-identification into research-safe zone | 42 CFR Part 2, GDPR, trials, export audit | (a) per-project de-id (b) synthetic-only © central de-id pipeline | © — consistent pseudonymization, audited release | Central bottleneck; strong governance required |
| 007 | Dual-hub network, Firewall Premium secured hubs | vWAN + TGW, segmentation, IDPS | (a) NVA per spoke (b) 3rd-party FW © native secured hubs | © — managed scale, TLS inspection, less to run | Vendor-native; premium tier cost |
| 008 | CMK in Managed HSM (FIPS 140-3 L3), per-tenant keys | PHI encryption, key custody, HITRUST | (a) platform-managed (b) Key Vault CMK © Managed HSM CMK | © — highest assurance, tenant key isolation | HSM pool cost; key lifecycle operational load |
| 009 | Immutable WORM audit store for PHI access, 7-yr+ | HITECH access logging, legal hold | (a) standard retention (b) SIEM-only © WORM immutable + SIEM | © — tamper-evident, defensible in audit/litigation | Storage + ingest cost; retention discipline |
| 010 | LZ vending via Terraform + ServiceNow gated self-service | 180 apps, repeatable, auditable | (a) ClickOps (b) ticket-only © gated self-service | © — golden paths, policy-as-code, speed + control | Platform-product investment; module maintenance |
Risks, assumptions, issues and dependencies
The RAID log is the honest ledger — the things that can still go wrong, the beliefs the plan rests on, the problems already live, and the external hooks the timeline hangs from. It is reviewed at every ARB and CAB; each item has an owner and a response, because an unowned risk is just a surprise waiting for a date.
| ID | Type | Description | Impact | Prob. | Owner | Response | Status |
|---|---|---|---|---|---|---|---|
| R1 | Risk | EHR vendor limits active/active replication topology | High | Med | Clinical-Apps | Validate topology in W3 spike; fallback pilot-light for that app | Open |
| R2 | Risk | Sentinel ingest exceeds forecast, blows observability budget | Med | High | Security | Commitment tier + data-collection rules + archive tier | Mitigating |
| R3 | Risk | Unpatchable medical devices resist microsegmentation | High | Med | Network | NAC + isolated VLANs + FDA-aware exception register | Open |
| R4 | Risk | 2.3 PB imaging migration exceeds cutover window | High | Med | Imaging | Bulk seed via offline transfer + delta sync; phased by centre | Mitigating |
| R5 | Risk | De-id re-identification finding in research release | High | Low | Data-Research | Expert-determination + k-anonymity checks + export audit | Open |
| A1 | Assumption | Dual ExpressRoute/DX delivered on schedule by carriers | High | — | Network | Ordered M0; weekly carrier status to ARB | Tracking |
| A2 | Assumption | M365 E5 entitlements cover all 55k in scope | Med | — | Security | License true-up before W2 | Confirmed |
| A3 | Assumption | On-prem AD DS is clean enough for cloud sync | Med | — | Platform | Pre-sync hygiene + IdFix remediation | Tracking |
| A4 | Assumption | Rhapsody EU node satisfies GDPR data residency | High | — | Integration | DPIA + residency attestation in W3 | Tracking |
| I1 | Issue | Legacy HL7 v2 feeds use non-standard Z-segments | Med | — | Integration | Map Z-segments in engine; parallel-run reconcile | Active |
| I2 | Issue | Duplicate patient IDs across facilities (no EMPI) | High | — | Integration | Stand up PIX/PDQ + EMPI before results routing | Active |
| D1 | Dependency | HITRUST assessor availability for r2 timeline | Med | — | ARB | Book assessor at M12; interim readiness reviews | Tracking |
| D2 | Dependency | Carrier SD-WAN to 120+ sites for telemedicine QoS | Med | — | Network | Site rollout schedule aligned to W4 | Tracking |
| D3 | Dependency | Epic upgrade window aligns with W4 clinical go-live | High | — | Clinical-Apps | Joint change calendar, freeze coordination | Tracking |
Delivery roadmap and acceptance
The roadmap ties the waves to dated milestones and — critically — to the objectives in the executive proposal (Part 1), so the board can trace every rupee of spend to an outcome it approved. The milestones are the checkpoints finance releases funding against; the acceptance criteria are how “done” is proven, not asserted.
| Milestone | Month | Wave | Proposal objective served |
|---|---|---|---|
| Platform live, guardrails enforced (G1) | M5 | W1 | O8 Cost & operability, O1 Compliance |
| Entra sole IdP, PE-only PHI (G2) | M9 | W2 | O3 Unified identity, O4 PHI protection |
| Interop backbone in parallel-run (G3) | M14 | W3 | O5 Interoperability |
| First Tier-1 active/active go-live (G4) | M18 | W4 | O2 Zero-downtime Tier-1 |
| VNA is imaging SoR; research zone live (G5) | M22 | W5 | O6 Imaging at scale, O7 Research |
| Steady-state run, HITRUST r2 (G6) | M24 | W6 | O1 Compliance, O8 Operability |
Acceptance is deliberately measurable — every criterion has a threshold, a verification method, an owner, and the gate it belongs to. A criterion with no test is a wish, not an acceptance.
| Acceptance criterion | Target / threshold | Verification method | Owner | Gate |
|---|---|---|---|---|
| Policy guardrail coverage | ≥ 98%, 0 high-severity drift | Defender/Config posture scan | Security | G1 |
| Backbone availability | ≥ 99.95% | Synthetic + carrier SLA report | Network | G1 |
| Identity: standing admin | 0 (all JIT via PIM) | PIM audit + access review | Security | G2 |
| PHI public exposure | 0 public PaaS endpoints | Policy compliance + PE inventory | Platform | G2 |
| Message loss (interop) | 0 lost, 100% reconciled | Parallel-run count reconciliation | Integration | G3 |
| Tier-1 RTO / RPO | ≤ 30 min / ≤ 5 min | Live failover drill evidence | Clinical-Apps | G4 |
| Clinical safety | Signed safety case | Clinical safety board review | CMIO delegate | G4 |
| Imaging retrieval P95 | ≤ 3s (hot/cool) | Synthetic C-FIND/C-MOVE tests | Imaging | G5 |
| Research export audit | 100% audited, 0 un-approved | Export log + approval workflow | Data-Research | G5 |
| Compliance certification | HITRUST CSF r2 achieved | External assessor report | ARB | G6 |
| Cost lever attainment | ≥ 90% of modelled savings | FinOps actuals vs model | FinOps | G6 |
Finally, the traceability matrix closes the loop from proposal promise to design proof — each objective the board funded maps to the design part that delivers it and the test that confirms it. This is what turns a 26,000-word design document into an auditable commitment.
| Proposal objective | Delivered by (design part) | Proven by (acceptance) |
|---|---|---|
| O1 Compliance-by-default (HIPAA/HITRUST/GDPR/42 CFR) | Security & compliance, governance parts | Guardrail coverage; HITRUST r2 (G1, G6) |
| O2 Zero-downtime Tier-1 (RTO≤30m/RPO≤5m) | Resiliency & multi-region parts | Failover drill evidence (G4) |
| O3 Unified identity (Entra hub) | Identity part; ADR-001 | 0 standing admin, MFA 100% (G2) |
| O4 PHI protection (private, CMK, audit, break-glass) | Network/security parts; ADR-004/008/009 | 0 public PHI, CMK bound (G2) |
| O5 Interoperability (HL7/FHIR/DICOM/IHE/X12) | Integration part; ADR-005 | 0 lost messages, traceability (G3) |
| O6 Imaging at scale (cloud VNA, 2.3 PB) | Imaging part; ADR-003 | C-FIND/C-MOVE parity, P95 ≤3s (G5) |
| O7 Research enablement (de-id, trials isolation) | Data & research part; ADR-006 | 100% audited export (G5) |
| O8 Cost & operability (FinOps, single op model) | This part (cost, operating model) | Lever attainment ≥90% (G6) |
Taken together, the cost model gives finance a defensible number, the bill of materials gives procurement a buy-list, the operating model and RACI give the pods a contract, the waves and roadmap give the program a dated path with hard gates, the ADRs record why every load-bearing call was made, and the RAID and acceptance criteria make “done” measurable — the difference between an architecture diagram and a landing zone that goes live, on time and through its HITRUST certification, for a 14-hospital integrated delivery network carrying real patients on Tier-1.