Architecture Multi-Cloud

Secure Multi-Cloud Landing Zone and Enterprise Architecture for Healthcare: A Complete Azure + AWS Design

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.

Meridian Health target-state: on-prem data centres reach Azure and AWS landing zones over dual ExpressRoute and Direct Connect circuits, with a hardened public-in/private-out patient edge

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.

Meridian Health domain segmentation: five isolated landing zones — Clinical, Imaging, Telemedicine, Research, Corporate/Integration — each with its own CIDR and default-deny east-west, connected only by brokered HL7/FHIR/IHE/X12 contracts

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.

Azure Healthcare Landing Zone — mh management groups, platform subscriptions and clinical/shared landing-zone subscriptions

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.

AWS Healthcare Landing Zone — Organizations OU tree with Security, Infrastructure and workload accounts governed by Control Tower and SCPs

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.

Governance hierarchy and policy inheritance — root to platform to landing zone to workload, with Deny, DeployIfNotExists and Audit effects across Azure and AWS

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.

Compliance and control mapping — HIPAA, HITRUST, GDPR and NIST 800-66 mapped to Azure and AWS controls and their enforcing policies

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.

Meridian Health global hybrid connectivity: three on-premises data centres (Ashburn, Chicago, Dublin running the EHR, PACS and corp AD forest) plus 120-plus SD-WAN sites, homing through dual ExpressRoute (East US 2 + West Europe, 10G ExpressRoute Direct with MACsec) and dual Direct Connect (us-east-1 + eu-west-1, 2×10G LAG with MACsec) into the Azure Virtual WAN secured hubs (routing intent, ErGw3AZ with FastPath and Global Reach) and the AWS Transit Gateway backbone (one per region, RAM org-shared, fronted by a Direct Connect Gateway with allowed-prefixes), finally reaching the Tier-1 active/active Azure and AWS workload regions — with six numbered badges marking single-circuit-no-SLA, unencrypted PHI on the wire, ExpressRoute not transiting to AWS, DXGW over-advertisement, sites stranding on hub failure, and data-residency crossings

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.

Meridian Health Azure regional network in East US 2: a secured Virtual WAN hub (10.20.0.0/20, routing intent for private and internet traffic, Azure Firewall Premium doing TLS inspection and IDPS) fronting the clinical spoke (10.20.16.0/22, EHR/ADT/results, Tier-1) and imaging spoke (10.20.20.0/22, PACS/VNA/DICOM), the research spoke (10.20.24.0/22, trials and de-identification) and business spoke (10.20.28.0/22, ERP and analytics), a private-PaaS zone with a /26 Private Endpoint subnet (10.20.19.0/26) and hub-linked privatelink DNS, and an egress/observe plane enforcing NSG+UDR forced tunnelling (0/0 to firewall, deny inter-spoke) with NSG flow logs and firewall logs into Sentinel — five badges mark egress bypassing the firewall, clinical-to-research routing, mis-sized PE subnets, unlinked private DNS, and missing flow logs

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.

Meridian Health AWS regional network in us-east-1: workload VPCs (clinical 10.40.16.0/22 for EHR integration, imaging 10.40.20.0/22 for VNA and DICOM) default-routing 0/0 into a Transit Gateway (one per region, RAM org-shared, appliance mode on) whose route domains keep clinical, imaging, research and corp from cross-propagating, hairpinning through a centralized inspection VPC (100.64.0.0/16) running AWS Network Firewall with Suricata STRICT-order rules and NAT+IGW egress, alongside interface endpoints (PrivateLink for ECR, Secrets Manager, KMS, API) and gateway endpoints (S3, DynamoDB, no NAT bill), plus a hybrid-DNS-and-logs plane with Route 53 Resolver inbound/outbound endpoints and FORWARD rules and CloudWatch/S3 flow and firewall logs — six badges mark asymmetric drops, missing domain segmentation, allow-list leaks/bypass, S3-on-the-NAT-path, publicly reached PaaS, and unresolvable hybrid DNS

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.

