Identity & Access Management (IAM)Identity & Access Management (IAM)
Thuộc bộ tài liệu Cloud knowledge base — GCP deep dive. Bổ sung cho DevOps Roadmap.
Tổng quan
Cloud IAM trả lời một câu hỏi cho mọi lời gọi API: ai được làm gì trên tài
nguyên nào? Trong GCP nó tách thành ba phần — một member (ai), một role
gom các permission (làm gì), và một resource mà binding gắn vào (trên cái
nào). Một binding (member, role) trên một resource là một allow policy
(trước đây gọi là “IAM policy”).
Đặc điểm định hình của GCP IAM là thừa kế theo phân cấp: một policy đặt ở Organization hay Folder chảy xuống mọi project và resource bên dưới, và policy hiệu lực tại một node bất kỳ là hợp (union) của binding của chính nó cộng tất cả binding thừa kế. Không có khái niệm “mặc định deny theo thừa kế” — các grant cộng dồn xuống dưới. Điều này khiến resource hierarchy (ở trang Overview) trở thành xương sống của toàn bộ mô hình truy cập, nên hãy làm đúng ngay từ đầu.
Kiến thức nền tảng
Member (principal)
Member là bất kỳ identity nào có thể được cấp role. Tiền tố trong chuỗi member cho IAM biết đó là loại gì:
| Loại member | Dạng chuỗi | Dùng cho |
|---|---|---|
| Google account | user:alice@example.com | Một người có identity Google/Workspace |
| Google group | group:devs@example.com | Cấp cho một team; quản lý thành viên trong group |
| Service account | serviceAccount:svc@PROJECT.iam.gserviceaccount.com | Workload phi con người (app, VM, pipeline) |
| Workspace / Cloud Identity domain | domain:example.com | Mọi người trong org |
| Workload Identity Federation | principal://… / principalSet://… | Identity ngoài (GitHub, AWS, OIDC) không cần key |
| Mọi identity đã xác thực | allAuthenticatedUsers | Bất kỳ ai đã xác thực bằng Google (hiếm khi hợp lý) |
| Tất cả, không xác thực | allUsers | Hoàn toàn public (ví dụ bucket/website công khai) |
Hãy ưu tiên cấp role cho group, không phải cá nhân — bạn quản lý truy cập bằng cách đổi thành viên group trong identity provider, vừa audit được vừa tồn tại qua việc người vào/ra.
Role
Role là một tập permission có tên. Bản thân permission (ví dụ
compute.instances.start) không bao giờ được cấp trực tiếp — luôn qua một role.
Có ba cấp:
| Loại role | Là gì | Ví dụ | Khi nào dùng |
|---|---|---|---|
| Basic (primitive) | Role cũ, rất rộng: roles/owner, roles/editor, roles/viewer | roles/editor cho quyền ghi lên gần như mọi thứ | Tránh trong production — quá thô |
| Predefined | Role do Google biên soạn, giới hạn theo dịch vụ, luôn cập nhật | roles/compute.instanceAdmin.v1, roles/storage.objectViewer | Lựa chọn mặc định — least-privilege mà không phải bảo trì |
| Custom | Role bạn tự định nghĩa từ danh sách permission chọn tay | roles/customDeployBot | Khi không predefined role nào vừa và cần phạm vi chặt hơn |
Nguyên tắc: bắt đầu từ predefined role gần nhất. Chỉ dùng custom role khi predefined quá rộng, và để dành basic role cho project sandbox nhỏ (hoặc không bao giờ).
Allow policy & binding
Một allow policy gắn vào resource là danh sách các binding. Mỗi binding gắn một role với một tập member, tùy chọn kèm condition:
{
"bindings": [
{
"role": "roles/storage.objectViewer",
"members": [
"group:analysts@example.com",
"serviceAccount:etl@payments-prod.iam.gserviceaccount.com"
]
},
{
"role": "roles/compute.instanceAdmin.v1",
"members": ["group:sre@example.com"],
"condition": {
"title": "prod-only",
"expression": "resource.name.startsWith('projects/payments-prod')"
}
}
],
"etag": "BwY...",
"version": 3
}
etag cung cấp optimistic concurrency (đọc-sửa-ghi an toàn). version: 3 là bắt
buộc mỗi khi có condition.
Thừa kế xuống phân cấp
Policy hiệu lực trên một resource là hợp của binding đặt trực tiếp trên nó cộng mọi binding thừa kế từ project, folder và organization:
- Orggroup:security-admins → roles/iam.securityReviewerđọc IAM toàn org
- Folder "Engineering"group:eng-leads → roles/resourcemanager.folderViewer
- Project "payments-prod"group:payments-dev → roles/run.developer
- Resource (một bucket)serviceAccount:etl@… → roles/storage.objectAdmin
Một thành viên của security-admins nhận role đó trên mọi thứ; payments-dev
chỉ nhận run.developer trên đúng project đó. Vì grant chỉ luôn cộng thêm
quyền xuống dưới, IAM cố tình permissive theo thiết kế — các guardrail thật sự có
thể cấm điều gì đó là Organization Policy và IAM Conditions / deny
policy, không phải allow binding thông thường.
Khái niệm chính
Service account
Service account (SA) là identity đặc biệt cho workload phi con người. Nó vừa là identity (được cấp role) vừa là resource (người/SA khác có thể được cấp role trên nó, như quyền impersonate nó). Ba loại:
- User-managed SA — bạn tạo cho từng workload (khuyến nghị).
- Default SA — tự tạo (ví dụ Compute Engine default SA). Mặc định thừa quyền
(
Editor); hãy vô hiệu hóa hoặc thay thế. - Google-managed service agent — Google dùng để thay mặt bạn thao tác trên resource; thường bạn không đụng tới.
Key vs keyless — đây là quyết định IAM quan trọng nhất.
| Cách | Cơ chế | Rủi ro | Kết luận |
|---|---|---|---|
| SA key (JSON) | Private key sống lâu, tải về được | Rò rỉ trong Git, laptop, log CI; không hết hạn | Tránh. Vector rò rỉ credential số 1 của GCP |
| Attached SA | VM/Cloud Run/GKE chạy dưới danh nghĩa một SA; metadata server phát token ngắn hạn | Token tự xoay, không nằm trên đĩa | Ưu tiên cho workload trong cloud |
| Impersonation | Một principal có roles/iam.serviceAccountTokenCreator phát token ngắn hạn cho một SA | Không key; có audit; giới hạn thời gian | Ưu tiên cho người/CI thao tác dưới danh nghĩa một SA |
| Workload Identity Federation (WIF) | IdP ngoài (GitHub, AWS, OIDC/SAML) đổi token của chính nó lấy token GCP ngắn hạn | Không key nào rời khỏi hệ thống ngoài | Ưu tiên cho GKE và CI/CD bên ngoài |
Ví dụ impersonation (chạy lệnh dưới danh nghĩa một deploy SA, không key file):
gcloud storage ls gs://payments-artifacts \
--impersonate-service-account=deployer@payments-prod.iam.gserviceaccount.com
Workload Identity cho GKE ánh xạ một Kubernetes service account (KSA) sang một Google service account (GSA) để pod nhận token mà không mount key:
gcloud iam service-accounts add-iam-policy-binding \
my-gsa@payments-prod.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:payments-prod.svc.id.goog[prod-ns/my-ksa]"
# rồi annotate KSA: iam.gke.io/gcp-service-account=my-gsa@payments-prod.iam.gserviceaccount.com
WIF cho GitHub Actions — cho một workflow GitHub xác thực mà không lưu secret nào, giới hạn vào đúng một repo:
# 1. Tạo workload identity pool + OIDC provider cho GitHub
gcloud iam workload-identity-pools create github-pool \
--location=global --display-name="GitHub Actions"
gcloud iam workload-identity-pools providers create-oidc github-provider \
--location=global --workload-identity-pool=github-pool \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
--attribute-condition="assertion.repository=='my-org/my-repo'"
# 2. Cho identity từ repo đó impersonate deploy SA
gcloud iam service-accounts add-iam-policy-binding \
deployer@payments-prod.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="principalSet://iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/github-pool/attribute.repository/my-org/my-repo"
attribute-condition là cực kỳ quan trọng — không ghim repository (hoặc
branch/ref) thì bất kỳ repo GitHub nào cũng có thể đổi token lấy SA của bạn.
Ví dụ: cấp role bằng gcloud
# Cấp cho một group một predefined role trên một project
gcloud projects add-iam-policy-binding payments-prod \
--member="group:payments-dev@example.com" \
--role="roles/run.developer"
# Cấp trên một resource đơn lẻ (bucket) thay vì cả project
gcloud storage buckets add-iam-policy-binding gs://payments-artifacts \
--member="serviceAccount:etl@payments-prod.iam.gserviceaccount.com" \
--role="roles/storage.objectViewer"
# Kiểm tra ai đang có quyền
gcloud projects get-iam-policy payments-prod --format=json
IAM Conditions
Condition thêm quy tắc dựa trên thuộc tính lên trên một binding bằng CEL (Common Expression Language). Dùng phổ biến:
- Giới hạn thời gian:
request.time < timestamp('2026-12-31T00:00:00Z') - Giới hạn theo tên resource:
resource.name.startsWith('projects/_/buckets/logs-') - Giới hạn theo loại resource:
resource.type == 'storage.googleapis.com/Bucket'
Condition biến một role rộng thành một grant hẹp, hiểu ngữ cảnh mà không cần bịa ra custom role.
IAM deny policy
Allow policy chỉ cộng thêm quyền. Deny policy gắn các quy tắc từ chối được
đánh giá trước allow và ghi đè chúng — cách để nói “không ai ngoài X được phép
làm Y”, kể cả khi có binding thừa kế cấp quyền đó. Dùng cho guardrail cứng (ví dụ
deny iam.serviceAccountKeys.create toàn org).
Organization Policy vs IAM
Phân biệt rõ hai cái:
| IAM (allow/deny policy) | Organization Policy Service | |
|---|---|---|
| Câu hỏi | Ai được làm gì? | Cái gì được phép tồn tại / thế nào? |
| Độ chi tiết | Member + role + resource | Constraint trên cấu hình resource |
| Ví dụ | ”Group SRE được admin VM" | "Không VM nào được có external IP” |
| Ghi đè Owner? | Deny policy thì được | Có — cả Owner cũng bị ràng |
Org policy là guardrail tuân thủ (xem Overview); IAM là mô hình truy cập hằng ngày.
IAM Recommender & least privilege
IAM Recommender (thuộc Policy Intelligence) phân tích 90 ngày sử dụng thực tế và đề xuất gỡ permission không dùng hoặc đổi một role rộng lấy một role chặt hơn. Policy Analyzer trả lời “ai truy cập được resource này?” và “member này truy cập được gì?”. Kết hợp lại, chúng giúp bạn tiến tới least privilege bằng bằng chứng thay vì phỏng đoán.
Best Practices
- Cấp cho group, không cho cá nhân. Quản lý truy cập bằng thành viên group trong IdP — audit được, và tồn tại qua người vào/ra mà không phải sửa policy.
- Ưu tiên predefined role hơn basic role.
Owner/Editor/Viewerquá rộng; predefined role được biên soạn theo hướng least-privilege. - Không bao giờ tải service account key. Dùng attached SA, impersonation hoặc
Workload Identity Federation. Cưỡng chế bằng
iam.disableServiceAccountKeyCreation. Key rò rỉ là vector breach hàng đầu của GCP. - Một SA chuyên biệt cho mỗi workload. Đừng dùng chung SA giữa các app; một vụ xâm nhập nên bị giới hạn trong đúng phạm vi hẹp của workload đó.
- Vô hiệu hóa hoặc hạ quyền default service account. Compute Engine default SA
nhận
Editor— thay bằng một user-managed SA least-privilege. - Dùng Workload Identity Federation cho CI/CD và cloud bên ngoài. GitHub Actions, GitLab, và workload AWS nên đổi identity gốc lấy token GCP ngắn hạn — không lưu secret ở đâu cả.
- Luôn ghim WIF attribute condition vào đúng repo/branch/subject. Một pool provider không ghim thực chất là một cánh cửa công khai vào project của bạn.
- Cấp ở node hiệu lực thấp nhất. Bind trên project hoặc resource, không phải org/folder, trừ khi truy cập thực sự cần rộng như vậy — thừa kế khiến grant cao trở nên rất rộng.
- Dùng IAM Conditions cho truy cập giới hạn thời gian và phạm vi. Nâng quyền trong một khoảng, hoặc giới hạn role theo tiền tố tên resource, thay vì một grant rộng vĩnh viễn.
- Dùng deny policy cho các quy tắc “không bao giờ” cứng. Mã hóa các điều không thương lượng (không tạo key, không xóa audit sink) để không allow thừa kế nào ghi đè được.
- Chạy IAM Recommender định kỳ và gỡ grant không dùng. Tiến tới least privilege bằng bằng chứng sử dụng; grant rộng đã cũ là rủi ro âm thầm.
- Tách bạch nhiệm vụ giữa các môi trường. Group/SA khác nhau cho dev/staging/prod; không identity con người đơn lẻ nào nên giữ quyền admin prod theo mặc định.
- Để dành
Ownerchỉ cho break-glass. Loại nó khỏi quy trình hằng ngày; log và alert mỗi khi nó được dùng. - Bật Cloud Audit Logs (Admin Activity + Data Access) và giám sát thay đổi
IAM. Alert khi có tạo key mới, cấp role ở cấp org/folder, và lời gọi
setIamPolicy. - Quản lý IAM bằng code (Terraform). Ưu tiên resource kiểu authoritative
nhưng hẹp như
google_project_iam_memberhơn_policyđể tránh vô tình xóa binding bạn không quản lý; review mọi thay đổi trong PR.
Tài liệu tham khảo
- IAM overview
- IAM roles and permissions
- Understanding allow policies
- Policy inheritance & resource hierarchy
- Service accounts overview
- Best practices for using service accounts
- Workload Identity Federation
- WIF with deployment pipelines (GitHub Actions)
- GKE Workload Identity
- IAM Conditions overview
- IAM deny policies
- IAM Recommender / Policy Intelligence
- Organization Policy Service
- google-github-actions/auth
Part of the Cloud knowledge base — GCP deep dive. Complements DevOps Roadmap.
Overview
Cloud IAM answers one question for every API call: who can do what on which
resource? In GCP that decomposes into three parts — a member (the who), a
role that bundles permissions (the what), and a resource the binding is
attached to (the which). A binding of (member, role) on a resource is an
allow policy (formerly “IAM policy”).
The defining feature of GCP IAM is hierarchical inheritance: a policy set on the Organization or a Folder flows down to every project and resource beneath it, and the effective policy at any node is the union of its own bindings and all inherited ones. There is no “deny by inheritance by default” — grants accumulate downward. This makes the resource hierarchy (from the Overview page) the backbone of your entire access model, so get it right early.
Fundamentals
Members (principals)
A member is any identity that can be granted a role. The prefix in the member string tells IAM what kind it is:
| Member type | String form | Use for |
|---|---|---|
| Google account | user:alice@example.com | A human with a Google/Workspace identity |
| Google group | group:devs@example.com | Grant to a team; manage membership in the group |
| Service account | serviceAccount:svc@PROJECT.iam.gserviceaccount.com | Non-human workloads (apps, VMs, pipelines) |
| Workspace / Cloud Identity domain | domain:example.com | Everyone in your org |
| Workload Identity Federation | principal://… / principalSet://… | External identities (GitHub, AWS, OIDC) with no key |
| All authenticated users | allAuthenticatedUsers | Any Google-authenticated identity (rarely appropriate) |
| Everyone, no auth | allUsers | Fully public (e.g. a public bucket/website) |
Prefer granting roles to groups, not individuals — you then manage access by changing group membership in your identity provider, which is auditable and survives people joining/leaving.
Roles
A role is a named collection of permissions. Permissions themselves
(e.g. compute.instances.start) are never granted directly — always via a role.
There are three tiers:
| Role type | What it is | Example | When to use |
|---|---|---|---|
| Basic (primitive) | Legacy, very broad roles: roles/owner, roles/editor, roles/viewer | roles/editor grants write on almost everything | Avoid in production — far too coarse |
| Predefined | Google-curated, service-scoped roles kept up to date | roles/compute.instanceAdmin.v1, roles/storage.objectViewer | The default choice — least-privilege without custom maintenance |
| Custom | Roles you define from a hand-picked permission list | roles/customDeployBot | When no predefined role fits and you need tighter scope |
Rule of thumb: start from the closest predefined role. Reach for a custom role only when a predefined one is too broad, and reserve basic roles for tiny sandbox projects (or never).
Allow policies & bindings
An allow policy attached to a resource is a list of bindings. Each binding ties one role to a set of members, optionally with a condition:
{
"bindings": [
{
"role": "roles/storage.objectViewer",
"members": [
"group:analysts@example.com",
"serviceAccount:etl@payments-prod.iam.gserviceaccount.com"
]
},
{
"role": "roles/compute.instanceAdmin.v1",
"members": ["group:sre@example.com"],
"condition": {
"title": "prod-only",
"expression": "resource.name.startsWith('projects/payments-prod')"
}
}
],
"etag": "BwY...",
"version": 3
}
The etag provides optimistic concurrency (read-modify-write safely). version: 3 is required whenever conditions are present.
Inheritance down the hierarchy
The effective policy on a resource is the union of bindings set directly on it plus every binding inherited from its project, folders, and organization:
- Orggroup:security-admins → roles/iam.securityReviewerorg-wide read of IAM
- Folder "Engineering"group:eng-leads → roles/resourcemanager.folderViewer
- Project "payments-prod"group:payments-dev → roles/run.developer
- Resource (a bucket)serviceAccount:etl@… → roles/storage.objectAdmin
A member of security-admins gets its role on everything; payments-dev gets
run.developer on that project only. Because grants only ever add
permissions downward, IAM is deliberately permissive by design — the guardrails
that can actually forbid things are Organization Policies and IAM
Conditions / deny policies, not plain allow bindings.
Key Concepts
Service accounts
A service account (SA) is a special identity for non-human workloads. It is both an identity (it can be granted roles) and a resource (people/SAs can be granted roles on it, like the right to impersonate it). Three types:
- User-managed SAs — you create them per workload (recommended).
- Default SAs — auto-created (e.g. the Compute Engine default SA). These are
over-privileged (
Editor) by default; disable or replace them. - Google-managed service agents — used by Google services to act on your resources; you generally don’t touch these.
Keys vs keyless — this is the single most important IAM decision.
| Approach | Mechanism | Risk | Verdict |
|---|---|---|---|
| SA keys (JSON) | Downloadable long-lived private key | Leaks in Git, laptops, CI logs; never expires | Avoid. The #1 GCP credential-leak vector |
| Attached SA | VM/Cloud Run/GKE runs as an SA; metadata server mints short-lived tokens | Tokens auto-rotate, never on disk | Preferred for in-cloud workloads |
| Impersonation | A principal with roles/iam.serviceAccountTokenCreator mints a short-lived token for an SA | No keys; audited; time-boxed | Preferred for humans/CI acting as an SA |
| Workload Identity Federation (WIF) | External IdP (GitHub, AWS, OIDC/SAML) exchanges its own token for a short-lived GCP token | No keys leave the external system | Preferred for GKE and external CI/CD |
Impersonation example (run a command as a deploy SA, no key file):
gcloud storage ls gs://payments-artifacts \
--impersonate-service-account=deployer@payments-prod.iam.gserviceaccount.com
Workload Identity for GKE maps a Kubernetes service account (KSA) to a Google service account (GSA) so pods get tokens with no mounted key:
gcloud iam service-accounts add-iam-policy-binding \
my-gsa@payments-prod.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:payments-prod.svc.id.goog[prod-ns/my-ksa]"
# then annotate the KSA: iam.gke.io/gcp-service-account=my-gsa@payments-prod.iam.gserviceaccount.com
WIF for GitHub Actions — let a GitHub workflow authenticate with no stored secret, restricted to a specific repo:
# 1. Create a workload identity pool + OIDC provider for GitHub
gcloud iam workload-identity-pools create github-pool \
--location=global --display-name="GitHub Actions"
gcloud iam workload-identity-pools providers create-oidc github-provider \
--location=global --workload-identity-pool=github-pool \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
--attribute-condition="assertion.repository=='my-org/my-repo'"
# 2. Let identities from that repo impersonate the deploy SA
gcloud iam service-accounts add-iam-policy-binding \
deployer@payments-prod.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="principalSet://iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/github-pool/attribute.repository/my-org/my-repo"
The attribute-condition is critical — without pinning repository (or the
branch/ref), any GitHub repo could exchange a token for your SA.
Example: granting a role with gcloud
# Grant a group a predefined role on a project
gcloud projects add-iam-policy-binding payments-prod \
--member="group:payments-dev@example.com" \
--role="roles/run.developer"
# Grant on a single resource (bucket) instead of the whole project
gcloud storage buckets add-iam-policy-binding gs://payments-artifacts \
--member="serviceAccount:etl@payments-prod.iam.gserviceaccount.com" \
--role="roles/storage.objectViewer"
# Inspect who has access
gcloud projects get-iam-policy payments-prod --format=json
IAM Conditions
Conditions add attribute-based rules on top of a binding using CEL (Common Expression Language). Common uses:
- Time-bound access:
request.time < timestamp('2026-12-31T00:00:00Z') - Resource-name scoping:
resource.name.startsWith('projects/_/buckets/logs-') - Restrict to a resource type:
resource.type == 'storage.googleapis.com/Bucket'
Conditions turn a broad role into a narrow, context-aware grant without inventing a custom role.
IAM deny policies
Allow policies only add access. Deny policies attach denial rules that are
evaluated before allows and override them — the way to say “no one except X may
ever do Y”, even if some inherited binding grants it. Use them for hard
guardrails (e.g. deny iam.serviceAccountKeys.create org-wide).
Organization Policies vs IAM
Keep the two straight:
| IAM (allow/deny policies) | Organization Policy Service | |
|---|---|---|
| Question | Who can do what? | What is allowed to exist / how? |
| Granularity | Member + role + resource | Constraints on resource config |
| Example | ”SRE group can admin VMs" | "No VM may have an external IP” |
| Overrides Owners? | Deny policies can | Yes — even Owners are bound |
Org policies are the compliance guardrails (see Overview); IAM is the day-to-day access model.
IAM Recommender & least privilege
The IAM Recommender (part of Policy Intelligence) analyzes 90 days of actual usage and recommends removing unused permissions or swapping a broad role for a tighter one. Policy Analyzer answers “who can access this resource?” and “what can this member access?”. Together they let you drive toward least privilege with evidence rather than guesswork.
Best Practices
- Grant to groups, not individuals. Manage access by group membership in your IdP — auditable, and it survives joiners/leavers without policy edits.
- Prefer predefined roles over basic roles.
Owner/Editor/Viewerare far too broad; predefined roles are curated and least-privilege by design. - Never download service account keys. Use attached SAs, impersonation, or
Workload Identity Federation. Enforce with
iam.disableServiceAccountKeyCreation. Leaked keys are the top GCP breach vector. - One dedicated SA per workload. Don’t share SAs across apps; a compromise should be contained to that one workload’s narrow permissions.
- Disable or de-privilege default service accounts. The Compute Engine
default SA gets
Editor— replace it with a least-privilege user-managed SA. - Use Workload Identity Federation for CI/CD and external clouds. GitHub Actions, GitLab, and AWS workloads should exchange their native identity for short-lived GCP tokens — no secrets stored anywhere.
- Always pin WIF attribute conditions to a specific repo/branch/subject. An unpinned pool provider is effectively a public door into your project.
- Grant at the lowest effective node. Bind on the project or resource, not the org/folder, unless the access genuinely needs to be that broad — inheritance makes high grants very wide.
- Use IAM Conditions for time-boxed and scoped access. Elevate access for a window, or scope a role to a resource-name prefix, instead of a permanent broad grant.
- Use deny policies for hard “never” rules. Codify non-negotiables (no key creation, no deletion of audit sinks) so no inherited allow can override them.
- Run the IAM Recommender regularly and remove unused grants. Drive to least privilege with usage evidence; stale broad grants are silent risk.
- Separate duties across environments. Different groups/SAs for dev/staging/prod; no single human identity should hold prod admin by default.
- Reserve
Ownerfor break-glass only. Keep it out of daily workflows; log and alert whenever it is used. - Enable Cloud Audit Logs (Admin Activity + Data Access) and monitor IAM
changes. Alert on new key creation, role grants at org/folder level, and
setIamPolicycalls. - Manage IAM as code (Terraform). Prefer authoritative-but-scoped
resources like
google_project_iam_memberover_policyto avoid accidentally wiping bindings you don’t manage; review every change in PRs.
References
- IAM overview
- IAM roles and permissions
- Understanding allow policies
- Policy inheritance & resource hierarchy
- Service accounts overview
- Best practices for using service accounts
- Workload Identity Federation
- WIF with deployment pipelines (GitHub Actions)
- GKE Workload Identity
- IAM Conditions overview
- IAM deny policies
- IAM Recommender / Policy Intelligence
- Organization Policy Service
- google-github-actions/auth