From AWS to GCP: Mental Models and Architecture Mapping for DevOps Engineers
When DevOps engineers familiar with AWS first encounter Google Cloud Platform (GCP), the biggest friction rarely comes from terminology mapping—it stems from conflicting underlying architectural assumptions.
While both platforms provide virtual machines, managed containers, object storage, and identity management, their foundational mental models differ sharply across resource boundaries, permission inheritance, and network topology. Applying AWS operational instincts directly to GCP not only risks unintended permission overwrites in Terraform, but can also lead to misjudgments in network planning and Kubernetes integration.
Core Architecture Mapping Overview
Before diving into specific mechanisms, the table below highlights the architectural dimensions with the highest “cognitive friction” in day-to-day DevOps operations:
| Domain | AWS Concept | GCP Concept | Difference Level | Key Mindset Shift |
|---|---|---|---|---|
| Resource Isolation | AWS Organizations / Account | Organization / Folder / Project | High | A Project is a trust boundary and billing unit, not an independent account |
| Identity & Access | IAM User / Role / Policy | IAM Principal / Role / Allow Policy | High | Permissions attach to the resource hierarchy and inherit downward; a Role is merely a set of permissions |
| Virtual Networking | VPC (Regional) | VPC (Global, Subnets are Regional) | High | A single VPC spans the globe; cross-region routing requires no peering |
| Firewalls | Security Group + NACL | VPC Firewall Rules / Policies | Medium | Single-tier stateful firewall supporting Allow/Deny and Priority |
| Private Connectivity | VPC Endpoint / PrivateLink | Private Google Access / Private Service Connect / Private Services Access | Medium | Split across three distinct mechanisms; access to Google APIs is free |
| Outbound NAT | NAT Gateway (traditionally one per AZ) | Cloud NAT | Medium | Software-defined distributed architecture attached to Cloud Router |
| Load Balancing | ALB / NLB (Regional resources) | Cloud Load Balancing | Medium | Global Anycast single-IP ingress composed of modular resources |
| Container Orchestration | Amazon EKS | Google Kubernetes Engine (GKE) | Medium | Built-in rolling auto-upgrades, VPC-native networking, and Autopilot |
| Serverless Containers | ECS on Fargate / App Runner | Cloud Run | Medium | Single platform unifying HTTP services, scheduled jobs, and worker pools |
| Audit Logging | AWS CloudTrail | Cloud Audit Logs | Medium | Admin logs are mandatory and free; Data Access logs are disabled by default |
1. Resource Hierarchy and IAM: The Downward-Inheriting Union Model
A Project Is Not an AWS Account
In AWS, the fundamental isolation boundary is the AWS Account. AWS Organizations groups accounts using Organizational Units (OUs) and restricts maximum allowable permissions from the top down using Service Control Policies (SCPs), but each account maintains an independent IAM system.
GCP, by contrast, enforces a strict tree-like structure: the Resource Hierarchy governs the relationship of Organization → Folder → Project → Underlying Resources.
AWS Model:
[Organization] --> [OU] --> [AWS Account]
+-- Role / User / Resources
GCP Model:
[Organization]
+-- [Folder]
+-- [Project] (Resource Container & Billing Unit)
+-- Compute Engine / GKE / Cloud Storage
In GCP, a Project is the container for billing, quotas, enabled APIs, and default networking. The core distinction lies in how permissions flow:
- Attachment point: AWS IAM policies attach primarily to identities (Users, Roles); GCP Allow Policies attach to nodes in the resource hierarchy, defining “which Principal holds which Role on this node.”
- Downward inheritance: Permissions granted at a parent level (such as a Folder) automatically propagate down to all child Projects and resources.
- Union rule: Under GCP’s permission inheritance model, effective permissions on a resource are the union of the policy attached to the resource itself and all policies attached to its ancestors. Consequently, a child resource cannot revoke permissions granted at a parent level. To block inherited access, you must enforce an IAM Deny Policy or apply Organization Policies at the organization level.
Service Accounts vs. IAM Roles
In AWS, an IAM Role acts like a “hat”: an EC2 instance wears it via an Instance Profile, a Lambda function wears it via an Execution Role, and a developer wears it via AssumeRole.
GCP completely decouples identity from permissions into two distinct primitives:
- Role: A pure collection of permissions (e.g.,
roles/storage.objectViewer). A Role is not an identity and cannot be “assumed.” - Service Account (SA): A machine identity with an email address format (e.g.,
deployer@PROJECT_ID.iam.gserviceaccount.com).
The fundamental architectural differences between the two are outlined below:
| Dimension | AWS IAM Role | GCP Service Account |
|---|---|---|
| Fundamental Nature | Virtual identity (assumed by entities) | Machine identity (has a dedicated email) |
| Permission Assignment | IAM Policies attached directly | Roles (permission sets) granted on the resource hierarchy |
| Compute Binding | Instance Profile / Execution Role | Attached directly to VMs, GKE nodes, or Cloud Run |
| Resource Status | Pure identity construct | Both an identity and a resource (requires authorization to use) |
WARNING
Two-tier authorization blind spot: In GCP, a Service Account is itself a resource. To run a compute workload (or allow a user) as a specific SA, the operator must hold the roles/iam.serviceAccountUser role on that SA. Having permission to deploy resources does not inherently grant permission to act as a given Service Account.
In production operations, follow these two best practices:
- Revoke excessive default SA permissions: The Compute Engine Default SA created automatically often carries the overly permissive
Editorrole. Disable automatic role granting via Organization Policies, and provision dedicated, least-privilege service accounts instead. - Eliminate long-lived key files: Avoid downloading Service Account JSON keys. Instead, use attached identities, IAM Impersonation (acting as a Service Account, similar to AWS
AssumeRole), or Workload Identity Federation (swapping an AWS IAM Role directly for a short-lived GCP token when integrating across clouds).
Terraform IAM Pitfalls and Local Authentication
In AWS Terraform, aws_iam_role_policy_attachment manages only the association between a specific policy and a role, leaving other existing bindings untouched. In contrast, the GCP Provider offers three distinct IAM resource types with varying levels of granularity (see google_project_iam), each with vastly different behaviors and blast radii:
# 1. google_project_iam_binding (Authoritative over a single role)
# Controls the exclusive member list for a given role. If someone manually adds a member via the Console, the next `terraform apply` will purge them!
resource "google_project_iam_binding" "editor_binding" {
project = var.project_id
role = "roles/editor"
members = ["serviceAccount:${google_service_account.admin.email}"]
}
# 2. google_project_iam_member (Safest: Additive)
# Adds a single binding without affecting other members under this role or out-of-band Console configurations.
resource "google_project_iam_member" "deployer_ar_writer" {
project = var.project_id
role = "roles/artifactregistry.writer"
member = "serviceAccount:${google_service_account.deployer.email}"
}
When writing IaC for existing projects, always default to google_project_iam_member to avoid unintended access revocation caused by authoritative iam_binding or globally authoritative iam_policy resources.
TIP
Local authentication blind spot: Decoupled gcloud and ADC: When writing and testing Terraform locally, AWS CLI and SDKs share ~/.aws/credentials. GCP separates them. Running gcloud auth login only authenticates the CLI itself. To run Terraform or execute code via GCP SDKs, you must separately run gcloud auth application-default login to generate local Application Default Credentials (ADC). Missing this step causes a common point of confusion where CLI commands work flawlessly while applications throw authentication errors.
2. Networking: Global VPCs, Shared VPCs, and Software-Defined Perimeters
Global VPCs and Regional Subnets
An AWS VPC is confined to a single Region, and each Subnet resides within a specific Availability Zone (AZ). Routing between two cross-region VPCs requires VPC Peering or a Transit Gateway.
In GCP, a VPC is a global resource, whereas subnets are regional resources:
AWS Architecture (Regional VPC):
[Region: us-east-1] [Region: ap-northeast-1]
+-----------------------+ +-----------------------+
| VPC: 10.0.0.0/16 | | VPC: 10.1.0.0/16 |
| +-- Subnet AZ-a |<->| +-- Subnet AZ-a |
| +-- Subnet AZ-b | | +-- Subnet AZ-b |
+-----------------------+ +-----------------------+
(Cross-region requires Peering or Transit Gateway)
GCP Architecture (Global VPC):
+---------------------------------------------------+
| Global VPC Network |
| +-- Region: us-central1 (Subnet: 10.128.0.0/20) |
| +-- Region: asia-east1 (Subnet: 10.140.0.0/20) |
| |
| <--- Direct Internal Routing ---> |
+---------------------------------------------------+
This architecture introduces three fundamental shifts:
- Native cross-region internal communication: A VM in
asia-east1and a VM inus-central1communicate directly via internal IP addresses as long as they reside in the same VPC. Traffic traverses Google’s global private fiber backbone without requiring VPC peering. - Subnets span all zones: Subnets are not tied to a specific zone. You only need to allocate a single subnet per region; all zones in that region automatically share this address space.
- No public vs. private subnet distinction: GCP has no Internet Gateway (IGW) resource. Whether a VM can reach the public internet depends entirely on whether it is assigned an external public IP, or whether Cloud NAT is configured for that region.
NOTE
Cross-project networking: Shared VPC: Comparable to sharing subnets via AWS Resource Access Manager (RAM), GCP’s Shared VPC allows a centralized Host Project to share its VPC network with multiple Service Projects. Central network teams manage subnets and firewalls in the Host Project, while individual application teams deploy VMs and GKE clusters in their respective Service Projects, aligning with project-level IAM isolation boundaries.
Firewall Mechanisms and Demystifying the Three Types of “Tags”
AWS uses a two-tier model: stateful Security Groups attached to ENIs (allow-only) paired with stateless NACLs attached to subnets (supporting allow/deny with rule evaluation order).
GCP collapses both into a single system: VPC Firewall Rules:
- Stateful connection tracking: Return traffic is automatically allowed.
- Both Allow and Deny supported: Rules are evaluated based on Priority (
0to65535, where lower numbers indicate higher priority). - Target selectors: Rules target instances using Network Tags (plain text strings) or Target Service Accounts.
resource "google_compute_firewall" "allow_lb_to_web" {
name = "allow-lb-to-web"
network = google_compute_network.main.id
direction = "INGRESS"
priority = 1000
source_ranges = ["35.191.0.0/16", "130.211.0.0/22"] # Google LB probe and GFE-to-backend source ranges
target_service_accounts = [google_service_account.web.email]
allow {
protocol = "tcp"
ports = ["8080"]
}
}
While AWS relies on a single tagging mechanism, GCP splits the concept into three distinct, non-interchangeable mechanisms:
| Tag Type | Definition Level & Characteristics | Core Purpose & Limitations |
|---|---|---|
| Labels | Resource level (Key-Value pairs) | Used exclusively for cost allocation / billing breakdown and Console filtering; cannot be used in IAM or firewall conditions |
| Tags (RM Tags) | Organization / Project level (hierarchical inheritance) | Strongly typed and governed by IAM; can be referenced in IAM conditions and Org Policies |
| Network Tags | VM attribute (list of plain text strings) | Used exclusively for target filtering in VPC firewall rules and routes; lacks IAM access controls |
In security-sensitive environments, always configure firewall rules using Target Service Accounts instead of Network Tags to prevent instances from bypassing network perimeters through arbitrary tag modifications.
Private Connectivity and Load Balancing Architectures
AWS unifies private, non-internet-routable communication under VPC Endpoints. GCP separates private access into three distinct mechanisms based on the target architecture:
- Private Google Access: Enabled with a single toggle at the subnet level, allowing VMs with only internal IP addresses to reach Google APIs (such as GCS and BigQuery) completely free of charge, without provisioning dedicated endpoint network interfaces.
- Private Service Connect (PSC): The direct equivalent of AWS PrivateLink, providing custom internal IP endpoints to connect securely to third-party SaaS providers or private services published across VPCs.
- Private Services Access (PSA): Implemented as VPC Peering to connect directly to dedicated internal subnets hosting Google-managed services (such as Cloud SQL and Memorystore). Cloud SQL now recommends PSC for private IP connectivity (the console default for new projects), making PSA the earlier-generation mechanism.
For egress and load balancing:
- Cloud NAT: AWS NAT Gateways are tied to specific AZs, traditionally requiring one gateway per AZ for high availability (a Regional NAT Gateway that expands across AZs automatically became available in November 2025). GCP’s Cloud NAT is a distributed, software-defined service configured directly on a regional Cloud Router, eliminating the need to manage proxy VMs.
- Cloud Load Balancing: AWS ALB and NLB are regional resources. GCP provides the Global External Application Load Balancer, fronted by a single Anycast IP. Global user traffic enters Google’s private backbone at the edge PoP nearest to the client and is automatically routed to backends across multiple regions, delivering built-in multi-region failover.
NOTE
Further reading: AWS VPC Fundamentals (Part 1): Routing, Firewalls, and Costs in a Single VPC
3. Kubernetes: GKE vs. EKS Cloud Integrations
For Certified Kubernetes Administrators (CKAs), core Kubernetes constructs such as kubectl, Deployment, and Service translate seamlessly. The key differences lie in how each platform integrates with the underlying cloud control plane.
Standard vs. Autopilot Modes
EKS manages compute fleets using Managed Node Groups, Karpenter, or Fargate. GKE offers two cluster operation modes at creation time:
| Evaluation Dimension | GKE Standard | GKE Autopilot |
|---|---|---|
| Node Management | User-managed Node Pools | Worker nodes fully managed by Google |
| Control Plane | Supports Zonal (single zone) and Regional (HA) | Strictly Regional (replicated across multiple zones) |
| Billing Basis | Node VM specifications and instance counts | Pod-declared Resource Requests |
| Node Access | Full SSH access and node-level debugging | SSH disabled; troubleshooting relies entirely on Cloud Logging |
| Upgrade Mechanism | Manual, custom, or managed via Release Channels | Strictly locked to Release Channels for automatic upgrades |
WARNING
Single point of failure blind spot in Standard clusters: In AWS EKS, the control plane is always managed by AWS across multiple availability zones. GKE Standard, however, permits the creation of Zonal clusters (where the control plane resides in a single zone, reducing cluster management fees but introducing a single point of failure). For production environments, always provision Regional clusters to guarantee control plane high availability.
Under GKE Autopilot, operational thinking changes significantly: resource requests directly dictate infrastructure spend; Google assumes all node maintenance duties; and observability shifts entirely to Cloud Logging and kubectl describe.
VPC-Native Clusters and Secondary IP Ranges
In AWS EKS, the AWS VPC CNI assigns Pod IPs directly from the primary subnet address space, often leading to subnet CIDR exhaustion as clusters scale.
GKE defaults to a VPC-Native architecture leveraging Alias IPs:
GCP Subnet CIDR Layout for GKE:
+---------------------------------------------------+
| Primary CIDR: 10.0.0.0/24 (Node VMs) |
| Secondary CIDR 1: 10.4.0.0/14 (Pods) |
| Secondary CIDR 2: 10.8.0.0/20 (Services) |
+---------------------------------------------------+
Because secondary ranges are natively routable across the entire VPC, Google Cloud Load Balancers can bypass NodePorts and kube-proxy entirely using Network Endpoint Groups (NEGs). Traffic routes directly to Pod IPs (Container-Native Load Balancing), significantly reducing network hops and latency.
Workload Identity Federation for GKE
In AWS EKS, pods accessing AWS APIs historically relied on IRSA (requiring an IAM OIDC Provider and Trust Policies), with EKS Pod Identity as the more recent option.
GCP’s equivalent solution is Workload Identity Federation for GKE. When enabled, the GKE Metadata Server intercepts credential requests on nodes and exchanges the Kubernetes ServiceAccount (KSA) token for a short-lived GCP access token.
GCP’s current best practice is to grant IAM roles directly to the KSA principal using the following URI format:
principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/KSA_NAME
This principal can be bound directly as a member in any GCP resource IAM policy, removing the need to provision an intermediate GCP Service Account.
IMPORTANT
Project Number vs. Project ID: When configuring Workload Identity principals, the string path requires the numeric Project Number, not the alphanumeric Project ID. This mismatch is a common debugging pitfall in cross-cloud migrations.
Automatic Rolling Upgrades via Release Channels
In EKS, Kubernetes version upgrades require operators to manually trigger control plane and node group updates.
GKE introduces Release Channels (Rapid, Regular, Stable, plus Extended for long-term support on Standard clusters) by default:
- Both control planes and worker nodes undergo automatic rolling upgrades.
- The core operational responsibility for DevOps shifts to: establishing robust PodDisruptionBudgets (PDBs), proactively migrating workloads before API deprecations, and configuring Maintenance Windows and exclusion periods to ensure automatic upgrades do not disrupt core business operations.
4. Serverless Boundaries: Unified Workloads on Cloud Run
While AWS splinters serverless compute across Lambda (pure functions), App Runner (simplified containers), and ECS on Fargate (full containerized tasks), GCP unifies these workloads under Cloud Run.
The core value proposition of Cloud Run is that a single standard Docker container image and a unified IAM model can handle multiple execution patterns:
| Execution Mode | Trigger & Lifecycle | Ideal Use Cases |
|---|---|---|
| Services | HTTP / gRPC request-driven (scales to zero) | Web APIs, microservices, webhooks |
| Jobs | Invoked on demand or scheduled via Cloud Scheduler | Data transformation, DB migrations, batch reporting |
| Worker Pools | Continuously pull messages from queues like Pub/Sub | Asynchronous event processing, background queue workers |
Standalone Cloud Functions have now been fully folded into the Cloud Run functions family. For operations teams running stateless workloads, Cloud Run delivers serverless execution without the operational overhead of managing Kubernetes clusters or nodes.
5. Operational Boundaries: Audit Logging, API Ecosystem, and Billing
Audit Logging Mechanisms (vs. CloudTrail)
GCP’s Cloud Audit Logs enforces clear architectural boundaries around costs and enablement:
- Admin Activity and System Event logs: Always enabled, retained for 400 days free of charge (written to the
_Requiredlog bucket), and cannot be manually disabled. Every resource creation, modification, and deletion is recorded automatically. - Data Access logs: Track data-plane API calls (such as reading Cloud Storage objects). They are disabled by default (except for BigQuery) and incur log ingestion and storage charges once enabled.
The API Ecosystem and Committed Use Discounts (CUDs)
- APIs require explicit enablement; disabling them is destructive: AWS accounts enable all services by default. In GCP, each Project must explicitly enable APIs before use (e.g.,
container.googleapis.com), typically managed declaratively in Terraform viagoogle_project_service. Critically, disabling an API (such as disabling the Cloud Run API) is a destructive operation that permanently deletes all existing instances and resources managed under that service within the project. - Flexible cross-service discounts (CUDs): While AWS Savings Plans come in Compute, EC2 Instance, and SageMaker variants, GCP’s Committed Use Discounts (CUDs) provide both resource-based commitments for Compute Engine and Spend-based Flexible CUDs. The latter applies commitments simultaneously across Compute Engine, GKE, and Cloud Run, offering greater purchasing flexibility for mixed architectures.