Meridian Health IPAM carve-down: three non-overlapping /12 super-nets (on-prem 10.0.0.0/12 for AD/EHR/PACS, Azure 10.20.0.0/12 as a vNet Manager IPAM pool, AWS 10.40.0.0/12 in VPC IPAM) splitting into per-region /16s (East US 2 10.20.0.0/16, us-east-1 10.40.0.0/16, US-in-US and EU-in-EU), each region reserving its first /20 for the hub (vWAN hub 10.20.0.0/20 with gateway/firewall/DNS/Bastion; inspection VPC 10.40.0.0/20 plus 100.64/16 dark space), handing segments contiguous /22 spokes (clinical .16.0/22, imaging .20.0/22, research .24.0/22), down to leaf subnets (Private-Endpoint /26 at 10.20.19.0/26 with 5 reserved in Azure, and /28 TGW-attach subnets per AZ) — six badges mark cross-cloud non-overlap, one-/16-per-region, hub-/20-reserved-first, contiguous-/22-segments, /26-PE-reserved-IP math, and /28-attach-in-every-AZ

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.

Meridian Health private connectivity for PHI PaaS: a clinical Azure app (in-spoke, no public egress) and an AWS workload VPC app resolving via private DNS (Azure privatelink.database.windows.net zone; Route 53 private zone with split-horizon endpoint DNS) to a Private Endpoint (NIC at 10.20.19.x, subresource-scoped) or interface endpoint (ENI with an endpoint policy), reaching PHI PaaS (Azure SQL/Blob/Health Data Services; S3/RDS/DynamoDB/HealthLake with a KMS CMK) over the backbone only — while a guardrail plane (Azure Policy denying publicNetworkAccess; SCP/RCP data perimeter denying public S3) blocks the public path as a dashed deny — six badges mark DNS still returning a public IP, wrong subresource group-id, an over-open endpoint policy, PHI without a CMK or residency, a resource re-enabling public access, and S3 public despite endpoints

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:

Meridian Health identity control plane: on-prem AD DS forest synchronises via Entra cloud sync into the Entra ID hub, where Conditional Access and PIM gate every token before Entra federates as the sole IdP to AWS IAM Identity Center, SaaS SSO, and clinical relying parties (Epic EHR, PACS/VNA), with two excluded break-glass accounts

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:

Meridian Health clinician digital workplace: workforce personas (clinician via badge tap-and-go, corp/remote via laptop) prove Intune compliance and Defender device risk, pass a Conditional Access gate keyed to persona, device and risk, then reach M365 productivity apps and Tier-1 clinical apps (Epic EHR, PACS imaging) with session controls on PHI

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:

Meridian Health Conditional Access and PIM: user/device/app/network signals feed the numbered CA policy set (CA001-CA015), which emits grant and session controls; the privileged path requires phishing-resistant auth then routes admins to PIM eligible roles activated just-in-time with MFA, approval and an 8-hour cap, audited to Sentinel

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 devicescannot 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:

Meridian Health endpoint and UEM: device classes (corp laptops, shared clinical workstations, physician mobile) enrol via Intune Autopilot/ADE/shared-mode, prove compliance and Defender EDR posture, then pass a device Conditional Access gate to reach PHI, while biomedical devices that cannot take an agent are diverted to Defender for IoT passive monitoring and NAC network segmentation

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:

Meridian Health global edge and ingress: patient resolves via geo/health-aware DNS (Traffic Manager + Route 53) to the nearest CDN edge (Front Door Premium for US, CloudFront for EU), where the WAF inspects in prevention mode, then reaches origin over Private Link with an edge-ID header and mTLS so the active/active portal app and FHIR API have no public inbound path

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.

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:

  1. 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.
  2. 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.
  3. 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.

Meridian clinical systems: care delivery to cloud EHR engagement tier, private ExpressRoute link to the on-prem Epic system of record, ancillary pharmacy/LIS/RIS system-of-record apps, and a CMK-encrypted PHI store with immutable break-glass audit

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.

