# GCP Associate Cloud Engineer (ACE) — Part 3: IAM & Identity

> **Series:** GCP Associate Cloud Engineer (ACE) (3 of 8)
> **Part 1:** `01-fundamentals-and-resource-hierarchy.md` (Fundamentals, Resource Hierarchy & Cloud Identity)
> **Part 2:** `02-billing-and-gcloud-tooling.md` (Billing, gcloud CLI & Cloud Tooling)
> **Part 3:** This file (IAM & Identity)
> **Part 4:** `04-compute-engine-and-autoscaling.md` (Compute Engine & Autoscaling)
> **Part 5:** `05-gke-and-serverless.md` (GKE, Serverless & the Agent Platform)
> **Part 6:** `06-storage-and-managed-databases.md` (Storage & Managed Databases)
> **Part 7:** `07-networking-fundamentals.md` (Networking Resources)
> **Part 8:** `08-monitoring-logging-and-operations.md` (Monitoring, Logging & Operations)
> **Questions:** `questions.md`

> Assumes you're comfortable with Part 1's resource hierarchy and inheritance model. This chapter is the entirety of the ACE exam's "configuring access and security" domain (~20% of the exam on its own), so it earns the deepest treatment of any single chapter in this course.

## Table of Contents

1. [What This Chapter Covers](#what-this-chapter-covers)
2. [IAM's Core Model: Principals, Roles, Permissions, Policies](#iams-core-model-principals-roles-permissions-policies)
3. [Basic, Predefined, and Custom Roles: Choosing the Right Type](#basic-predefined-and-custom-roles-choosing-the-right-type)
4. [Policy Inheritance Across the Resource Hierarchy](#policy-inheritance-across-the-resource-hierarchy)
5. [IAM Conditions: Scoping Access Beyond the Role Itself](#iam-conditions-scoping-access-beyond-the-role-itself)
6. [Service Accounts: Creation, Types, and Google-Managed Accounts](#service-accounts-creation-types-and-google-managed-accounts)
7. [Service Account Impersonation and Short-Lived Credentials](#service-account-impersonation-and-short-lived-credentials)
8. [Service Account Keys: Why They're Disabled by Default Now](#service-account-keys-why-theyre-disabled-by-default-now)
9. [Workload Identity Federation for External Workloads](#workload-identity-federation-for-external-workloads)
10. [Workload Identity Federation for GKE](#workload-identity-federation-for-gke)
11. [Workforce vs. Workload Identity Federation: The Distinction That Matters](#workforce-vs-workload-identity-federation-the-distinction-that-matters)
12. [A Full Worked Example: Meridian's IAM Model End to End](#a-full-worked-example-meridians-iam-model-end-to-end)
13. [Real-World Scenario: The Service Account Key Found in a Public Repository](#real-world-scenario-the-service-account-key-found-in-a-public-repository)
14. [Second Real-World Scenario: The Impersonation Chain Nobody Could Audit](#second-real-world-scenario-the-impersonation-chain-nobody-could-audit)
15. [Part 3 gcloud IAM Cheat Sheet](#part-3-gcloud-iam-cheat-sheet)
16. [Pre-Flight Checklist: Is This IAM Model Actually Production-Ready?](#pre-flight-checklist-is-this-iam-model-actually-production-ready)
17. [Common Mistakes and Interview Traps](#common-mistakes-and-interview-traps)
18. [Worked Practice Problems](#worked-practice-problems)
19. [Summary and What's Next](#summary-and-whats-next)

## What This Chapter Covers

🎯 By the end of this chapter, you'll be able to design an IAM model that grants every human and every workload exactly the access it needs, nothing more, using groups instead of individuals, short-lived credentials instead of keys, and federation instead of a directory entry wherever one will do.

This chapter is, on its own, the ACE exam's entire "configuring access and security" domain: managing IAM (policies, role types, inheritance) and managing service accounts (creation, minimum permissions, impersonation, short-lived credentials, Workload Identity Federation). Nothing here is optional exam ground; every subsection maps to a named exam bullet.

## IAM's Core Model: Principals, Roles, Permissions, Policies

Four terms, precisely defined, that the exam expects you to use correctly rather than interchangeably:

| Term | What it is |
|---|---|
| **Permission** | The smallest unit, a specific allowed API operation (`compute.instances.start`) |
| **Role** | A named bundle of permissions (`roles/compute.instanceAdmin.v1` bundles dozens of `compute.*` permissions) |
| **Principal** | Who or what is being granted access: a user, a group, a service account, or a federated identity |
| **Policy** | The actual binding: which principal(s) get which role(s), attached to a specific resource |

A **policy** is the object that ties everything together: `{ role: "roles/editor", members: ["group:gcp-freight-team@meridianlogistics.com"] }`, attached to a specific project, folder, or organization. GCP evaluates a principal's *effective* permissions as the union of every policy binding that applies to them, directly or through inheritance, there's no "explicit deny wins" default the way some systems work; a `Deny` policy (a separate, newer mechanism layered on top of the classic allow-only model) is required if you specifically need to block access regardless of what an allow policy elsewhere grants.

## Basic, Predefined, and Custom Roles: Choosing the Right Type

| Role type | Granularity | Example | When to use |
|---|---|---|---|
| **Basic** (legacy: Owner, Editor, Viewer) | Extremely coarse, thousands of permissions across every service | `roles/editor` | Almost never in production; a personal sandbox project at most |
| **Predefined** | Scoped to one service or task, Google-maintained and updated as the service evolves | `roles/compute.instanceAdmin.v1`, `roles/storage.objectViewer` | The default choice for the vast majority of real access grants |
| **Custom** | Exactly the permission set you define, capped at 64 KB per role | `roles/meridian.dispatchOperator` | When no predefined role matches your actual least-privilege need, and the gap is worth the ongoing maintenance |

> [!TIP]
> **Best Practice: reach for a predefined role first, always.** Google maintains predefined roles as services gain new permissions, meaning your access grant stays correctly scoped as GCP evolves. A custom role is a maintenance commitment: it's a snapshot of permissions at the moment you wrote it, and it's your job (not Google's) to notice when a service adds a new permission your custom role should probably include. Build custom roles only when a documented gap exists between a predefined role and your actual least-privilege requirement, and document why the gap exists.

A custom role, once you've confirmed the gap is real, is defined as an explicit permission list rather than inherited from any existing role:

```yaml
title: "Meridian Dispatch Operator"
description: "Read shipment data and publish dispatch events, nothing else"
stage: "GA"
includedPermissions:
  - pubsub.topics.publish
  - bigquery.tables.getData
  - bigquery.jobs.create
```

```bash
gcloud iam roles create meridianDispatchOperator \
  --project=meridian-freight-prod-8f2k \
  --file=dispatch-operator-role.yaml
```

Basic roles deserve one specific warning: **`roles/editor` includes the ability to modify IAM policies on the resources it covers in many contexts and grants access to nearly every service**, so granting it "just to keep things simple" for a new hire is a far larger blast radius than the requester usually intends. Meridian's platform team scans for basic-role grants outside personal sandbox projects as a standing Cloud Asset Inventory query, the same pattern from Part 1's shadow-project scenario.

## Policy Inheritance Across the Resource Hierarchy

IAM policies inherit down the resource hierarchy the same way org policies do (Part 1), but the composition rule is different and worth stating precisely: **a principal's effective permissions at a resource are the union of every role bound to them at that resource and at every ancestor above it.** There's no overriding a parent's grant at a lower level the way some org-policy constraints allow; an IAM grant at the Organization node reaches every project beneath it, permanently, until removed at the level it was granted.

```mermaid
flowchart TD
    Org(["Organization"]) -->|"roles/viewer<br/>to group:all-engineers@"| FolderProd["Folder: Production"]
    FolderProd -->|"roles/compute.admin<br/>to group:platform-team@"| ProjFreight[("meridian-freight-prod")]
    ProjFreight -->|"roles/compute.instanceAdmin.v1<br/>to group:freight-oncall@"| VM["A specific Compute Engine VM"]

    Effective["Effective access on the VM for<br/>a platform-team member:<br/>viewer + compute.admin<br/>(both inherited, unioned)"]
    ProjFreight -.-> Effective

    classDef org fill:#f0e9fb,stroke:#6d43c0,color:#10161c
    classDef folder fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef proj fill:#e5f5ea,stroke:#1f8a4c,color:#10161c
    classDef result fill:#fbeee0,stroke:#b8650f,color:#10161c
    class Org org
    class FolderProd folder
    class ProjFreight proj
    class Effective result
```

**Caption:** a platform-team member's effective access on that VM is the union of every role granted anywhere above it in the hierarchy, not just the role granted at the project itself, which is why an audit of "who can touch this VM" has to walk the entire ancestor chain, not just the project's own policy.

⚠️ This union-only model is exactly why the earlier basic-roles warning matters: a stray `roles/editor` grant at the Organization node reaches every single project in the company, forever, until someone finds and removes that specific binding.

## IAM Conditions: Scoping Access Beyond the Role Itself

**IAM Conditions** let you attach a Common Expression Language (CEL) expression to a role binding, restricting when it actually applies, by resource attribute, request time, or (as Part 1 mentioned) a Resource Manager tag value.

```
expression: resource.name.startsWith("projects/_/buckets/meridian-freight-staging")
title: "staging-bucket-only"
```

A role bound with this condition grants access only to resources matching the expression, letting you scope a broad predefined role (`roles/storage.objectAdmin`) down to a specific bucket prefix without writing a custom role at all. Time-bound conditions (`request.time < timestamp("2026-12-31T00:00:00Z")`) are the standard mechanism for **temporary access grants**, a contractor's elevated access that expires automatically rather than depending on someone remembering to revoke it.

## Service Accounts: Creation, Types, and Google-Managed Accounts

A **service account** is an identity for a workload, not a human, used by applications, VMs, and automation to call GCP APIs. Every service account has an email-formatted identifier (`meridian-dispatcher@meridian-freight-prod.iam.gserviceaccount.com`) and can be granted IAM roles exactly like a user or group.

Three categories exist, and mixing them up is a real exam trap:

| Category | Example | Who manages it |
|---|---|---|
| **User-managed** | `meridian-dispatcher@PROJECT.iam.gserviceaccount.com`, created explicitly by you | You: creation, key/impersonation policy, role bindings, deletion |
| **Google-managed, user-visible** | The default Compute Engine service account (`PROJECT_NUMBER-compute@developer.gserviceaccount.com`), auto-created per project | Google creates it; you control its IAM bindings and whether workloads use it |
| **Google-managed, hidden (robot accounts)** | Service agents like `service-PROJECT_NUMBER@gcp-sa-*.iam.gserviceaccount.com`, used internally by GCP services to act on your behalf | Fully Google-managed; you generally never touch these directly |

> [!WARNING]
> **From the Trenches:** the default Compute Engine service account is automatically granted `roles/editor` on its project unless the project was created after Google changed this default (project-wide, this changed for projects created after May 2024, granting no broad default role at all). A team running an older project, migrating a batch-processing VM to a new machine type, discovered their VM's default service account could modify IAM policies, delete Cloud Storage buckets, and touch resources entirely unrelated to the batch job, none of which the job needed. The immediate cause was inheriting a years-old legacy default; the underlying condition was that nobody had audited default service account permissions since the project was first created. The fix: create a purpose-scoped user-managed service account for every workload going forward, and treat "still using the default Compute Engine service account with its legacy broad role" as a finding in every access review.

## Service Account Impersonation and Short-Lived Credentials

**Impersonation** lets a principal (a user or another service account) temporarily act as a target service account, obtaining short-lived credentials through the IAM Credentials API rather than a long-lived key file. The caller needs `roles/iam.serviceAccountTokenCreator` on the target service account; the resulting token defaults to a one-hour lifetime, with a 12-hour maximum.

```bash
# Impersonate a service account for a single gcloud command,
# no key file involved anywhere
gcloud compute instances list \
  --project=meridian-freight-prod-8f2k \
  --impersonate-service-account=meridian-dispatcher@meridian-freight-prod.iam.gserviceaccount.com
```

```mermaid
sequenceDiagram
    participant Eng as Engineer
    participant IAMCred as IAM Credentials API
    participant SA as Target service account
    participant API as GCP API (e.g. Compute Engine)

    Eng->>IAMCred: Request short-lived token,<br/>impersonating SA
    IAMCred->>IAMCred: Check caller has<br/>roles/iam.serviceAccountTokenCreator on SA
    IAMCred-->>Eng: Short-lived access token (max 1 hour)
    Eng->>API: Call API using the short-lived token
    API-->>Eng: Response, audit-logged as<br/>"Eng, acting as SA"
```

**Caption:** the audit trail records both identities, the engineer and the service account they impersonated, which is exactly the accountability a shared static key file can never provide.

💡 Impersonation's audit advantage over a static key is the whole point: every action is logged as "principal X, acting as service account Y," creating a clear chain of accountability that a leaked JSON key (usable by literally anyone who has it, with no record of who) simply cannot offer.

## Service Account Keys: Why They're Disabled by Default Now

A **service account key** is a long-lived JSON credential file that can authenticate as the service account indefinitely, from anywhere, with no expiration unless manually rotated. This is precisely the profile of a security liability: a key committed to a public repository, copied to a personal laptop, or embedded in a container image remains valid until someone notices and revokes it.

**For any Google Cloud organization created on or after May 3, 2024, the `constraints/iam.disableServiceAccountKeyCreation` org policy is enforced by default**, meaning new service account key creation is blocked out of the box, not something a security team has to remember to lock down. Older organizations should apply this constraint explicitly (dry-run first, per Part 1's guidance) rather than assuming it's already in place.

| | Service account key | Impersonation / short-lived credential |
|---|---|---|
| Lifetime | Indefinite until manually revoked | 1 hour default, 12 hour maximum |
| Where it can be used from | Anywhere the file is copied to | Only by a principal explicitly granted Token Creator |
| Audit trail on use | Shows only the service account, not who used the key | Shows both the caller and the impersonated account |
| Exposure if leaked | Full account access until manually revoked | Expires within hours regardless of exposure |

**Choose a service account key only when** you have a genuine, documented reason no alternative fits, most commonly a third-party system entirely outside GCP that can't perform impersonation or federation and requires a static credential; treat every such case as an exception needing its own justification and rotation schedule, not a default.

## Workload Identity Federation for External Workloads

**Workload Identity Federation** lets a workload running outside GCP (in AWS, in Azure, in an on-premises Kubernetes cluster, in a CI/CD pipeline like GitHub Actions) authenticate to Google Cloud using its *own* platform's native identity, mapped into a **workload identity pool**, without a service account key ever being generated or stored.

```mermaid
flowchart LR
    GHA["GitHub Actions workflow<br/>with an OIDC token"] --> Pool["Workload Identity Pool<br/>+ OIDC provider config"]
    Pool --> Map{"Attribute mapping<br/>matches configured condition?"}
    Map -->|"Yes"| Impersonate["Impersonate a target<br/>service account"]
    Map -->|"No"| Deny["Access denied"]
    Impersonate --> GCPAPI["Call GCP APIs with<br/>short-lived credentials"]

    classDef ok fill:#e5f5ea,stroke:#1f8a4c,color:#10161c
    classDef crit fill:#fbe8e6,stroke:#b3261e,color:#10161c
    class Impersonate,GCPAPI ok
    class Deny crit
```

**Caption:** the GitHub Actions workflow never holds a GCP credential of its own; it exchanges a token its own platform already issued for temporary GCP access, gated by an explicit attribute-mapping condition.

Two access patterns exist, worth distinguishing precisely: **direct access** grants IAM roles straight to the federated identity itself (no service account involved at all), while **impersonation-based access** has the federated identity impersonate a specific service account, gaining that account's roles instead. Google's own guidance is to start with direct access for simplicity and fall back to impersonation only when you specifically need the extra indirection (a single service account representing "the CI pipeline" regardless of which specific external identity is calling, for instance).

## Workload Identity Federation for GKE

**Workload Identity Federation for GKE** is the GKE-specific application of the same underlying mechanism: it binds a **Kubernetes service account** (a namespace-scoped identity inside your cluster) to a **Google Cloud service account**, letting Pods authenticate to GCP APIs with no key file mounted into the container and no reliance on the node's own (often overly broad) default service account.

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: dispatcher-ksa
  namespace: freight
  annotations:
    iam.gke.io/gcp-service-account: meridian-dispatcher@meridian-freight-prod.iam.gserviceaccount.com
```

```bash
# Bind the Kubernetes SA to the GCP SA via Workload Identity
gcloud iam service-accounts add-iam-policy-binding \
  meridian-dispatcher@meridian-freight-prod.iam.gserviceaccount.com \
  --role=roles/iam.workloadIdentityUser \
  --member="serviceAccount:meridian-freight-prod.svc.id.goog[freight/dispatcher-ksa]"
```

This closes exactly the gap the "From the Trenches" callout above described: instead of every Pod on a node inheriting whatever broad role the node's default service account happens to carry, each workload gets precisely the GCP permissions its own Kubernetes service account is explicitly bound to, and nothing else.

## Workforce vs. Workload Identity Federation: The Distinction That Matters

Both mechanisms federate an external identity into GCP without a Cloud Identity account or a service account key, and the naming similarity is a deliberate, frequent exam trap:

| | Workforce Identity Federation (Part 1) | Workload Identity Federation |
|---|---|---|
| Federates | **Humans** (employees, contractors, partners) | **Workloads** (a CI pipeline, an application, a Kubernetes Pod) |
| Typical IdP | Okta, Azure AD, a SAML/OIDC corporate IdP | GitHub Actions OIDC, AWS/Azure workload identity, a Kubernetes cluster's own OIDC issuer |
| Console access | Yes, humans sign in through the console via SSO | No, this is purely for programmatic API calls |
| Requires an Organization resource | Yes | No |

If the exam question describes a person signing in, it's Workforce Identity Federation; if it describes a pipeline, a container, or a script calling an API, it's Workload Identity Federation. The two are not interchangeable and are frequently paired in the same distractor-style question.

## A Full Worked Example: Meridian's IAM Model End to End

```bash
# 1. Create a purpose-scoped service account, never reuse the default
gcloud iam service-accounts create meridian-dispatcher \
  --display-name="Freight Dispatch Service Account" \
  --project=meridian-freight-prod-8f2k

# 2. Grant it exactly the predefined roles it needs, nothing broader
gcloud projects add-iam-policy-binding meridian-freight-prod-8f2k \
  --member="serviceAccount:meridian-dispatcher@meridian-freight-prod-8f2k.iam.gserviceaccount.com" \
  --role="roles/pubsub.publisher"

# 3. Wire up Workload Identity Federation for GKE, no keys anywhere
gcloud iam service-accounts add-iam-policy-binding \
  meridian-dispatcher@meridian-freight-prod-8f2k.iam.gserviceaccount.com \
  --role=roles/iam.workloadIdentityUser \
  --member="serviceAccount:meridian-freight-prod-8f2k.svc.id.goog[freight/dispatcher-ksa]"

# 4. Grant human access to a group, with a time-bound condition
#    for a contractor's temporary elevated access
gcloud projects add-iam-policy-binding meridian-freight-prod-8f2k \
  --member="group:gcp-freight-team@meridianlogistics.com" \
  --role="roles/compute.instanceAdmin.v1" \
  --condition="expression=request.time < timestamp('2026-12-31T00:00:00Z'),title=contractor-engagement-2026"

# 5. Confirm nobody's holding a service account key on this project
gcloud iam service-accounts keys list \
  --iam-account=meridian-dispatcher@meridian-freight-prod-8f2k.iam.gserviceaccount.com
```

## Real-World Scenario: The Service Account Key Found in a Public Repository

An open-source contribution from a Meridian engineer, a small utility script shared publicly on GitHub, was found by an automated secret-scanning bot to contain a hardcoded service account key, committed accidentally eight months earlier during a debugging session and never removed. The key belonged to a service account with `roles/storage.objectAdmin` on Meridian's shared logging bucket.

The immediate symptom was the scanning bot's alert; the immediate cause was the accidental commit; the underlying condition was twofold: the organization hadn't yet enforced `constraints/iam.disableServiceAccountKeyCreation` (this predated Meridian's own adoption of that policy), so creating the key in the first place had been trivially easy, and the key had never been rotated or reviewed in eight months because nothing in Meridian's process treated "service account keys older than 90 days" as a thing worth tracking. The response: immediate key revocation, migration of that service account's workload to Workload Identity Federation, retroactive enforcement of the key-creation-blocking org policy organization-wide, and a new recurring Cloud Asset Inventory query specifically hunting for service account keys older than 90 days.

## Second Real-World Scenario: The Impersonation Chain Nobody Could Audit

During a security review, Meridian's platform team discovered a chain of impersonation: Service Account A had `roles/iam.serviceAccountTokenCreator` on Service Account B, which in turn had the same role on Service Account C, which held the actual production database access. A single compromised credential for Service Account A could, through two hops of impersonation, eventually reach production data, and no single IAM policy binding made that reachability obvious; it only became visible by manually tracing three separate bindings across three separate service accounts.

The underlying condition was that impersonation chains had grown organically, one convenience grant at a time, over roughly a year, with no one ever reviewing the *transitive* reachability the individual grants added up to. The fix Meridian adopted: a quarterly Cloud Asset Inventory query specifically constructing the full impersonation graph across every service account in the organization, flagging any chain longer than one hop for explicit review, treating transitive impersonation reach the same way a security team would treat transitive IAM role inheritance.

## Part 3 gcloud IAM Cheat Sheet

| Task | Command |
|---|---|
| Create a service account | `gcloud iam service-accounts create NAME --project=PROJECT_ID` |
| Grant a role to a principal | `gcloud projects add-iam-policy-binding PROJECT_ID --member=MEMBER --role=ROLE` |
| Grant a role with a condition | Add `--condition="expression=EXPR,title=TITLE"` to the above |
| Impersonate a service account for one command | `--impersonate-service-account=SA_EMAIL` on any gcloud command |
| Grant impersonation rights | `gcloud iam service-accounts add-iam-policy-binding TARGET_SA --role=roles/iam.serviceAccountTokenCreator --member=MEMBER` |
| Bind a Kubernetes SA via Workload Identity | `gcloud iam service-accounts add-iam-policy-binding SA_EMAIL --role=roles/iam.workloadIdentityUser --member="serviceAccount:PROJECT.svc.id.goog[NAMESPACE/KSA_NAME]"` |
| List a service account's keys | `gcloud iam service-accounts keys list --iam-account=SA_EMAIL` |
| Create a custom role | `gcloud iam roles create ROLE_ID --project=PROJECT_ID --permissions=PERM1,PERM2` |

## Pre-Flight Checklist: Is This IAM Model Actually Production-Ready?

- [ ] No basic role (`roles/owner`, `roles/editor`, `roles/viewer`) is bound outside a personal sandbox project
- [ ] Every service account is purpose-scoped; nothing depends on the default Compute Engine service account's legacy broad role
- [ ] Service account key creation is blocked org-wide, with any exception explicitly justified and time-boxed
- [ ] Every workload needing GCP access uses Workload Identity Federation (GKE or external), not a mounted key file
- [ ] Contractor and temporary access uses an IAM Condition with an expiration, not a manual reminder to revoke later
- [ ] Impersonation chains longer than one hop have been reviewed for transitive reachability

## Common Mistakes and Interview Traps

| Mistake | Why it's wrong | What to say instead |
|---|---|---|
| "A predefined role and a custom role provide the same level of control" | Predefined roles are Google-maintained and update as services evolve; custom roles are a static snapshot you own | Predefined roles first; custom roles only for a documented least-privilege gap, with the maintenance burden explicitly accepted |
| "IAM policy inheritance lets a child override a parent's grant" | Effective permissions are the union of every ancestor's grants; there's no lower-level override for a plain allow policy | A grant at any ancestor level reaches everything beneath it permanently, until removed at the level it was granted |
| "Workforce and Workload Identity Federation are the same feature" | One federates humans for console SSO; the other federates workloads for programmatic API access | Workforce for people signing in, Workload for pipelines/applications calling APIs |
| "Service account keys are the standard way to authenticate a workload" | They're a long-lived liability that newer organizations now block by default | Workload Identity Federation or impersonation-based short-lived credentials are the current standard |
| "The default Compute Engine service account is safe to use for any workload" | On older projects it can still carry a broad legacy role | Create a purpose-scoped service account per workload rather than relying on the default |

## Worked Practice Problems

**Problem 1:** Meridian needs to grant a CI/CD pipeline running in GitHub Actions the ability to deploy to a GKE cluster, without storing any GCP credential in GitHub's secrets store. What's the correct mechanism, and which of the two access patterns (direct or impersonation-based) fits best if the pipeline should act with exactly one well-known service account's permissions regardless of which specific GitHub repository triggered it?

*Answer:* Workload Identity Federation, configured with a workload identity pool trusting GitHub Actions' OIDC token issuer. Since the requirement is for the pipeline to act as one consistent, well-known service account regardless of the calling repository, the impersonation-based access pattern fits better than direct access: the federated GitHub identity impersonates a single target service account, which holds the actual GKE deployment permissions, rather than each repository's federated identity needing its own direct IAM grants.

**Problem 2:** An engineer requests `roles/editor` on a project "to avoid getting blocked by permission errors while building out a new feature." What's wrong with granting this, and what should happen instead?

*Answer:* `roles/editor` is a basic role covering thousands of permissions across nearly every GCP service, far beyond what any single feature realistically needs, and it can grant the ability to modify IAM policies on covered resources in some contexts, a significant escalation risk. The correct response is to identify the specific predefined roles the feature actually requires (likely something like `roles/compute.instanceAdmin.v1` plus `roles/pubsub.publisher`, scoped to what the feature touches) and grant those instead, adding more only if a genuine, specific permission gap surfaces.

**Problem 3:** During an audit, Meridian finds Service Account A can impersonate Service Account B, which can impersonate Service Account C, which holds production database access. No single IAM binding shows this reachability. Is this a misconfiguration, and how should it be found before an audit stumbles onto it by chance?

*Answer:* It's not a misconfiguration in the sense of any single binding being wrong; each individual `roles/iam.serviceAccountTokenCreator` grant may well have been a deliberate, reasonable decision at the time. The real gap is that nobody was tracking the *transitive* reachability those grants compose into. The fix is a standing, recurring Cloud Asset Inventory query that reconstructs the full impersonation graph across every service account and flags any chain longer than one hop, so this kind of compounding risk surfaces on a schedule rather than only during a manual audit.

## Summary and What's Next

This chapter covered the ACE exam's entire access-and-security domain: IAM's principal/role/permission/policy model, choosing between basic, predefined, and custom roles, how policy inheritance composes as a strict union up the resource hierarchy, IAM Conditions for scoping access by resource or time, and the full service-account lifecycle, from Google-managed defaults through impersonation, short-lived credentials, and Workload Identity Federation as the replacement for long-lived keys.

With identity and access now fully covered, **Part 4** shifts to the first concrete compute platform: Compute Engine, its disk options (including the newer Hyperdisk family), autoscaling via managed instance groups, and the operational tasks (snapshots, images, OS Login, VM Manager) the exam's "ensuring successful operation" domain expects you to know cold.
