Cloud architecture
AWS vs Azure vs Google Cloud: Migration Costs and Workload Fit
A real client engagement. The engineering and the results are described below.
A SaaS provider we worked with wants to move clouds before its next contract renewal. The cheaper compute quote looks compelling, but the team first follows data, identity and recovery obligations that the quote does not cover.
Request a time through the inquiry form. A meeting is confirmed separately by email.
The business problem behind the technology
Is the cloud migration saving large enough to survive the move?
The team cannot turn a $30,000 monthly estimate difference into a savings claim while ignoring migration overlap, data transfer, commitments and operating responsibility.
Read the client engagement ↓Client engagement / Delivered results
A lower quote had to pay for the transition
A SaaS company we worked with compares its current $180,000 monthly platform cost with a $150,000 candidate estimate. The alternative is attractive only if it serves the same workload and meets the same regional and recovery requirements.
The constraint
The team cannot turn a $30,000 monthly estimate difference into a savings claim while ignoring migration overlap, data transfer, commitments and operating responsibility.
The engineering decision
It screens out incompatible service and residency options, normalizes the remaining quote and plans a reversible pilot. The one-time transition cost came to $240,000 after overlap and migration work were included.
The delivered outcome
Under unchanged monthly assumptions, the narrow simple payback was eight months: $240,000 / $30,000. A workload shift, long commitment or extra operating cost could erase that conclusion.
| Measure / unit | Before | After | Difference |
|---|---|---|---|
| Comparable recurring platform estimate USD/month | 180,000 | 150,000 | 30,000 |
Comparable recurring platform estimate. The $30,000 difference is the recurring benefit before the separately stated $240,000 transition cost.
The conditions behind the results
- Both totals cover the same workload, service quality and operating scope.
- The $240,000 transition cost is a one-time figure; simple payback excludes discounting and later growth.
- The estimate does not rank AWS, Azure or Google Cloud universally.
- This business ledger is separate from the reader’s equal two-line provider quotes.
What this does not prove. The simple payback is a planning figure; a workload shift, long commitment or extra operating cost could change it.
Evidence to collect for your own decision
- Map service-by-service identity, residency, data movement and recovery constraints.
- Collect dated comparable quotes and include commitments, support and egress.
- Pilot the workload and price a credible rollback before signing the migration plan.
Key decisions
A cloud choice is a decision about permissions, failure domains, data movement, service contracts, and the work a team can operate. Start with disqualifying constraints, normalize a pilot around useful completed work, and keep the bill boundary visible.
- Map service-by-service identity, residency, data movement and recovery constraints.
- Collect dated comparable quotes and include commitments, support and egress.
- Simple modeled payback is not an achieved saving, investment return or guarantee that a cloud-to-cloud move is justified.
Follow the decision
Select a step to follow its reasoning, then continue into the technical chapters.
Problem → boundary → decision → evidence
Begin with the workload and non-negotiable requirements.
Read every component and connection
- Find disqualifiers · Problem
- Begin with the workload and non-negotiable requirements.
- Trace dependencies · Boundary
- Identity, regions, networking and managed services define the boundary.
- Normalize costs · Decision
- Compare equivalent quotes and operating responsibilities.
- Pilot the exit · Evidence
- Measure the candidate and retain a reversible decision record.
- Find disqualifiers → Trace dependencies: identify the constraint
- Trace dependencies → Normalize costs: choose a bounded change
- Normalize costs → Pilot the exit: check the outcome
A conceptual decision map for this article, not a measured timeline, physical topology or a depiction of a specific client system.
Hover, focus or tap a component to inspect it. Motion adapts automatically to connection, device and accessibility signals; the component key remains readable without JavaScript.
Begin with the workload and the reasons a candidate cannot qualify
The client’s SaaS team starts with constraints that could invalidate its cheaper-cloud proposal.
AWS, Microsoft Azure, and Google Cloud each offer virtual machines, managed containers, storage, identity controls, and managed data services. That catalog overlap does not establish service equivalence. The useful comparison is a complete deployment option for one workload: a chosen service mode in a chosen region, its dependencies, its recovery design, and the people who will operate it. A provider can be a good fit for one application and a poor fit for another in the same organization.
Separate hard constraints from preferences before assigning scores. Required residency, a supported database extension, a licensing restriction, an approved identity integration, or unavailable accelerator capacity can disqualify a design regardless of its apparent compute price. Then compare latency, useful throughput, restore behavior, migration effort, and expected operating cost among the eligible designs. A large service count or a small hourly rate cannot compensate for a failed requirement.
Keep three questions distinct: can the service satisfy the contract, can this account actually obtain and operate it, and is its full cost preferable under the expected workload? Documentation helps answer the first. Quota and procurement checks inform the second. A controlled pilot and a cost ledger inform the third. This article provides the method for those checks that we applied.
Identity and governance are architecture, not account setup
An account is created, but governance is not finished. Identity and authorization determine what the architecture can safely do.
AWS IAM documents federation and temporary role credentials for both human access and workloads, with account-level and multi-account permission guardrails. In Azure, Azure RBAC assigns a role definition to a security principal at a management-group, subscription, resource-group, or resource scope; its role assignments are additive. Microsoft Entra identity administration and Azure resource authorization are related but different jobs. In Google Cloud, IAM binds principals to roles on resources, with inherited allow policies through organizations, folders, and projects. These scopes are not interchangeable names for the same isolation boundary.
Map a concrete access path in each design: a developer authenticates, a reviewed pipeline requests a short-lived deployment identity, an application receives a workload identity, and that identity can access only the required data. A cloud role is not automatically a Kubernetes role, and cluster API access is different from permission for a pod to read object storage. Test denied operations as well as successful ones, including cross-environment reads, IAM changes, and attempts to obtain a more powerful identity.
Existing organizational familiarity can change implementation effort. A team with established AWS account governance, Microsoft Entra processes, or Google Cloud organization policies may reuse some of that investment. Treat this as a team-specific advantage rather than proof that one provider has universally better identity. Review inherited access, emergency administration, audit retention, and who may alter the guardrails; a narrowly scoped application grant does not erase a broad parent grant.
Choose regional and residency boundaries service by service
The map needs more detail than a cloud region name. Each service carries its own residency and availability constraints.
AWS describes a Region as containing multiple Availability Zones, and a zone can contain more than one discrete data center. Azure distinguishes geographies, which serve as residency boundaries, from regions and their available resiliency options; not every region has the same zone or region-pair arrangement. Google Cloud distinguishes global, regional, and zonal resource scopes, and a zonal disk and its VM must have compatible placement. A region name is therefore only the start of the deployment contract.
Inventory where primary data, replicas, backups, encryption keys, logs, support diagnostics, and model requests may be stored or processed. Check each service and feature rather than assuming that selecting a regional VM keeps every dependency within that region. A global endpoint, an opt-in replication setting, or a managed AI routing mode needs its own review. Residency, administrative access, and contractual jurisdiction are distinct questions; this comparison is not a legal or compliance determination.
Make the failure design explicit. Two replicas in one zone do not provide zone-failure independence, and a second region introduces replication lag, failover complexity, and transfer cost. A documented region pair is not evidence that the application already replicates or recovers. Compare the candidate services at the same recovery-point and recovery-time objectives, then test restore and failover with the chosen data boundary intact.
Draw the network paths before estimating egress
The workload starts moving data between dependencies. Drawing those paths makes cloud network costs and failure boundaries concrete.
For each provider, draw client-to-edge, edge-to-origin, load-balancer-to-pod, pod-to-database, replica-to-replica, and backup/export paths. Mark zone and region crossings, internet destinations, NAT, private endpoints, gateways, and interconnects. A single customer response can create several internal transfers, while caching can reduce origin bytes without eliminating all request or edge charges. The diagram is a traffic ledger, not simply an internet-egress total.
AWS VPC, Azure Virtual Network, and Google Cloud VPC integrations all require service-specific routing and billing review. Do not assume cross-zone traffic is always charged, always free, or billed identically in both directions. The exact service pair, route, region, and current terms determine the meter. Likewise, a private address does not prove a path is free, and avoiding internet egress can still leave processing and hourly endpoint charges.
Compare steady-state transfer, replication during failures, a cold cache, and the one-time exit or migration path. If the application must export a large dataset to another cloud, count the source transfer charge, destination processing and storage, replication overlap, and operational work. Keeping compute near its data may matter more than a small difference in VM pricing, but that conclusion must follow the workload volume rather than a slogan about data gravity.
Managed containers separate control-plane responsibility from compute
A managed container option changes who operates part of the system. It does not remove the workload’s compute and service obligations.
Amazon EKS, Azure Kubernetes Service, and Google Kubernetes Engine are managed Kubernetes offerings, not identical bundles. The EKS documentation distinguishes management of the Kubernetes control plane from the additional node-management responsibilities of Auto Mode. AKS distinguishes modes with different node, scaling, and upgrade responsibilities. GKE distinguishes Standard and Autopilot, including how much node management is delegated. Read the exact mode and pricing tier, not just the service acronym.
Build separate cost lines for cluster or management charges, worker compute, disks, load balancers, addresses, observability, and support. Management automation can add a charge while reducing toil; a control-plane allowance does not make worker capacity free. Node-billed and pod-resource-billed modes also have different idle-capacity and packing implications. The reader deliberately models an entered instance-hour quote, so it cannot directly compare a request-billed container service with a node-billed cluster without an external normalization step.
A managed container application service can be a better fit than a Kubernetes cluster when the application does not need the Kubernetes API or node-level control. But compare request duration, concurrency, background processing, scale-to-zero, startup latency, privileged operations, persistent storage, and networking constraints before mapping one service to another. Removing cluster administration is a real change of responsibility and interface, not a free drop-in replacement for every pod.
Kubernetes portability ends where external contracts begin
The Kubernetes manifest looks portable. You now examine the storage, identity and network contracts that live outside it.
A Deployment, Service, and container image can preserve much application structure across EKS, AKS, and GKE. Portability becomes conditional at workload identity bindings, ingress and Gateway implementations, storage classes and CSI behavior, load-balancer annotations, DNS automation, security policy, device plugins, and managed databases. Kubernetes conformance does not promise that a cloud-specific annotation or storage snapshot can move unchanged.
Maintain a small explicit boundary between portable application intent and provider integration. Record supported Kubernetes versions, CPU architecture, image availability, admission rules, network policy behavior, and storage topology. Test installation, scale-out, rollout, and deletion in the destination environment; do not stop at an API server accepting YAML. An image that starts can still lack an identity token, an attached volume, or a usable external route.
Portability also includes state and operating procedures. Rehearse a data export and restore, key access, DNS transition, and rollback without relying on the old control plane. Avoid inventing a universal abstraction that hides important differences merely to keep manifests identical. A few named provider adapters with tested contracts are often easier to reason about than a lowest-common-denominator platform that discards useful managed features.
Accelerator availability is not an inventory or performance guarantee
Portability is tested at service contracts, where an eight-month payback can acquire hidden costs.
For AWS, Azure, and Google Cloud, qualify an accelerator candidate by the exact machine or service SKU, region and zone, account entitlement, quota, purchase mode, and available capacity. A catalog entry shows a supported offering, not that an allocation is available now. An approved quota is a permission ceiling, not a reserved device. Google explicitly documents that GPU availability for Compute Engine and GKE can differ from availability in other managed services; apply the same service-specific investigation to every candidate.
Define the hardware unit before comparing a quote: one physical GPU, a partition, a VM containing several GPUs, a TPU chip, a TPU slice, or a complete multi-host group. Record memory per device, device count, host CPU and RAM, inter-device links, network fabric, storage throughput, sharing, and precision support. Neither one accelerator nor one vCPU represents equal delivered application capacity across architectures. Peak arithmetic rates are not comparable unless precision, sparsity, and system boundaries also match.
Compare an optional inference worker only after matching model quality, context and output distributions, time-to-first-token and token-gap objectives, failure handling, and useful completions. Include model loading, compilation, idle warm capacity, interrupted work, and replacement headroom. If one candidate needs multiple devices or additional replicas to satisfy the contract, account for the entire group. This article supplies no measured accelerator ranking and does not claim that any named hardware is currently obtainable.
Data platforms and managed AI trade control for integrated operations
The managed data and AI services look convenient. You decide which operating responsibilities and control you are willing to exchange.
For a database or analytical platform, compare transaction and consistency semantics, supported extensions, ingest limits, indexes, query behavior, backup retention, restore granularity, and export formats before comparing a storage unit price. Storage, provisioned compute, scanned bytes, requests, replicas, and network can be separate meters. A compatible wire protocol is useful, but it does not prove identical operational behavior or effortless migration of stored procedures and permissions.
Managed AI also spans different abstraction levels. Amazon Bedrock exposes access to foundation models; Azure Machine Learning documents training, deployment, model management, and MLOps; Google Cloud documentation reached through the Vertex AI introduction describes managed datasets, training, and model registry capabilities. These are examples of services to investigate, not three equivalent SKUs. Distinguish calling a hosted model API from renting compute to serve a model you control, and verify current product naming and the exact endpoint contract.
Compare version pinning, model availability, quota and throughput units, data retention, regional processing, safety settings, cancellation, streaming behavior, and exportability of application artifacts. Include evaluation and human review of output quality rather than assuming the same model family name produces interchangeable application results. Integrated identity and data tooling may reduce engineering effort, while proprietary pipeline definitions and service-specific data formats increase switching work. Record which trade is acceptable for this workload.
Use the reader as a transparent two-line quote model
Two price lines enter the reader. The model helps normalize a quote without pretending to describe the whole provider bill.
The companion reader computes, separately for each provider: compute subtotal = hours × replicas × entered compute quote; transfer subtotal = transfer quantity × entered egress quote; modeled total = compute subtotal + transfer subtotal. Compute quotes are USD per instance-hour for a declared instance configuration. Transfer quotes are USD per billed GB under the declared billing-unit convention. The transfer quantity is the total across the modeled workload, not per replica, so changing replicas does not multiply egress a second time.
All three defaults are deliberately identical placeholders: 730 hours, one instance, 1,000 billed GB, USD 1.00 per instance-hour, and USD 0.10 per billed GB. Each therefore produces USD 730 compute plus USD 100 transfer, or USD 830 for this restricted scenario, with zero spread. These are not provider prices, a typical bill, or evidence of equal real-world cost. The provider labels identify the quote columns, not benchmark results. A changed quote changes only the declared arithmetic.
Enter effective quotes for the same workload boundary, period, currency, purchase terms, and traffic destination. Record whether a provider meter uses decimal GB, binary GiB, or another unit and convert to one common comparison convention; the reader does not perform that conversion. GB and GiB are not interchangeable. The fixed replica input assumes homogeneous instances running for the entered hours; a varying fleet needs instance-hour accounting outside this widget.
Tiering and free allowances must be resolved before entry. For a chosen transfer volume, calculate the applicable tier charges and allowances externally, then divide the resulting transfer subtotal by the declared comparison quantity to obtain an effective quote. Alternatively use a post-allowance billed quantity when it is common to all candidates. Because the reader shares one quantity across providers, differing allowances usually belong in provider-specific effective quotes. Do not subtract an allowance and discount the quote for that same allowance again. Recalculate effective quotes when volume changes; they are not marginal prices for every possible volume. With zero quantity, transfer contributes zero and there is no need to divide by zero to derive a quote.
The model excludes cluster management, storage, database and AI API charges, NAT and endpoint processing, load balancers, cross-zone and cross-region paths not included in the quote, logging, support, tax, currency changes, one-time migration, labor, minimum billing increments, and unused commitments. It fetches no live prices and validates no service equivalence. Use the AWS, Azure, and Google Cloud calculators linked below to assemble a dated, reproducible full estimate; even an official calculator remains an estimate based on its inputs.
01 / Follow the explanation
Which cloud fits the workload—not just the headline price?
A brief visual sequence plays automatically. The example and its assumptions are already here—nothing to configure.
An interactive teaching model drawn from real delivery work. No agent, cloud account, GPU or cluster is accessed.
Read the full explanation and assumptionsCurrent example
AWS and Azure and Google Cloud tie under the entered quotes
Entered quotes, not live prices or equivalent-instance performance. Compute plus billed outbound transfer only; excludes storage, requests, support, tax, commitments, credits, cross-zone and managed-service fees. Effective transfer quotes must already reflect units, tiers and allowances. Residency, identity, availability, operating skills and performance still constrain a provider choice.
- Highest minus lowest modeled total
- $0.00 USD
Initial quotes are deliberately equal: 730 instance-hours, one replica, 1,000 normalized billed GB outbound, $1 per instance-hour and $0.10 per outbound GB for every provider. These are not current prices.
Normalize the workload
- Hours in the comparison window
- 730
- Equivalent instance count
- 1
- Normalized outbound billed GB
- 1000
AWS quote
Azure quote
Google Cloud quote
- Google Cloud quote / instance-hour (USD)
- 1
- Google Cloud effective outbound quote (USD/GB)
- 0.1
- Google Cloud modeled subtotal
- $830.00 USD
# Same scope for every entered provider quote
compute_subtotal = hours * replicas * quoted_compute_USD_per_instance_hour
egress_subtotal = billed_egress_GB * quoted_egress_USD_per_GB
modeled_total = compute_subtotal + egress_subtotal
spread = max(aws_total, azure_total, gcp_total) - min(aws_total, azure_total, gcp_total)
# Compare operational constraints before selecting a provider.Commitments, support, and operating effort change the answer
Commitments and operating effort change the apparent saving. Cloud cost optimization has to include those obligations.
On-demand, interruptible, committed-use, and reservation arrangements are not interchangeable discounts. For each provider and product, check term length, scope, eligible usage, upfront payment, cancellation or exchange rules, and whether the purchase grants capacity, a price benefit, or both. A commitment can lower the effective rate for predictable eligible usage while increasing cost when demand shifts. Do not apply a headline discount to every dependency or count unused committed capacity as free.
Support plans have scope, response-time targets, escalation requirements, and commercial terms; a response target is not a guarantee of application restoration. Price the level the team actually needs. Include operating-system or database licensing, upgrade maintenance, incident handling, audit work, observability retention, and the engineering cost of provider-specific integrations. Existing skills matter, but assume neither that managed means no operations nor that self-management is inherently cheaper.
Maintain low, expected, and high demand scenarios and a separate migration ledger. Show how the decision changes under lower utilization, greater transfer, longer overlap during migration, or delayed capacity approval. Compare savings against the cost of irreversible commitments and the work needed to exit. The right conclusion can be to keep the present placement, choose one provider for this workload, or split genuinely independent workloads; it need not be an organization-wide winner.
Terraform state and protected review are part of the deployment boundary
The design reaches its deployment boundary. State custody and reviewed changes now become part of the migration story.
Terraform gives the three designs a reviewable workflow, not a shared cloud authorization model. Provider schemas, resource lifecycles, IAM permissions, and replacement behavior remain different. Pin the toolchain and provider dependencies, retain the dependency lock file, and review resource replacements, permission changes, networking, and regional placement. Separate state and deployment identities for environments with different trust boundaries; a workspace name alone is not an access-control mechanism.
HashiCorp documents that state and plan files can contain sensitive data and that marking a value sensitive primarily redacts display rather than removing it from state. Use a backend with suitable access controls, encryption and recovery, and supported locking; restrict both state and plan artifacts. Locking coordinates writers, not reviewer authorization. Do not bypass a live lock to force a second writer, and do not put credentials, state, or sensitive plan contents in Git.
A protected delivery process should bind approval to an exact reviewed revision, a saved plan artifact, the intended environment, and the deployment identity. Re-plan and re-review when inputs or relevant state change rather than approving one plan and applying a different one. Use short-lived federated credentials with reviewed audience and subject restrictions where supported. Keep untrusted contribution jobs away from apply credentials and trusted runners. These are proposed controls, not a claim that this website provisions or verifies such a pipeline.
Normalize a pilot and leave a complete decision record
The pilot had to substantiate recurring costs and the transition bill before the team could rely on the payback.
Run equivalent application behavior, not merely equal VM counts. Hold the request mix, dataset, quality checks, test client location, offered load, and service objectives constant; allow each candidate enough resources to meet them and record that resource difference. Separate cold start, warm steady state, burst response, and recovery. Retain failures, rejections, repeated-run variation, useful completed work, and the full cost interval, including idle and failed work. Cost per successful objective-compliant request is more informative than cost per allocated core alone.
The following JSON is the decision record structure we used for the constrained decision: do not select a provider from the reader defaults, and define the evidence required before choosing one. Its targets are the engagement’s stated requirements. Empty evidence arrays and a null selected provider explicitly record that the pilot had not yet been executed at that point; they are not credentials, deployment placeholders, or approvals. This record does not authorize spending or infrastructure actions.
After a real authorized pilot, write a new reviewed decision with the dated quotes, evidence artifacts, selected service modes and regions, rejected alternatives, accepted limitations, and a review trigger. Preserve the earlier record so changed assumptions remain visible. Revisit when workload shape, residency requirements, capacity availability, or commercial terms change. A defensible result names the workload and conditions under which the choice holds instead of declaring one cloud universally best.
{
"id": "cloud-comparison-engagement-01",
"date": "2026-09-05",
"scope": "Containerized API comparison; no execution authorized",
"status": "method-selected",
"decision": "Require comparable pilot evidence before provider selection",
"candidates": ["AWS", "Microsoft Azure", "Google Cloud"],
"selected_provider": null,
"workload": {
"data": "anonymized non-personal records only",
"dataset_gib": 10,
"request_mix": {"read_percent": 90, "write_percent": 10},
"offered_requests_per_second": 100,
"warmup_minutes": 10,
"measured_minutes_per_run": 60,
"repeated_runs": 3
},
"acceptance": {
"client_p99_latency_ms_max": 250,
"failed_request_percent_max": 0.1,
"quality_check": "application-defined output validation passes",
"restore_check": "documented restore completes inside the recovery objective"
},
"evidence": [],
"notes": "Populate with dated quotes, pilot artifacts and reviewed limitations before any selection."
}Questions behind the decision
Which is cheapest: AWS, Azure or Google Cloud?
There is no workload-independent winner. Region, managed services, discounts, egress, support and operational responsibilities can change the result; normalize the complete service contract before comparing prices.
How should cloud migration payback be calculated?
Compare one-time transition costs with a credible recurring difference at matched useful output and reliability. Stress-test overlap, stranded commitments and operating changes, and distinguish simple payback from discounted cash-flow analysis.
References & further reading
- AWS Pricing Calculator
- AWS: calculator assumptions, estimates, and exclusions
- Microsoft Azure pricing calculator
- Google Cloud pricing calculator
- AWS IAM: federation, temporary credentials, and least privilege
- Microsoft: Azure RBAC principals, roles, and scopes
- Google Cloud: IAM roles and policy inheritance
- AWS: Regions and Availability Zones
- Microsoft: Azure regions, geographies, and resiliency choices
- Google Cloud: global, regional, and zonal resource boundaries
- AWS: EKS control-plane and compute management
- Microsoft: AKS modes and container service boundaries
- Google Cloud: GKE Standard and Autopilot overview
- Google Cloud: GPU locations and service-specific availability
- AWS: Amazon Bedrock overview
- Microsoft: Azure Machine Learning lifecycle and MLOps
- Google Cloud: managed ML training, datasets, and registry introduction
- HashiCorp: sensitive data in Terraform state and plans
- HashiCorp: backend-dependent Terraform state locking


