Cross-Cloud Architecture for Organizations That Refuse Lock-In
01
What problem did this cross-cloud architecture solve?
The organization was spending $4.8 million annually on a single cloud provider and had zero leverage in pricing negotiations because migration was perceived as technically infeasible. The cross-cloud architecture restored negotiation leverage by making migration a credible option.
The financial services organization had built 34 services on AWS over 5 years. Every service used AWS-specific technologies: Lambda for compute, DynamoDB for storage, SQS for messaging, CloudFormation for infrastructure. When AWS increased pricing for their tier, the organization had no alternative. Migration would take an estimated 18 months and $2.3 million. They were locked in, and AWS knew it. The business case for cross-cloud was not about actually migrating. It was about making migration possible so that the threat of migration was credible during contract negotiations.
02
How was the cross-cloud architecture designed?
The architecture used 3 abstraction layers: infrastructure-as-code with provider-neutral modules, containerized workloads with Kubernetes as the compute platform, and application-level abstractions that isolated cloud-specific SDK usage to adapter layers.
Infrastructure Abstraction: I replaced CloudFormation with Terraform using provider-neutral module interfaces. Each infrastructure component (compute cluster, database, message queue, object storage) was defined as a module with a cloud-agnostic interface and cloud-specific implementations. Switching providers meant swapping the implementation module, not rewriting the infrastructure definition. The Terraform modules for AWS, Azure, and GCP were maintained in parallel, with CI tests verifying that each implementation produced equivalent functionality.
Compute Abstraction: All workloads migrated from Lambda to containerized services running on Kubernetes (EKS on AWS, AKS on Azure, GKE on GCP). The Kubernetes manifests were identical across providers. The only provider-specific configuration was the cluster provisioning, which was handled by the Terraform modules. This migration from serverless to containers increased baseline infrastructure cost by approximately 8% but eliminated the single largest lock-in vector.
Application Abstraction: Cloud-specific SDK calls (S3 client, DynamoDB client, SQS client) were isolated behind adapter interfaces. The application code used generic interfaces (ObjectStore, DocumentStore, MessageQueue). Provider-specific adapters implemented these interfaces. Switching providers required implementing new adapters (approximately 2 to 4 weeks of work per adapter) without touching business logic. This pattern is the same anti-corruption layer I described in integration architecture.
According to vendor lock-in analysis, the cost of switching providers increases exponentially with the depth of provider-specific integration. The abstraction layers linearize this cost by confining provider dependencies to well-defined adapter boundaries.
03
What were the measurable outcomes?
12%
Cost Overhead for Portability
$1.4M
Annual Negotiation Savings
4 wk
Estimated Migration Time (from 18 mo)
34
Services Made Portable
The 12% cost overhead was the price of maintaining abstraction layers, running Kubernetes instead of serverless, and maintaining parallel Terraform modules. The $1.4 million in negotiation savings came from the next contract renewal, where the organization demonstrated a working proof-of-concept on Azure (3 services running in parallel) and negotiated a 23% discount that the previous renewal could not achieve. The estimated migration time dropped from 18 months to 4 weeks because the abstraction layers confined the migration work to adapter swaps and Terraform module switches rather than application rewrites.
04
What would I change in hindsight?
I would have been more selective about which services to make portable and invested the savings in deeper portability for the 10 most critical services rather than shallow portability for all 34.
Not every service needed cross-cloud portability. Internal tools, development utilities, and batch processing jobs with no vendor-specific dependencies did not benefit from the abstraction layers. They just inherited the complexity. If I were designing this again, I would classify services into 3 tiers: Tier 1 (revenue-critical, must be portable), Tier 2 (important, should be portable within 3 months), and Tier 3 (internal, no portability requirement). This would have concentrated the 12% cost overhead on the 10 services that mattered most and reduced overall architecture complexity.
I also underestimated the operational burden of maintaining parallel Terraform modules. Each provider’s implementation needed testing against that provider’s current API, which changed frequently. A dedicated 0.5 FTE engineer was required to keep the modules current. For organizations considering this approach, budget for ongoing maintenance of the abstraction layers, not just their initial implementation. The architecture of portability requires the same ongoing investment as the cloud architecture itself.