The same primitive, three providers

A mapping you can use to read a design written for another cloud — and, more importantly, a record of where that mapping misleads. Every row carries what differs, because the services in a row are near-equivalents and never exact ones.

CLOUD-SPECIFICRead the third block of every row you use

Reading a row across is the fast way to orient yourself and the fast way to be wrong. These are not drop-in equivalents, and the differences are not trivia: they change what a region means, where failover is decided, how a workload gets an identity, what a restart does, and what you are billed for. A migration plan built on the first two columns alone fails at the first scaling event. Managed-service behaviour also moves — verify anything load-bearing against current provider documentation.

Container registry
AWS
Elastic Container Registry (ECR)
Google Cloud
Artifact Registry
Azure
Azure Container Registry (ACR)
Where the mapping misleads

Where replication and access control live. An ECR repository is a regional resource with its own resource policy, so "the image is available in every region" means creating and replicating per region. Artifact Registry is one multi-format service whose repositories are regional or multi-regional and whose access is normally granted at project level. ACR is a single registry resource where geo-replication is a feature of the higher service tier — so the same requirement is a per-region resource in one, a repository location choice in another, and a pricing-tier decision in the third.

Managed Kubernetes
AWS
Elastic Kubernetes Service (EKS)
Google Cloud
Google Kubernetes Engine (GKE)
Azure
Azure Kubernetes Service (AKS)
Where the mapping misleads

How much of the cluster you still operate, and how pods get an identity and an IP. EKS gives you the control plane and leaves node groups, add-ons and networking to you; with the VPC CNI, pods receive real VPC addresses, which makes subnet IP exhaustion a genuine capacity limit that has no analogue on an overlay network. GKE Autopilot goes furthest in removing node management and bills against pod resource requests, which changes how requests and limits affect your bill rather than just your bin packing. Workload identity is a different mechanism in each — IAM roles for service accounts, Workload Identity Federation, and Entra Workload ID — so identity is the first thing that breaks in a port, not the manifests.

Managed containers without a cluster
AWS
ECS on Fargate, or App Runner
Google Cloud
Cloud Run
Azure
Container Apps
Where the mapping misleads

The scaling unit, and whether idle costs anything. Cloud Run scales on in-flight requests per container with a configurable concurrency setting and can scale to zero. ECS on Fargate scales the number of tasks against metrics such as CPU or per-target request count, and a service with a minimum count of one keeps paying while idle. Container Apps drives scaling through KEDA rules, so a queue length or an event source can be the scaling signal directly. The same image therefore has materially different cost, cold-start and concurrency behaviour on each — and code that assumes one request per container at a time is only correct on some of them.

CI/CD
AWS
CodePipeline with CodeBuild, or CodeDeploy
Google Cloud
Cloud Build, with Cloud Deploy for promotion
Azure
Azure Pipelines, or GitHub Actions
Where the mapping misleads

Where promotion and approval are modelled. CodePipeline makes stages and manual approval actions part of the pipeline resource itself. Google splits the two: Cloud Build produces artifacts and Cloud Deploy owns the delivery pipeline, targets and promotion between them. Azure Pipelines models environments as first-class objects with their own approvals and RBAC, so an approval is attached to the environment rather than to the pipeline definition. Federating a build job to cloud credentials without long-lived keys is possible on all three but is configured through different trust relationships, and the identity a job assumes is the part most likely to be silently over-privileged after a port.

Secrets
AWS
Secrets Manager (and SSM Parameter Store)
Google Cloud
Secret Manager
Azure
Key Vault
Where the mapping misleads

The rotation and deletion model. AWS Secrets Manager can invoke a rotation function you own on a schedule, so rotation is a managed workflow. GCP Secret Manager versions are immutable and rotation is effectively "add a version and notify"; the job that updates the consumer is yours to write. Key Vault separates secrets, keys and certificates into distinct object types and adds soft-delete and purge protection, so a deleted secret remains recoverable — and its name remains taken — for a retention window. Because of that, "delete and recreate with the same name" is a routine operation on one provider and a blocked one on another.

Monitoring and logs
AWS
CloudWatch (Metrics, Logs, Alarms)
Google Cloud
Cloud Monitoring and Cloud Logging
Azure
Azure Monitor with Log Analytics
Where the mapping misleads

The query language and what counts as billable custom telemetry. Alerts are written in CloudWatch Logs Insights syntax and metric alarms, in GCP against Cloud Monitoring with PromQL or MQL over a resource-and-label model, and in Azure in KQL over Log Analytics tables. Default retention, metric resolution and the definition of a chargeable custom metric or ingested log GB differ, so a high-cardinality label that is cheap on one provider is a significant bill on another. An alert set does not port; it is rewritten, and the rewrite is where thresholds silently change meaning.

Object storage
AWS
S3
Google Cloud
Cloud Storage
Azure
Blob Storage
Where the mapping misleads

Namespace scope and how redundancy is expressed. Cloud Storage bucket names are globally unique and the bucket location can be regional, dual-region or multi-region, so geographic redundancy is a property chosen at creation. S3 buckets are regional and cross-region copies are explicit replication rules with their own IAM and asynchronous lag. Azure organises containers under a storage account whose replication option is account-level, and read-access geo-redundant storage exposes a secondary endpoint that is read-only and lags the primary. Storage classes, lifecycle transitions and the cost of retrieval from cold tiers differ enough that a lifecycle policy is not portable even when the object layout is.

DNS
AWS
Route 53
Google Cloud
Cloud DNS
Azure
Azure DNS with Traffic Manager
Where the mapping misleads

Whether traffic policy lives in DNS. Route 53 holds weighted, latency, geolocation and failover routing together with its own health checks, so a failover record is one service. Azure separates authoritative hosting (Azure DNS) from DNS-level traffic routing (Traffic Manager), and again from anycast HTTP routing (Front Door), so the same outcome spans two or three resources with distinct health-check semantics. Cloud DNS supports geo and weighted routing policies, with health-checked failover tied to other GCP components. Minimum TTLs, negative caching defaults and how quickly a change propagates through the provider's own resolvers differ — which matters most during the failover you planned around them.

Load balancing
AWS
Application and Network Load Balancers, with Global Accelerator for anycast
Google Cloud
Cloud Load Balancing
Azure
Load Balancer (L4), Application Gateway (regional L7), Front Door (global L7)
Where the mapping misleads

Regional versus global by default. Google's external HTTP(S) load balancer is a single global anycast address with one backend service that can span regions, so cross-region failover is a property of the load balancer. An AWS ALB is regional: going global means Route 53 or Global Accelerator in front of per-region balancers, which changes where failover decisions are made and how fast they take effect. Azure splits the role across three services by layer and scope. Connection draining, idle timeouts and health-check intervals are configured per product and are exactly the settings that decide whether a deploy drops in-flight requests — so draining behaviour must be re-verified on each, not assumed.