Meridian interoperability: HL7 v2 source systems into a Rhapsody interface engine with a persistent replay store, bridging to a FHIR/SMART API gateway and a Service Bus event mesh feeding FHIR, population-health and payer/HIE consumers, with an end-to-end message trace and a dead-letter/replay queue

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.

Meridian imaging: modalities with DICOM modality worklist into a DICOM router over Private Link, to cloud PACS reading and RIS reporting, archived into a vendor-neutral archive across hot/warm/cold/immutable-archive Blob tiers, and streamed to zero-footprint and EHR-launched viewers via WADO-RS

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.

Meridian telemedicine: patient app through secure edge and media SFU to FHIR EHR write-back, with consent gating and active/active HA

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.

Meridian medical IoT and RPM: devices and wearables through a segmented VLAN and edge gateway to IoT ingest, then clinical alert routing and analytics

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.

Meridian data platform: sources and ingest into medallion lake zones (raw to curated to governed to research-safe) with a Purview and Lake Formation governance 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.

Meridian research and trials: isolated account and de-id pipeline feeding approved datasets into a sealed researcher workspace with export audit

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 7-layer zero-trust security model for PHI

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)

Azure network security around the clinical spoke

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

AWS network security around the clinical workload VPC

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.

Zero-downtime release patterns for a Tier-1 clinical service

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.

Active-active data topology: Region A to Region B replication and conflict handling

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.

Disaster recovery tiers 0-3 with RTO/RPO and failover mechanism

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.

Azure paved-road onboarding: a ServiceNow intake is classified by tier and PHI data class, a Terraform run vends the mh-lz-clinical-prod subscription with a /22 spoke, a /26 private-endpoint subnet and an HSM-backed CMK, Azure Policy denies any public exposure, and a HITRUST/SOC-2 go-live gate blocks promotion until evidence is attached

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.

AWS paved-road onboarding: intake drives Account Factory for Terraform to vend mh-clinical-web-prod into the Workloads/Clinical/Prod OU, a baseline provisions a /22 VPC on Transit Gateway with PrivateLink endpoints and a per-account KMS CMK, SCPs and AWS Config enforce guardrails, and a HIPAA go-live gate matches the Azure bar

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.

SAP S/4HANA landing zone: Fiori users enter via Application Gateway WAF and Entra SSO, a clustered ASCS/ERS app tier fronts a HANA primary with synchronous HSR to a second availability zone, an asynchronous DR replica and immutable Backint backups provide resilience, and the platform integrates to clinical and finance systems as X12 837/835 and IDoc/BAPI-to-FHIR

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.

Terraform and Ansible multi-stage CI/CD: repo-per-module and repo-per-app sources feed a single extends-based YAML template with a private Artifacts feed, a plan stage runs tfsec/checkov and an OPA/cost policy gate, apply runs on self-hosted VMSS runners with Ansible finishing OS and app configuration, and promotion flows dev to UAT to staging to prod behind manual 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.

DevSecOps software supply chain: a signed commit on a protected branch passes SAST, SCA and secret scanning, a distroless image is built to SLSA level 3 and signed with cosign plus a Syft SBOM, ACR/ECR re-scans with Trivy and Defender gating on CVE and VEX, and a Ratify/Kyverno admission controller admits only images whose signature and provenance verify into the AKS/EKS 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.

Observability to SIEM to ITSM: app/APM, infrastructure/network and patient-portal/telemedicine UX emit OpenTelemetry to a collector that tail-samples and scrubs PHI, logs and metrics land in Log Analytics and CloudWatch, security signals feed Microsoft Sentinel where a 24x7 SOC triages, and an analytics-rule detection fires a SOAR playbook and opens a ServiceNow incident with MTTR tracking

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.

Meridian Health delivery roadmap: six migration waves across 24 months — foundation, identity and network, interoperability, clinical, imaging/data/research, and run/optimize — each wave a zone with milestone gates 1 through 6 marking the go/no-go stage-gates between phases

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.

HealthcareLanding ZoneMulti-CloudAzureAWSHIPAAZero TrustInteroperability
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading