Business Challenge
#40 removed the key and #41 removed the need to hold one. This removes the Google Cloud identity as a thing you provision at all.
Not for this mechanism. Your workloads use AWS temporary security credentials — IAM roles or instance profiles — and Google Cloud verifies them using the AWS GetCallerIdentity API. You do not need to make any configuration changes in your AWS account for this option.
Configure the pool and provider on the Google side and leave AWS alone. The trust is established by Google asking AWS who the caller is, not by AWS being told to trust Google.
It can, but that is no longer the recommendation. Google recommends that you grant direct resource access to the principal — and in that case, the principal is the federated user. Impersonation is the fallback for APIs with limitations.
Correct approachGrant the role to the federated principal on the resource. Reach for a service account only when a specific API forces it.
AWS role names are reusable, and federation has no directory to tell two of them apart. Delete an identity and recreate one sharing its attributes, and the old and new will be indistinguishable to Workload Identity Federation — so a binding that was meant to only refer to the old one might apply to the new one too.
Correct approachMap attributes that cannot be reused over time. This is the single most important decision in the configuration and it is made once, invisibly, in a mapping expression.
It is also how subjects collide. Google's guidance is explicit: use a single provider per workload identity pool to avoid subject collisions.
Correct approachOne pool per provider, in a dedicated project, with an organization policy constraint stopping pools being created elsewhere.
Architecture
The AWS mechanism is unusual among federations because the verification runs in the opposite direction from the one you expect.
What arrives, and what you do with it
The AWS assertion carries three things: account (the AWS account number), arn (the ARN of the external entity) and userid. An attribute mapping turns those into a Google Cloud identity. The documented mapping for an EC2 instance with an attached role is:
| Mapping | Produces |
|---|---|
google.subject=assertion.arn |
The subject — a unique identifier for the user, used in IAM principal:// role bindings, and appearing in Cloud Logging logs. |
attribute.account=assertion.account |
The AWS account ID as a custom attribute. |
attribute.aws_role=assertion.arn.extract(...) |
Introduces a custom attribute aws_role and assigns it the AWS role name. |
attribute.aws_ec2_instance=... |
The EC2 instance ID, pulled out of the same ARN. |
From a single ARN like arn:aws:sts::000000000000:assumed-role/ec2-my-role/i-00000000000000000, the mapping produces four separately grantable facts. You can then bind a role to every workload in that AWS role, or to one specific EC2 instance, using a principalSet:// identifier that names the attribute and its value — the same principal type from #31, pointed at a pool rather than a project.
Two ways to use the result
| Direct resource access | Service account impersonation | |
|---|---|---|
| Who the principal is | The federated user itself. | The Google Cloud service account. |
| Where the grant lives | On the resource, naming the federated principal. | On the resource, naming the service account. |
| Status | Recommended. | For APIs with documented limitations. |
Direct access is the more interesting of the two, because it means an AWS workload appears in a Google Cloud allow policy as itself. No service account exists to be audited, disabled, or accidentally granted Editor. The #39 checklist does not apply, because there is nothing there to check.
An attribute condition is a CEL expression that can check assertion attributes and target attributes. Its evaluation rule is stated plainly: if the condition evaluates to true for a given credential, the credential is accepted — otherwise, the credential is rejected. Compare #36, where a denial condition that cannot be evaluated applies the deny. Both fail safe, but by opposite routes: the deny rule fires when it cannot decide, and the federation condition refuses when it cannot confirm. Same instinct, mirrored, because one is subtracting access and the other is admitting an identity.
Why This Architecture Holds Up
The sentence that governs everything else is this: Workload Identity Federation does not maintain a directory of user accounts; instead, it implements claims-based identities. The consequence is stated just as directly — when two tokens are issued by the same identity provider and their claims map to the same google.subject value, the two tokens are assumed to identify the same user.
There is no lookup. Identity is the mapped claim. Which produces an exact inversion of something established four posts ago.
| Delete it, then recreate it with the same name | Result |
|---|---|
| A Google Cloud service account (#39) | Inherits nothing. Bindings use an immutable unique ID, so the old grants do not apply. |
| A federated external identity | Inherits everything. The old and new are indistinguishable, so a binding meant for the old one may apply to the new one. |
Same scenario, opposite outcome, and the reason is ownership of the identifier. Google mints the unique ID behind a service account and can guarantee it is never reissued. It has no such control over an AWS ARN or an email address in somebody else's directory, so it cannot promise the identifier means one thing forever — and it tells you so rather than pretending.
That pushes the guarantee onto the mapping expression, which is why the best-practice list reads the way it does. Use attributes that cannot be reused over time. Use attributes the user cannot modify, because bad actors might be able to gain unauthorized access to other resources by deliberately modifying their user attributes to match existing IAM bindings. Do not let the mappings themselves be changed. Each of those is a sentence about a CEL expression that somebody writes once during setup and nobody reviews again.
What to do with this
- Map on something non-reusable. A role name can be deleted and recreated; treat that as the default assumption rather than the edge case.
- Treat the attribute mapping as production security config. It is the entire trust relationship, expressed in one field.
- Prefer direct resource access. It is the recommendation, and it removes the service account from the design entirely.
- One provider per pool, in a dedicated project, with pool creation elsewhere blocked by organization policy.
- Remember the AWS side is untouched. Nothing in your AWS account records that this trust exists, so the documentation for it has to live somewhere you will look.
- Check what the subject will be before granting anything — it is what appears in Cloud Logging, so it is what an investigation will have to work from.
Key Architecture Decisions
| Decision | Choose this | Because |
|---|---|---|
| An AWS workload needs Google Cloud access | Federation, not a key | Google verifies the AWS credentials it already has. |
| Configuration on the AWS side | None required | Verification runs through GetCallerIdentity. |
| How the workload gets permissions | Direct resource access | It is the recommendation; the principal is the federated user. |
| Service account impersonation | Only for limited APIs | It is the documented fallback, not the default. |
What to map to google.subject |
Something non-reusable | Recreated identities are otherwise indistinguishable. |
| Attributes a user can edit | Never map them | They can be modified to match an existing binding. |
| Pool and provider layout | One provider per pool | It is how subject collisions are avoided. |
| Where pools live | A dedicated project | Creation elsewhere can be blocked by org policy. |
| Granting a whole AWS role | principalSet:// on attribute.aws_role |
The mapping makes the role name separately grantable. |
Closing Thought
Federation is usually sold as the end of a credential problem, and for the workload it is: nothing is issued, nothing is stored, nothing expires into an outage. But the problem has not disappeared so much as changed form. A service account key was a secret you had to protect. A federated identity is an assertion you have to be able to trust, and trusting it means trusting that the attribute you mapped means the same thing next year as it does today.
Google is unusually candid about this. It does not claim to maintain a directory; it says outright that identity here is whatever the claims map to, and then spends a best-practices page explaining what that costs. The whole list — immutable attributes, non-reusable attributes, unmodifiable mappings, one provider per pool — is really one instruction repeated: the identifier is now yours to choose, and nobody downstream can tell a good choice from a bad one. A service account key at least announced itself as dangerous by being a file. A mapping expression looks like configuration.
#44 takes federation to the place it is most used and most misconfigured: OIDC providers and CI systems — GitHub Actions, Terraform Cloud, and why a single shared issuer URL makes the attribute condition non-optional.
Comments