---
cuppy_hub: gcp
cuppy_path: guides/parity/identity.md
cuppy_section: docs
cuppy_doc: 58DQ-2GBP
cuppy_doc_uuid: b3274c89-d1bc-4424-a220-192de39dc916
cuppy_permalink: https://cuppy.io/gcp/d/58DQ-2GBP
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/gcp/docs/guides/parity/identity
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: e2e40dec9caff57321789346e16560ec41545d71d2b2f94937e40fabc7006762
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Identity parity

## 1. The names

| Google Cloud | AWS | Azure / Microsoft |
|---|---|---|
| IAM (role-based access control) | AWS IAM | Azure RBAC |
| Cloud Identity | IAM Identity Center | Microsoft Entra ID |
| service account | IAM role (workloads) / IAM user | managed identity, or app registration + service principal |
| Workload Identity Federation | OIDC federation + `AssumeRoleWithWebIdentity` | workload identity federation (a **federated identity credential**) |
| Secret Manager | Secrets Manager | Azure Key Vault (secrets) |
| Cloud KMS | AWS KMS | Azure Key Vault (keys) |

Both tables agree on these rows. **The one-to-two row matters:** Microsoft maps *both* Cloud KMS and Secret
Manager to **Azure Key Vault**. Google Cloud splits the two jobs into two services with separate IAM surfaces
and separate quota models. Either way, that is a service boundary appearing or disappearing, not a rename.

## 2. The grant model — the difference that rewrites the policy code, not just the names

| | Google Cloud | AWS | Azure |
|---|---|---|---|
| what you write | a **role binding** in a resource's **allow policy**: principal + role + optional condition | a **JSON policy document**, attached to an identity or to a resource | a **role assignment**: principal + role definition + scope |
| who holds it | the resource (and anything above it in the hierarchy) | identity-attached, resource-attached, or both | the scope |
| how a grant is narrowed | a **deny policy** or a Principal Access Boundary, both separate objects | an explicit `Deny` statement **inside the same policy language** | a deny assignment (created by Azure, not generally by you) |
| what inherits | policy inherits **down** org → folder → project → resource | an SCP constrains **down** an OU tree; identity policies do not inherit | an assignment applies **down** management group → subscription → resource group |

**Estimate consequence.** Google Cloud and Azure are both "bind a named role at a scope", so role mapping
between them is mechanical — the work is in the role catalogue, not the model. AWS is a different language:
one policy document expresses conditions, wildcards and explicit denials that Google Cloud needs **three
object types** for (a condition on a binding, a deny policy, a PAB policy). Translating AWS policy JSON into
bindings is a decomposition: budget it **per policy document**, not per role. It is the most
under-estimated task in an AWS-to-Google-Cloud move.

## 3. The ceilings, and which cloud lets you raise them

