Multi-Cloud and Hybrid Cloud Architecture Questions
Designing systems that span multiple cloud providers or bridge cloud and on-premises. Covers cloud-agnostic abstraction, workload placement across providers or environments, cross-cloud networking and identity federation, data gravity, infrastructure-as-code and centralized observability that span providers, and the operational cost of avoiding vendor lock-in versus the risk of accepting it. Also covers keeping a system correct once it spans providers: leader election, distributed transactions, rate limiting, and service discovery across cloud or cluster boundaries. Resilience patterns here are scoped to crossing a provider or on-prem/cloud boundary (for example failover from one provider to another, or from on-prem to cloud). Resilience across regions of a single provider, with no second provider or on-prem leg involved, is a different topic (multi-region architecture) and is out of scope here.
Design a hybrid network architecture for an enterprise web application where the web tier runs in AWS across two regions and the primary database remains on-premises. Requirements: low-latency DB access, high availability, secure admin access from corporate, and cost-efficiency. Provide a topology diagram description, choice of connectors, routing/routing policies, and potential failure modes and mitigations.
Sample Answer
Direct answer
Put the two AWS regions' web tiers behind global, latency-aware DNS routing, connect both regions to the on-prem database over redundant Direct Connect links (each with a VPN as backup), and route admin access through a separate, narrowly-scoped path rather than the same connection the application uses, so a mistake in application-layer routing cannot accidentally expose administrative access and vice versa. Low-latency database access from two regions to one on-prem database is fundamentally a single-writer problem: both regions are, at best, equally far from the one database, so the design's job is minimizing and bounding that distance, not eliminating it, while high availability (HA) comes from redundant paths and automatic failover rather than from having two database copies.
Structured elaboration (topology)
flowchart LR
subgraph RegionA["AWS Region A"]
WebA[Web tier ASG] --> ConnA[Direct Connect / VPN]
end
subgraph RegionB["AWS Region B"]
WebB[Web tier ASG] --> ConnB[Direct Connect / VPN backup]
end
ConnA --> Router[On-prem edge router]
ConnB --> Router
Router --> DB[(Primary database, on-prem)]
Corp[Corporate network] -->|site-to-site VPN, admin only| Router
R53[Global DNS / latency routing] --> WebA
R53 --> WebB
- Web tier: an Auto Scaling group per region, each fronted by a regional load balancer, with Amazon Route 53 (or an equivalent global DNS/traffic-management layer) doing latency-based routing so users land on whichever region is currently closer and healthy.
- Connectivity to on-prem: each region has its own Direct Connect connection to the on-prem data center (ideally terminating at a different on-prem edge device for physical redundancy), with a site-to-site VPN as an automatic backup path if a region's Direct Connect link fails; both links carry only application database traffic.
- Admin access: a separate path, for example a bastion host or VPN profile scoped only to the specific management ports and source IP ranges corporate admins use, kept logically and, ideally, physically distinct from the application's Direct Connect path, so a compromise or misconfiguration of one does not automatically grant reach on the other.
- On-prem edge: a router/firewall terminating both regions' Direct Connect links (or VPN backups), presenting the database to both regions and enforcing which specific application subnets in each region are allowed to reach it.
Connector choice. Direct Connect (or the equivalent dedicated interconnect) is preferred over VPN for the primary path specifically because this is a latency-sensitive, sustained-traffic pattern (every web request potentially touching the database), where a private, more consistent-latency path is worth the added cost and lead time over VPN; VPN remains valuable as the automatic failover for either region's Direct Connect link, accepting higher and more variable latency only during a failure.
Routing and routing policy. Route 53 latency-based routing directs a given user to whichever region's load balancer currently has the lower measured latency to that user, with health checks removing an unhealthy region from rotation. Within each region's VPC (Virtual Private Cloud, its isolated network segment in that AWS region), route tables send database-bound traffic out the region's own Direct Connect virtual interface as the primary path, with a route to the VPN gateway configured as the automatic backup, typically achieved with a lower BGP (Border Gateway Protocol, the protocol routers use to exchange and choose between routes) local-preference or a longer AS-path (the recorded list of network segments, or autonomous systems, a route has crossed; routers prefer a shorter list) advertisement on the VPN so it is only preferred when Direct Connect is down.
Worked example
A retail web application runs its web tier in us-east-1 and us-west-2, both querying the same on-prem PostgreSQL primary in a data center in the central United States. Route 53 latency-based records send East Coast users to us-east-1 and West Coast users to us-west-2; both regions typically see database round-trip latency in the low tens of milliseconds given the central location, and this is accepted as the cost of a single on-prem writer rather than engineered away, since a true multi-writer database would introduce its own consistency problems this design deliberately avoids. Each region has its own Direct Connect connection into the data center, landing on a different physical router for redundancy, with a VPN configured as backup on both; BGP advertises the Direct Connect path with a shorter AS-path so it is always preferred when up. Admin database access uses a separate bastion host reachable only from the corporate VPN, on a distinct security-group rule from the application's database port, so a security-group misconfiguration on the application path cannot accidentally also expose the admin path.
Failure modes and mitigations:
| Failure | Mitigation |
|---|---|
us-east-1's Direct Connect link fails | Traffic automatically shifts to that region's VPN backup via BGP preference; users experience higher latency, not an outage |
| The on-prem database itself fails | Both regions lose write capability simultaneously since there is one database; mitigate with on-prem HA (a standby replica with fast local failover) rather than trying to solve this at the network layer, since no amount of network redundancy compensates for a single logical database being down |
| One region's web tier fails entirely | Route 53 health checks remove it from rotation; the other region continues serving all users, at the cost of that region's database connections now carrying full traffic, which capacity planning needs to account for up front |
| Both Direct Connect and VPN fail for one region simultaneously | That region cannot reach the database at all; Route 53 health checks (checking actual database reachability, not just the load balancer) should catch this and redirect users to the other region rather than serving broken requests |
Trade-offs & pitfalls
- Treating "two regions" as solving high availability for the database is the most common mistake in this design: it solves web-tier HA, but the single on-prem database remains a single point of failure for writes from both regions, which needs its own, separately engineered HA story.
- Health checks that only verify the load balancer's health, not actual end-to-end database reachability from that region, will keep sending traffic to a region that can serve HTTP responses but cannot actually complete a database-backed request.
- Cost efficiency pushes toward fewer, larger Direct Connect ports; the redundancy requirement pushes toward per-region dedicated links, and the design above chose redundancy per region, which costs more than a single shared link but avoids one region's traffic saturating a link meant for the other region during a failover.
- Keeping admin access on a genuinely separate path adds operational overhead (a second set of credentials, a second connection to maintain) that is easy to erode over time if teams start routing admin traffic over the application path "just this once"; this needs to be enforced by network policy, not just documented as an intention.
Design-time tradeoffs: An engineering team asks whether to use cloud-managed object storage replication features across providers or implement application-level replication to achieve cross-cloud durability. List the pros and cons of both approaches, and recommend when application-level replication is preferable.
Sample Answer
Direct answer
Cloud-managed object storage replication (Amazon S3 Cross-Region Replication, Google Cloud Storage dual-region buckets with turbo replication, Azure geo-redundant storage) is operationally simpler and more reliable than building your own replication, and should be the default whenever the replication target is a second region or zone within the same cloud provider. The decision flips toward application-level replication as soon as you need something the managed feature structurally cannot do: true cross-cloud durability (replicating to a different vendor entirely, which none of the major providers' native replication features support, they only replicate within their own service), custom conflict resolution logic, selective or filtered replication of a subset of objects, or a consistency guarantee tighter than what the managed feature publishes.
Structured elaboration
What managed replication actually gives you, and its hard boundary
Managed object-storage replication is a same-vendor feature: S3 Cross-Region Replication moves objects between S3 buckets, GCS dual-region replication moves objects between GCS locations, Azure geo-redundant storage replicates within Azure. None of them replicate from one cloud provider's object storage into a different provider's object storage. This is the single fact that decides most of this trade-off for a genuinely multi-cloud durability requirement: if "durability across providers" is the actual goal (protecting against one vendor's outage or an unrecoverable account-level incident with that vendor, not just a regional outage within it), managed replication cannot be the whole answer no matter how good it is within its own provider, application-level replication (or a third-party cross-cloud sync tool sitting on top of both providers' application programming interfaces, APIs) is required to bridge providers.
Where managed replication is the right default
- Operational simplicity: enabled via a configuration setting, no code to write or maintain, and the provider owns the replication pipeline's reliability, monitoring, and scaling.
- Published, testable service-level agreements (SLAs): for example, Google Cloud Storage's turbo replication gives a contractual 15-minute recovery point objective (RPO) for dual-region buckets, versus the default replication tier's best-effort target of about one hour for 99.9% of objects (with no hard guarantee), a concrete, provider-backed number you can build a disaster-recovery plan around without writing or testing replication code yourself.
- Correctness by construction: the provider has already solved conflict handling, ordering, and retry logic for its own replication path, which is exactly the class of bug that is easy to get subtly wrong in a homegrown replicator (partial writes on retry, dropped objects on transient failures, replication lag that silently grows unbounded).
Where application-level replication is preferable
- True cross-cloud durability: as above, this is the case managed replication simply does not reach.
- Custom conflict resolution: if two regions (or two providers) can both accept writes to the same object key and you need domain-specific merge logic (for example, a last-writer-wins policy based on an application timestamp rather than storage-system arrival order, or a merge of two partial updates), managed replication gives you none of that, it is a straight copy.
- Selective or transformed replication: replicating only objects matching a business rule (only orders above a certain value, only records tagged for a specific jurisdiction for a residency requirement) rather than an entire bucket.
- A tighter consistency need than the managed SLA offers: if the business genuinely needs a lower RPO than the provider's fastest replication tier guarantees, the only way to get there is a custom pipeline (for example, synchronous dual-write from the application to both locations at write time, accepting the added write latency that comes with it) rather than waiting on any asynchronous managed replication path.
Worked example: the same trade-off for managed global database services
The identical managed-versus-application-level question shows up one layer up the stack for global databases, with an added backup and vendor-lock-in dimension. Google Cloud Spanner (a fully managed, globally distributed relational database) offers built-in, strongly-consistent multi-region replication; Amazon Aurora Global Database offers cross-region replication with sub-second typical replica lag and fast promotion for disaster recovery. Both are excellent, fully managed answers to "replicate my database across regions", and both share the exact same boundary as object storage replication: Spanner's multi-region replication stays inside GCP, Aurora Global Database's stays inside AWS. Neither offers a managed path to a true multi-cloud active database spanning, say, GCP and AWS simultaneously. A team that needs a database replicated across two different cloud vendors is choosing between application-level replication using a database built for it (CockroachDB or a similar distributed SQL database designed to run its own consensus protocol across arbitrary network locations, including different clouds) or accepting a weaker guarantee: single-cloud managed replication for day-to-day resilience, plus a separate, periodic cross-cloud backup and restore process (not live replication) as the disaster-recovery path of last resort if the entire primary provider becomes unavailable.
This also reframes the vendor-lock-in angle: choosing Spanner or Aurora Global Database for its managed replication convenience is simultaneously a commitment to that provider for the database layer, since the moment you rely on the specific consistency and replication behavior those services provide, migrating off means re-architecting the replication and consistency model, not just moving data. That lock-in is a reasonable trade for most teams (rebuilding Spanner's consistency guarantees yourself is a large undertaking), but it should be named explicitly as the cost of taking the managed path, not discovered later during a migration attempt.
Trade-offs & pitfalls
- Assuming "cross-region replication" means "cross-cloud replication" is the most common misunderstanding in this space, and it usually surfaces at the worst time, during an actual provider-level incident, when the team discovers the managed replication they relied on never left the affected provider.
- Building application-level replication without also building its monitoring (replication lag, failed-write alerting, a way to detect silent divergence between the two copies) recreates the exact operational risk that made managed replication attractive in the first place, the point of "application-level" is to gain a capability the managed feature lacks, not to accept a worse-monitored version of what it already does well.
- For the database case specifically, treating a periodic cross-cloud backup and restore as equivalent to real replication understates its recovery time objective (RTO), a restore from backup is measured in the time it takes to provision and load a new database instance, not the near-instant failover a live replica offers, that gap should be sized and stated explicitly in any disaster-recovery plan relying on it.
- Custom conflict resolution logic is exactly the kind of code that looks correct in testing and fails under a real concurrent-write race, it deserves the same rigor (explicit test cases for concurrent writes to the same key) as any other correctness-critical distributed-systems code, even though it is easy to underrate as a minor cleanup step rather than core logic.
Design a cost-sensitive hybrid compute strategy that runs predictable baseline ETL on on-prem servers and bursts to cloud for weekly peaks. Define autoscaling triggers, data movement and staging strategies to minimize egress, identity and access patterns spanning both environments, and cost controls to prevent runaway cloud spend while ensuring result consistency between environments.
Sample Answer
Direct answer
Trigger cloud bursts on backlog or queue depth, not a fixed calendar time alone, since weekly peak volume varies. Ship only the incremental change set to the cloud rather than re-exporting the full dataset every burst. Federate identity from the existing on-prem directory so burst capacity never needs its own standing cloud credentials. Enforce a hard spend ceiling with automated guardrails rather than relying on someone noticing the bill. Consistency between the on-prem baseline and the cloud burst path comes from one shared checkpoint both sides read, plus a reconciliation check before either output is trusted downstream, not from hoping the two never overlap.
Architecture
flowchart LR
subgraph ONPREM["On-prem: baseline ETL"]
A[Scheduled baseline ETL] --> B[CDC delta extractor]
end
B -->|compressed delta only| C[Cloud staging bucket]
subgraph CLOUD["Cloud: burst capacity"]
C --> D[Autoscaled burst workers]
D --> E[Shared checkpoint store]
D --> F[Burst job output]
end
F -->|single final write| G[(Downstream on-prem warehouse)]
E -.->|watermark read by both sides| A
Framework
Autoscaling triggers
- Primary trigger: extract-transform-load (ETL) job backlog depth, for example more than 500 jobs queued for more than 5 minutes, rather than CPU or a fixed schedule alone, because the signal that actually matters is "on-prem baseline capacity can't keep up," and backlog depth measures that directly.
- A scheduled trigger also runs in parallel for the known weekly peak window, pre-warming burst capacity just before the predictable volume arrives, since waiting for backlog to build before scaling adds latency exactly when it's foreseeable.
- Scale-in trigger: backlog at zero for a sustained grace period, for example 15 minutes, long enough to avoid flapping (scaling down and immediately back up) on a momentary lull.
Data movement and staging to minimize egress
- Use change-data-capture (CDC, tracking only the rows that changed since the last run) to ship a compressed delta to a cloud staging area, rather than re-exporting the full baseline dataset for every burst.
- Keep intermediate working data in the cloud for the duration of the burst job; write the final result back to on-prem, or wherever downstream consumers live, once at the end, rather than shuttling partial results across the boundary repeatedly.
Identity and access spanning both environments
- Federate the existing on-prem directory into the cloud's identity and access management (IAM) system through the Security Assertion Markup Language (SAML) or OpenID Connect (OIDC), both standards that let a user or service prove its identity once to the on-prem directory and have that proof trusted by the cloud side, rather than typing a separate cloud password, as the single source of truth for who, or what service, is allowed to act. Don't create separate, permanent cloud-only accounts for burst capacity.
- Burst workers assume short-lived, scoped roles tied to the burst job's own lifecycle, created when the job starts and expired when it ends, so no standing credential in cloud IAM outlives the workload it was created for.
Cost controls to prevent runaway spend
- A hard budget and quota caps both the maximum concurrent burst instances and the maximum monthly burst spend; this is an enforced limit, not a dashboard someone has to remember to check.
- Automated spend alerts fire at defined thresholds, for example 50%, 80%, and 100% of the monthly burst budget, and page an on-call owner, not just log to a report nobody reads until month-end.
- Org-level guardrail policies (the equivalent of a cloud provider's organization-wide policy controls) hard-block instance types, regions, or resource categories outside the approved burst pool, so a misconfigured job can't silently provision, say, an expensive GPU fleet that the budget alert would only catch after the fact.
Ensuring result consistency between environments
- Every job is idempotent, keyed by a deterministic batch/partition identifier, so a rerun after a scale event, a retry, or a burst worker eviction overwrites its own prior output instead of duplicating it.
- A single checkpoint, stored in one place that both the on-prem baseline job and the cloud burst workers read and update, prevents the two from ever processing the same window twice.
- Before downstream consumers read either output, a reconciliation job compares row counts and checksums between the on-prem baseline result and the cloud burst result for the same period, catching a silent mismatch before it propagates.
Worked example
Quantify the egress benefit of shipping deltas instead of the full dataset, with assumptions stated explicitly: a 2TB weekly dataset with an 80GB weekly change set (a 4% change rate), at an illustrative egress price of $0.05/GB. Real egress pricing varies by provider and contract; this comparison is about which staging strategy is cheap, not a budget figure.
Full re-export every burst:
2000 GB×$0.05/GB=$100/week
Delta-only, CDC-based, shipping:
80 GB×$0.05/GB=$4/week
100/4=25
100100−4=0.96
Shipping only the delta cuts egress cost to roughly a twenty-fifth, a 96% reduction, of the full re-export approach, for the same weekly burst. That is before counting the compute cost of reprocessing 2TB instead of 80GB every week, which typically dwarfs the egress difference itself; the CDC-based staging strategy above is what makes both numbers small at once.
Trade-offs and pitfalls
Sizing the budget guardrail as a monthly cap alone, with no per-run or per-instance-type limit, lets a single misconfigured job burn the entire month's budget in one burst before anyone can react; layer a per-run cap under the monthly one. Treating the shared checkpoint as "usually fine because the schedule doesn't normally overlap," instead of enforcing it as a single, atomically-updated value, is the most common source of the double-processing this design is built to prevent, particularly right after an autoscaling event when an old worker finishing up and a new worker starting can briefly both be active. Finally, granting burst workers a broad, long-lived role "to keep the identity setup simple" defeats the point of federating identity in the first place: the short-lived, job-scoped credential is what keeps a burst worker's blast radius, meaning how much a single leaked or misused credential could actually reach, bounded to the burst window, not the federation itself.
Distributed transactions: Your business requires a supply chain workflow that updates inventory across services hosted in different clouds. ACID transactions are not possible end-to-end. Propose an architecture and patterns (e.g., sagas, idempotency, compensation) to ensure eventual correctness and how you would test these patterns.
Sample Answer
Direct answer
I would implement this as a choreographed or orchestrated saga: a sequence of local transactions, each in its own cloud/service, where every step either succeeds and moves the workflow forward or triggers a compensating action that undoes the effect of prior steps, all keyed by a single saga ID so retries are safe. Since ACID (atomicity, consistency, isolation, durability) transactions cannot span three separately-hosted services, correctness comes from designing every step to be idempotent and every failure to have a defined compensation, not from pretending a distributed transaction exists when it doesn't.
Structured elaboration
Saga pattern, orchestrated vs choreographed: an orchestrated saga has a central coordinator that calls each service in sequence and explicitly triggers compensations on failure, easier to reason about and to test since the whole workflow's logic lives in one place, at the cost of that coordinator being a component every step depends on. A choreographed saga has each service react to the previous step's event and publish its own completion or failure event, no central coordinator, which avoids that single dependency but makes the overall workflow's state harder to observe and debug since it's implicit in the sum of published events rather than explicit in one place. For a supply chain workflow spanning three clouds, I'd lean orchestrated: the cross-cloud network path already adds enough indirection that keeping the workflow logic centralized and inspectable is worth the coordinator dependency.
Idempotency: every step must be safe to retry, since a network partition between clouds means you frequently cannot tell whether a prior call actually succeeded or the response was merely lost. Each request carries an idempotency key (the saga ID plus a step identifier); the receiving service checks whether it has already processed that exact key before applying the operation again, so a retried ReserveStock call after a timeout either finds the reservation already made (no-op, return the prior result) or genuinely makes it once, never double-reserving stock.
Compensation: for every forward step, define its inverse explicitly, upfront, as part of the same design, not bolted on after a failure is discovered in production. Reserving stock compensates with releasing stock; scheduling a shipment compensates with cancelling it. Compensations must themselves be idempotent for the same reason forward steps are: a compensation might also need to be retried.
Eventual correctness: the workflow does not guarantee that inventory and shipping are consistent at every instant (there's a real window where stock is reserved but shipment hasn't been scheduled yet), it guarantees that the workflow always reaches one of two final states: fully completed, or fully compensated back to the starting state, with no state where inventory is decremented but the order was actually cancelled and nobody ever released it.
Testing these patterns: this is not something to reason about only on paper. Concretely: unit test each step's idempotency directly (call the same operation twice with the same idempotency key, assert the second call is a no-op with the same result as the first); integration test the full saga's happy path and every single-step-failure path explicitly (inject a failure at each step in turn and assert the correct compensation chain fires and the system ends in the fully-compensated state); and run chaos-style tests that inject network partitions or timeouts between the specific service calls to confirm retries and idempotency checks behave correctly under real failure conditions, not just under a mocked "the call failed" branch that never actually exercises the retry path.
Worked example
sequenceDiagram
participant OrderSvc as Order Service (Cloud A)
participant InvSvc as Inventory Service (Cloud B)
participant ShipSvc as Shipping Service (Cloud C)
participant Ledger as Saga Log (event store)
OrderSvc->>Ledger: append OrderCreated (sagaId, idempotencyKey)
OrderSvc->>InvSvc: ReserveStock(sagaId, sku, qty)
InvSvc->>Ledger: append StockReserved or StockReserveFailed
alt stock reserved
InvSvc->>ShipSvc: ScheduleShipment(sagaId)
ShipSvc->>Ledger: append ShipmentScheduled or ShipmentFailed
alt shipment scheduled
ShipSvc-->>OrderSvc: SagaCompleted
else shipment failed
ShipSvc->>InvSvc: CompensateReleaseStock(sagaId)
InvSvc->>Ledger: append StockReleased
InvSvc-->>OrderSvc: SagaCompensated
end
else stock reserve failed
InvSvc-->>OrderSvc: SagaAborted
end
An order triggers OrderCreated, logged to the saga's event store with a unique sagaId and idempotency key. The orchestrator calls Inventory Service's ReserveStock; if inventory reserves successfully, the orchestrator calls Shipping Service's ScheduleShipment. If shipment scheduling fails (say, no carrier capacity in that region), the orchestrator calls Inventory Service's CompensateReleaseStock, using the same sagaId so Inventory can look up exactly what it reserved for this saga and release precisely that, not just decrement a counter that might now be wrong if another saga has run in between. If a network timeout occurs between the orchestrator and Shipping Service (not a definite failure response, an unknown outcome), the orchestrator retries ScheduleShipment with the same idempotency key; Shipping Service checks whether it already has a shipment recorded for that key, and if so returns the existing result rather than scheduling a second shipment for the same order.
Trade-offs and pitfalls
The main cost of the saga pattern is that it trades strong consistency for availability and cross-cloud feasibility: there is a real window of partial completion visible to anything reading state mid-saga (an inventory dashboard might briefly show reduced stock for an order that ultimately gets cancelled), which needs to be an accepted, designed-for behavior, not a bug someone discovers later. The most common implementation mistake is defining the happy path and forgetting that a compensation can itself fail: what happens if CompensateReleaseStock times out? The answer has to be the same idempotent-retry discipline applied to compensations as to forward steps, plus a dead-letter path and alerting for a saga that's been stuck mid-compensation longer than expected, since an un-monitored stuck saga is a silent data-integrity problem waiting to be discovered by an angry customer rather than by the system itself.
Design a hybrid multi-cloud data platform: primary workloads in GCP, disaster recovery in AWS, and specialized compute in Azure. Discuss networking (interconnects, VPNs), identity federation, cross-cloud data replication, deployment automation, and how to test failover end-to-end.
Sample Answer
Direct answer
I would build this around a cloud-agnostic control plane and metadata layer sitting above cloud-specific data and compute services, not a single federated control plane trying to abstract away all three clouds uniformly: GCP holds the primary data platform (BigQuery plus GCS), AWS holds asynchronously-replicated data and standby compute for disaster recovery, and Azure is used only where it earns its keep with genuinely specialized compute (say, a specific ML/GPU offering), connected by dedicated interconnects, a shared identity layer, and one metadata catalog that knows where every dataset actually lives regardless of which cloud that happens to be. The design explicitly optimizes for avoiding lock-in on the components that are expensive or slow to move later (ingestion, orchestration, and the metadata layer), while accepting cloud-specific lock-in on components that would cost more to keep portable than they're worth (a managed data warehouse's proprietary query engine, for instance).
Structured elaboration
Networking: GCP to AWS uses a dedicated interconnect for high-throughput DR replication traffic (a VPN is a reasonable fallback for lower-volume links, but replication for a primary data platform's DR copy is exactly the kind of sustained, high-bandwidth traffic where a dedicated link pays for itself); GCP to Azure uses ExpressRoute (Azure's dedicated private network connection product, the equivalent of AWS Direct Connect or Google Cloud Interconnect) or a VPN depending on how much data actually needs to move to the specialized compute, since if Azure is only receiving small feature extracts for a specific ML workload rather than a full data mirror, a VPN's lower fixed cost may be entirely sufficient.
Identity federation: one central identity provider, federated into all three clouds' native workload identity systems using cross-cloud identity federation: short-lived, narrowly-scoped credentials per workload rather than long-lived keys duplicated across three separate IAM (identity and access management) systems that would drift out of sync with each other over time.
Cross-cloud data replication: BigQuery's data replicates to AWS asynchronously (scheduled export/import, or a continuous change-data-capture pipeline, depending on the acceptable recovery point objective for DR) rather than synchronously, because synchronous replication across a cross-cloud link would make every write in the primary platform wait on the DR copy's latency, which is rarely an acceptable trade for the DR benefit gained. The Azure leg is different in kind, not just degree: it's a federated export or query-time read of specific datasets the specialized compute needs, not a full mirror, since specialized compute typically needs a narrow slice of data, not the whole warehouse.
Testing failover end-to-end: run a scheduled, real failover game day, not a tabletop exercise, at a cadence tied to how often the primary/DR relationship or its dependencies change (quarterly is a reasonable floor for a platform this consequential). The drill has to exercise the full path a real DR event would take: verify the AWS replica's replication lag is within its stated recovery point objective at the moment of the drill (not assumed), promote the AWS replica to a standalone read/write copy, redirect a controlled slice of traffic or a shadow workload to query the promoted AWS copy and confirm results match what GCP would have returned for the same queries, and separately validate that the ingestion and orchestration layer can actually target AWS as the write destination without a manual, undocumented reconfiguration step. Measure the actual time from declaring the drill to the promoted replica serving correct results, and compare that measured time against the platform's stated recovery time objective, rather than assuming the design meets its target because the architecture diagram looks right. After the drill, demote AWS back to a replica and resync it from GCP, and treat any manual step that was needed during the drill as a defect to automate before the next one, since a failover runbook with an undocumented manual step is the thing most likely to fail during a real, higher-pressure incident. The Azure specialized-compute leg does not need its own failover drill in the same sense, since it is not the DR target, but its dependency on GCP-sourced data exports belongs in the drill's blast-radius check: confirm that a GCP-to-AWS failover does not silently break the federated exports Azure depends on, or if it does, that the runbook says so explicitly rather than leaving Azure consumers to discover it themselves.
Deployment automation: infrastructure as code (Terraform, with separate state per cloud as in the general multi-cloud Terraform pattern) provisions each cloud's resources, and a single CI/CD pipeline deploys the pipeline code (the ingestion and transformation jobs) uniformly, using the same orchestration tool's cloud-agnostic core (Airflow, Dagster, or a similar workflow engine) rather than each cloud's native orchestrator (Cloud Composer, AWS Step Functions, Azure Data Factory) as three separate, incompatible workflow definitions.
Avoiding lock-in: egress cost as a first-class design input. Every cross-cloud read or replication job has a real, recurring egress cost (the fee cloud providers charge for data leaving their network) that a single-cloud architecture never pays; this needs to be modeled explicitly (which datasets actually need to leave GCP, at what frequency, and at what size) rather than discovered as a surprise line item. The lock-in-avoidance angle and the egress-cost angle pull in the same direction here: minimizing what actually needs to cross cloud boundaries both reduces cost and reduces how deeply any one cloud's specific data-transfer mechanics get baked into the pipeline design.
Metadata catalog consistency: run one metadata catalog (a tool like a data catalog service configured to index across clouds, or an open table format's own catalog such as Iceberg's REST catalog) that tracks schema, lineage, and physical location for every dataset regardless of which cloud actually stores it. Without this, "where does dataset X actually live, and is the AWS copy the current version or three days stale" becomes a question someone has to answer by checking three consoles, which does not scale past a handful of datasets and becomes a real source of production incidents (a query run against a stale replica because nobody could quickly confirm which copy was current).
Single vs. federated control-plane orchestration: a single control plane (one orchestration tool's scheduler, deployed once, dispatching jobs into whichever cloud they need to run in) is simpler to operate and monitor than a federated model (each cloud running its own orchestrator, coordinated by conventions rather than a shared scheduler), and is the right choice here specifically because the workload split (primary, DR, specialized compute) is asymmetric rather than three equal peers; a federated model earns its complexity mainly when multiple clouds are genuinely equal, independently-operated peers, which is not this scenario.
The cloud-agnostic vs. cloud-specific component decision framework: for each component, ask two questions. First, is this component's replacement cost high (would swapping it later require rewriting significant application or pipeline logic, not just reconfiguring a connection string)? Second, does this component's cloud-specific version provide a meaningfully better capability than a portable alternative would? Orchestration, the metadata catalog, and the ingestion framework score high replacement cost and low unique-capability benefit from being cloud-specific, so keep those cloud-agnostic. A managed data warehouse's query engine and the specialized ML compute in Azure score the opposite: genuinely differentiated capability, and realistically a decision made once, rarely revisited, so accepting that specific lock-in is the right trade rather than building a portable-but-worse abstraction over it.
flowchart TB
subgraph GCP["GCP, primary"]
BQ["BigQuery and GCS data lake"]
GKEp["Primary compute"]
end
subgraph AWS["AWS, disaster recovery"]
S3["S3 replica and Aurora standby"]
end
subgraph Azure["Azure, specialized compute"]
AzureML["Azure ML GPU compute"]
end
BQ -->|scheduled cross cloud replication| S3
BQ -->|federated export| AzureML
Interconnect1["Dedicated Interconnect or VPN"] --- GCP
Interconnect1 --- AWS
Interconnect2["ExpressRoute or VPN"] --- GCP
Interconnect2 --- Azure
IdP["Central identity provider, federated"] --> GCP
IdP --> AWS
IdP --> Azure
Worked example
An ingestion pipeline pulling from a mix of streaming and batch sources illustrates the framework concretely: the ingestion framework itself (say, a portable connector layer built on an open specification, or a tool like Airbyte/Fivetran-equivalent with cloud-agnostic connectors) runs as a cloud-agnostic component, since swapping the underlying compute it runs on later should not require rewriting every source connector. Once ingested, data lands in BigQuery (the cloud-specific choice, accepted deliberately for its query performance and cost profile at this data volume) which then feeds the AWS DR replica and the Azure specialized-compute exports. If the organization later needed to add a fourth cloud or migrate away from GCP, the ingestion connectors and the metadata catalog would move largely unchanged; only the warehouse and its query layer would need real migration work, which is the entire point of concentrating replacement cost into the one component where accepting it was a deliberate trade, not an accident of not having thought about it.
Trade-offs and pitfalls
The main trade-off is that a genuinely portable ingestion and orchestration layer costs more to build and operate than just using each cloud's native equivalent, and that cost is easy to under-budget for since it doesn't show up as a single line item, it shows up as slightly slower delivery on every pipeline built afterward. The most common pitfall is applying the lock-in-avoidance instinct uniformly to everything, including the data warehouse itself, producing a lowest-common-denominator abstraction that gives up real capability (BigQuery-specific query optimization, for instance) for a portability benefit that may never actually be exercised, since most organizations that build a fully portable warehouse abstraction never actually migrate warehouses. A second pitfall specific to this scenario is letting the metadata catalog itself become another single point of failure without its own resilience story: if it's the only thing that knows where data actually lives, it needs to survive the same DR scenario the data platform is designed around, not be forgotten as "just tooling" outside the DR plan.
Unlock Full Question Bank
Get access to all 17 Multi-Cloud and Hybrid Cloud Architecture interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.