Real organizations rarely use only one cloud. Engineers fluent in one provider but not the others write policies that drift in subtle ways — the AWS read-only role grants more than the equivalent Azure role, or vice versa — and the inconsistency becomes an audit finding.
This lab walks four common scenarios and writes each one in AWS IAM JSON, Azure RBAC, and GCP IAM bindings. Read all three columns. Notice where the syntax differs but the intent matches, and where the intent itself shifts because of how each provider models permissions.
Read-only access to one storage bucket
Intent: Group "DataAnalysts" can list and read objects in the bucket named reports — nothing else, no other buckets, no write access.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::reports",
"arn:aws:s3:::reports/*"
]
}]
}
ListBucket; the object-level ARN (with /*) is needed for GetObject.# Built-in role: Storage Blob Data Reader Role: Storage Blob Data Reader Scope: /subscriptions/<id>/ resourceGroups/data/ providers/Microsoft.Storage/ storageAccounts/reports Principal: group:DataAnalysts
{
"bindings": [{
"role": "roles/storage.objectViewer",
"members": [
"group:data-analysts@example.com"
]
}]
}
# Applied to: bucket "reports" only
storage.objectViewer. Binding attaches to the bucket, not the project — that's the scoping.What diverges
AWS grants permissions through verbs (Get, List). Azure uses named built-in roles that bundle verbs. GCP uses predefined roles. All three map cleanly here — this is the easy case.Full admin access to a single project / account / subscription
Intent: Alice should have administrative control over one specific environment (one AWS account, one Azure subscription, one GCP project), and nothing in any other environment.
# Federated through IAM Identity Center Permission set: AdministratorAccess Account assignment: 111122223333 Principal: alice@example.com # Or directly on the role: { "Effect": "Allow", "Action": "*", "Resource": "*" }
AdministratorAccess AWS-managed policy) still works but is discouraged.Role: Owner Scope: /subscriptions/<subscription-id> Principal: alice@example.com
Owner role at subscription scope. Beware: Owner can also grant access to others — "admin" in Azure usually means "User Access Administrator" + a less-permissive role for actual operations.{
"bindings": [{
"role": "roles/owner",
"members": [
"user:alice@example.com"
]
}]
}
# At: projects/payments-prod
roles/owner. Project-scoped binding. GCP's Owner is broad — consider resourcemanager.projectIamAdmin + service-specific admin roles instead.What diverges
The scoping unit differs. AWS scopes at the account level (one Identity Center permission set per account assignment). Azure scopes within a subscription's URL path. GCP scopes within a project. The same intent — "admin one environment" — lives in three different namespaces.Workload identity for an application reading secrets
Intent: An application running on the cloud's compute platform needs to read a specific secret from the cloud's secrets manager — without any downloaded credential file.
# Role attached to the EC2 instance / Lambda / ECS task { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:db-creds-*" }] }
# System-assigned managed identity on the resource Role: Key Vault Secrets User Scope: /subscriptions/<id>/.../ vaults/db-creds-vault Principal: managed-identity:<principal-id>
{
"bindings": [{
"role": "roles/secretmanager.secretAccessor",
"members": [
"serviceAccount:app-prod@my-project.iam.gserviceaccount.com"
]
}]
}
# Bound to: secret "db-creds"
What diverges
All three converged on the same pattern. No long-lived keys; the workload gets credentials from the platform via an identity bound to the resource. The vocabulary is different (instance role, managed identity, service account) but the architecture is identical.Conditional access — only from a specific network
Intent: Allow read access to a sensitive bucket only when the request comes from the corporate VPN range (203.0.113.0/24) or from a workload running inside the cloud account itself.
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::sensitive/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
}
}
}
aws:SourceVpce or aws:SourceVpc.# Conditional Access policy (Entra) Users: all Apps: Storage Conditions: Location: Include: Corporate VPN range Grant: Allow # + Storage account firewall Storage firewall: 203.0.113.0/24
# IAM Condition with CEL { "role": "roles/storage.objectViewer", "members": ["group:analysts@..."], "condition": { "expression": "request.ip.matches('203.0.113.0/24')" } } # Stronger: VPC Service Controls perimeter
What diverges
This is where the providers genuinely differ. AWS expresses network restrictions as IAM condition keys, baked into the policy. Azure splits it across Conditional Access (the IdP layer) and storage firewalls (the resource layer). GCP gives you IAM Conditions for simple cases and VPC Service Controls for the strong case — a discrete product with no AWS/Azure equivalent.Patterns that generalize
After walking the four scenarios, certain patterns become visible across all three providers:
- Identity → role → permission → resource. Every provider models access through some chain of these. Names differ; the architecture doesn't.
- Built-in roles beat raw permissions. AWS managed policies, Azure built-in roles, GCP predefined roles. Use them where they fit. Custom roles are for the cases where they don't.
- Workload identity replaces downloaded keys. AWS IAM Roles, Azure Managed Identities, GCP Service Accounts via Workload Identity — all the same idea.
- Conditions add context. All three support conditional access on attributes (IP, time, tag). The expressiveness varies; the concept is universal.
- Network and identity must be designed together. All three providers have separate network controls (security groups, NSGs, firewall rules) that complement identity. The provider's preferred composition differs — AWS conditions vs. Azure CA + storage firewall vs. GCP VPC-SC — but the layering principle is the same.
Cloud IAM is multi-dialect, not multi-language. The mental models are essentially identical across the four major providers; the vocabulary, the scoping conventions, and which controls are bundled together differ. Practitioners who can read all three quickly diagnose multi-cloud problems that single-cloud experts get stuck on.
When you're translating a policy from one provider to another, the questions to ask are always the same: who is the principal? what action? on what resource? under what conditions? The answers are written in different syntax in each cloud, but the underlying intent is portable.
References
Formatted in APA 7. Alphabetized by first author's last name.
- Amazon Web Services. (n.d.). IAM JSON policy reference. https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html
- Google Cloud. (n.d.). Policy reference: IAM. https://cloud.google.com/iam/docs/reference/rest/v1/Policy
- Microsoft. (n.d.). Azure built-in roles. Azure RBAC documentation. https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles