Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

IAM: AWS vs Azure vs GCP — Visual Comparison

1 — The spectrum (the one idea to remember)

The difference is where the permission record physically lives: attached to the person, or attached to the resource.

◄ permission lives on the IDENTITY permission lives on the RESOURCE ►
AWS Policy attached to the user/role
Azure Role assignment lives at a scope, identity lives in Entra ID
GCP Binding lives on the resource

Azure and GCP are both resource-centric. AWS is the outlier — it is principal-centric.


2 — Where does the permission live?

AWS — on the Identity

Alice Policy doc
Allow s3:GetObject
Allow redshift:Query
bucket-A← no record of Alice

Alice carries her permissions. Resources are passive.

Azure — at a Scope

Entra ID Alice (no perms)
Storage acct
Role assignment
Alice+Blob Reader

Identity in Entra ID; the assignment object lives at the resource scope.

GCP — on the Resource

Alice (just an identity)
bucket-A
Binding
aliceobjectViewer

Resource owns the access list directly.

Audit question: "Who has access to bucket-A / the storage account?"

AWS → scan IAM policies of every user, role & group + the bucket's own policy. Scattered.

Azure → az role assignment list --scope <resource-id>. One scope.

GCP → gcloud storage buckets get-iam-policy gs://bucket-A. One resource.

Why Azure sits in the middle: the identity (Alice) lives in a separate directory — Entra ID (formerly Azure AD) — exactly like AWS keeps identities in IAM. But the permission (the role assignment) is a standalone object pinned to a scope (resource, resource group, subscription, or management group), exactly like GCP pins a binding to a resource. So Azure is resource-centric for grants but has a separate identity plane.


3 — Real-life scenario

Scenario: Data engineer Alice needs access to a pipeline

Requirements:
• READ objects from storage raw-data (bucket / blob container)
• WRITE / query the analytics warehouse (BigQuery / Redshift / Synapse)
• Nothing else — least privilege

AWS — attach a policy to the identity

1
Write a policy document (JSON) listing allowed actions + ARNs
2
Create the policy in IAM, then attach it to Alice (or a role she assumes)
3
Verify against Alice's identity
# alice-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject","s3:ListBucket"], "Resource": ["arn:aws:s3:::raw-data","arn:aws:s3:::raw-data/*"] }, { "Effect": "Allow", "Action": ["redshift-data:ExecuteStatement"], "Resource": "arn:aws:redshift:eu-west-1:123:cluster:wh" } ] }

1+2. create & attach

aws iam create-policy --policy-name AliceDataPipeline
--policy-document file://alice-policy.json aws iam attach-user-policy --user-name alice
--policy-arn arn:aws:iam::123456789012:policy/AliceDataPipeline

3. verify (by identity)

aws iam list-attached-user-policies --user-name alice

The policy is attached to Alice. The bucket/cluster have no record of her unless you add resource policies too.

Azure — assign a role at the resource scope

1
Pick a built-in role per service (no policy doc to write)
2
Create a role assignment: principal + role + scope
3
Verify by scope or by assignee
# 1+2. assign roles at each resource scope az role assignment create \ --assignee alice@company.com \ --role "Storage Blob Data Reader" \ --scope "/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts/"

az role assignment create
--assignee alice@company.com
--role "Synapse SQL User"
--scope "/subscriptions//resourceGroups//providers/Microsoft.Synapse/workspaces/"

3. verify — by resource scope

az role assignment list --scope "/subscriptions//.../storageAccounts/"

...or by identity

az role assignment list --assignee alice@company.com

Built-in roles cover most needs (like GCP predefined roles). Identity stays in Entra ID; the grant lives at the scope.

GCP — add a binding on each resource

1
Add a binding on the bucket — no policy document created
2
Add a binding on the BigQuery dataset
3
Verify with get-iam-policy per resource
# 1. on the bucket gcloud storage buckets add-iam-policy-binding gs://raw-data \ --member="user:alice@company.com" \ --role="roles/storage.objectViewer"

2. on the BigQuery dataset

bq add-iam-policy-binding
--member="user:alice@company.com"
--role="roles/bigquery.dataEditor"
project:ingestion_ds

3. verify (per resource)

gcloud storage buckets get-iam-policy gs://raw-data bq get-iam-policy project:ingestion_ds

Permissions stored on the resources. Alice's identity stays clean.


4 — The audit difference (why it matters in practice)

Answering access questions

Question
AWS
Azure
GCP
Who can read raw-data?
Scan all user/role/group policies + bucket policy
role assignment list --scope — one call
get-iam-policy on the bucket — one call
What can Alice access?
simulate-principal-policy — easy, policies on Alice
role assignment list --assignee — easy, queryable
Policy Analyzer — must scan bindings across resources
Remove Alice everywhere
Detach her policy — done
Delete her role assignments (queryable by assignee)
Remove binding from every resource she was on
The real tradeoff

AWS: easy to audit a person (all perms in one place), hard to audit a resource (perms scattered).

GCP: easy to audit a resource (all bindings on it), harder to audit a person across everything.

Azure: best of both for auditing — role assignments are first-class objects you can query by scope OR by assignee — but at the cost of an extra object type and a separate identity plane (Entra ID).


5 — Quick reference

DimensionAWSAzureGCP
CentricityPrincipal-centricResource-centric (grant) + separate identityResource-centric
Identity planeIAM users/rolesEntra ID (Azure AD)Google / Cloud Identity
Grant unitPolicy doc attached to identityRole assignment = principal + role + scopeBinding = (resource, role, member)
Role definitionsManaged / customer policiesBuilt-in / custom rolesPredefined / custom roles
Scope hierarchyAccount; SCPs as ceilingMgmt Group → Subscription → RG → ResourceOrg → Folder → Project → Resource
InheritancePer-account (no cross-account)Inherits down the scope chainInherits down the hierarchy
Explicit denyYes — deny always winsDeny assignments (limited; via Blueprints/managed apps)IAM Deny (separate, newer feature)
Workload identityIAM Role (STS assume)Managed Identity / Service PrincipalService Account (identity + resource)