| Thing | Google Cloud | AWS | Azure |
|---|---|---|---|
| grants at one scope | **1,500 principals per allow policy**, "Limits cannot be changed" | managed policies per role 20, **raisable to 25** | **5,000 role assignments per subscription**, "fixed and cannot be increased"; **500 per management group**, also fixed |
| custom roles | **300 per project and 300 per organization**, not changeable | customer managed policies **1,500 per account, raisable to 10,000** | **5,000 custom roles per directory** (2,000 for Azure operated by 21Vianet) |
| workload identity objects | no quota on pools stated on the IAM quotas page | **OIDC providers 100 per account, raisable to 700** | federated identity credentials per object (see Microsoft's limits page) |
| credential lifetime | access token **3,600 s**, extendable to 12 h by organization policy | role session **1 h default, up to 12 h**, not raisable past 12 | access token lifetime governed by Entra policy |
| long-lived keys | **10 service account keys per account** | access keys per user (2) | app registration secrets/certificates |

Three readings an estimate depends on:

1. **AWS publishes an "automatically approved" increase table; the other two publish fixed limits.** A design
   that leans on raising an identity ceiling works on AWS and must be redesigned on Google Cloud and Azure.
2. **The ceilings are not comparable units.** 1,500 *principals in one policy*, 5,000 *role assignments in a
   subscription* and 20 *managed policies on one role* count three different things. Express the estimate as
   **principals × scopes** to convert; the arithmetic is in `guides/parity-and-migration-method.md`.
3. **All three answer the same way**: group-based grants. Azure's own first remedy is "Replace principal-based
   role assignments with group-based role assignments", and Google Cloud's 1,500-principal ceiling forces the
   same conclusion. A migration that lands principal-by-principal lands at the ceiling.

The two Azure strings a lift-and-shift actually hits:

```
No more role assignments can be created (code: RoleAssignmentLimitExceeded)
Role definition limit exceeded. No more role definitions can be created (code: RoleDefinitionLimitExceeded)
```

## 4. Where a workload's identity lives — the regional and attachment model

| | Google Cloud | AWS | Azure |
|---|---|---|---|
| identity object scope | a service account is a **project**-level, global object | an IAM role is **account**-level and global | a managed identity is a **regional resource in a resource group**; an app registration is tenant-level |
| how a workload gets it | **attach** the service account to the VM / Cloud Run service / job | **attach** an instance profile or execution role | **assign** a system- or user-assigned managed identity |

**Estimate consequence.** A user-assigned managed identity is regional, so it is part of a region's blast
radius and of its resource count; Google Cloud and AWS identities are not. A Google-Cloud-to-Azure move
therefore multiplies identity objects by the number of regions — the kind of row that silently triples a
"like-for-like" count.

## 5. Keyless federation — same idea, three different things validated

| | Google Cloud | AWS | Azure |
|---|---|---|---|
| objects to create | **2**: a workload identity pool and a pool provider | **1**: an IAM OIDC identity provider (plus a trust policy on each role) | **1 per trust**: a federated identity credential on a managed identity or app registration |
| where the trust condition lives | `--attribute-condition` on the **provider** | the **role's trust policy** (condition keys on `sub`, `aud`) | the credential's `issuer` / `subject` / `audience` fields |
| matching rule | attribute mapping expressions, then the condition | policy conditions | **exact, case-sensitive** on issuer, subject, audience |
| can the workload skip a service account? | **yes** — direct resource access is the recommended path | no: the token always maps to an IAM role | no: the token always maps to a managed identity or app |
| documented gotcha | "some APIs have limitations" with federation | a **five-minute clock-skew window** past the JWT `exp` claim | only the **first 100 signing keys** from the external IdP are stored; Entra-issued tokens may **not** be used in this flow |

**Estimate consequence — the biggest saving in this guide.** Google Cloud is the only one of the three where a
federated workload can be bound **straight onto the resource**; AWS and Azure always interpose an identity
object. So moving **to** Google Cloud can delete one object per workload, if every API that workload calls
supports federation (check first — the vendor says some do not); moving **from** Google Cloud where direct
resource access was used, you must **create** one identity object per workload on the far side.

Azure's exact, case-sensitive matching on three fields is the usual first-attempt failure when a workflow that
worked against Google Cloud's expression-based mapping is pointed at Azure.

## 6. Pricing dimension (never a price)

The control plane is free on all three. What is metered differs, and the dimension is what an estimate needs:

| | Google Cloud | AWS | Azure |
|---|---|---|---|
| secrets | per secret **version per location**, plus access operations | per secret per month, plus API calls | per secret operation |
| keys | per key version per month, plus cryptographic operations, priced by protection level | per key per month plus requests | per key plus operations, priced by tier |

Read every figure live. Google Cloud's *per version per location* metering is the row that matters: the
replication policy chosen in `reference/identity/secret-manager-and-kms.md` is a cost decision as well as a
residency one.

## 7. What this guide cannot tell you

Whether a predefined role on one cloud grants the same *set* of operations as its counterpart. It does not,
and no published table claims it does. Closing that gap means enumerating the permissions the workload
actually used, then mapping permission-to-role on the target: `guides/parity-and-migration-method.md`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 91 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/gcp/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
