資源階層簡介
對於 Professional Cloud Architect 而言,資源階層 (Resource Hierarchy) 是 Google Cloud 環境的「骨架」。它決定了資源如何分組、權限如何繼承以及治理如何實施。一個設計良好的階層對於隨著組織成長而管理複雜性至關重要。
Google Cloud 資源的邏輯結構,由組織節點 (Organization node)、資料夾 (Folders)、專案 (Projects) 和單個資源 (Resources) 組成。它實現了存取控制和配置的集中管理。參考資料:https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy
白話文解釋 Resource Hierarchy & Policies
資源階層就像是一家跨國公司的組織架構圖。
類比 1 — 企業組織架構圖(階層)
想像一家大公司。最頂端是 Organization (組織)(執行長 - CEO)。執行長之下是 Folders (資料夾)(部門,如「銷售部」、「研發部」和「財務部」)。每個部門內部是 Projects (專案)(具體倡議,如「2024 廣告活動」)。每個專案內部是 Resources (資源)(使用的工具,如特定的筆記型電腦或資料庫)。如果執行長制定了一條規則,它適用於所有人。如果銷售經理制定了一條規則,它僅適用於銷售部門。
類比 2 — 基因繼承 (IAM)
階層的工作方式就像 遺傳學 (Genetics)。如果「父項」(資料夾)具有某種特徵(IAM 角色),則「子項」(專案)會自動繼承該特徵。在 IAM 中,你無法「拒絕」父母的基因——如果你在資料夾層級是檢視者,那麼你就是其中所有內容的檢視者。
類比 3 — 建築法規 (Organization Policies)
Organization Policies (組織政策) 就像是 建築法規 或「護欄」。即使你擁有一棟房子(一個專案)並擁有所有鑰匙(IAM Owner 角色),你仍然不能在住宅區建造一棟 50 層高的摩天大樓。建築法規(組織政策)規定「建築物不得高於 2 層」。這可以確保整個社區(組織)的安全和合規,無論單個房主想做什麼。
階層的四個層級
1. 組織節點 (Organization Node)
- 階層的根節點。
- 連結到 Google Workspace 或 Cloud Identity 網域。
- 提供對所有資源的集中式可視化與控制。
2. 資料夾 (Folders)
- 用於對專案或其他資料夾進行分組。
- 可以代表部門(人力資源、工程)、環境(生產、開發)或區域。
- 架構師提示: 設計資料夾應符合你的 IAM 和政策需求,而不僅僅是你的公司內部結構。
3. 專案 (Projects)
- 啟用 API、管理帳單和添加服務的基礎層級。
- 每個資源(VM、儲存桶、SQL)必須僅屬於一個專案。
- 專案是網路和 IAM 的主要信任邊界。
4. 資源 (Resources)
- 你使用的實際服務:Compute Engine 執行個體、Cloud Storage 儲存桶等。
Organization Policy Service (護欄)
IAM 關注的是「誰」可以做某事,而 Organization Policies (組織政策) 關注的是「可以做什麼」。它們提供集中式、基於限制的控制。
- 限制 (Constraints): 你可以強制執行的預定義規則(例如
constraints/compute.disableSerialPortAccess)。 - 布林限制 (Boolean Constraints): 開啟或關閉功能(True/False)。
- 清單限制 (List Constraints): 定義允許清單或拒絕清單(例如「僅允許在
us-east1和us-central1中創建 VM」)。 - 繼承與覆蓋 (Inheritance and Overrides): 政策在階層中向下繼承。但是,你可以在較低層級「覆蓋」父項的政策,除非該政策被明確地「重置」或「封鎖」。
Organization Policy Service 在請求進入任何個別服務 API(Compute、GCS、BigQuery)之前,就由 Resource Manager API 先行強制執行。這代表即使是 roles/owner 主體,也無法繞過 constraints/compute.vmExternalIpAccess 的 deny — 請求會在 admission 階段以 FAILED_PRECONDITION 失敗,根本不會抵達 Compute Engine 的 control plane。
Boolean 與 List Constraints(以及 inheritFromParent)
了解 constraint 的型態會直接決定你如何在 Terraform 或 gcloud 中撰寫,以及繼承行為。
Boolean constraints
單純的 on/off 開關。Policy 的 spec 只有一個 enforce: true|false 欄位。
# constraints/iam.disableServiceAccountKeyCreation
name: organizations/123456789/policies/iam.disableServiceAccountKeyCreation
spec:
rules:
- enforce: true
常見的 boolean slug:iam.disableServiceAccountKeyCreation、iam.disableServiceAccountKeyUpload、compute.requireShieldedVm、compute.requireOsLogin、storage.uniformBucketLevelAccess、sql.restrictPublicIp。
List constraints
接受 allowedValues / deniedValues 的政策,可選擇搭配 allValues: ALLOW|DENY。範例:用 constraints/gcp.resourceLocations 強制資料落地 (data residency)。
name: organizations/123456789/policies/gcp.resourceLocations
spec:
rules:
- values:
allowedValues:
- in:eu-locations
- in:us-locations
常見的 list slug:gcp.resourceLocations、compute.vmExternalIpAccess、compute.trustedImageProjects、compute.restrictSharedVpcHostProjects、serviceuser.services、iam.allowedPolicyMemberDomains(網域受限分享)。
inheritFromParent 語意
僅對 list constraints 有效:子節點政策可以設定 inheritFromParent: true,與父節點的 allowlist 取聯集,而非整個取代。若不設此旗標,子節點政策會在該節點完全覆蓋父節點。對於 boolean constraints,子節點則是直接取代 — 沒有合併語意。這就是為什麼團隊常在 folder 層級對 gcp.resourceLocations 設 inheritFromParent: true,讓工程資料夾可以加入自己核可的區域,又不會丟掉組織層級的 EU/US 基線。
常見的架構師錯誤:把 constraints/compute.vmExternalIpAccess 當成 boolean 來用。它其實是 list constraint,要在 deniedValues 列出 VM 資源 ID(或用 allValues: DENY 全擋)。當成 boolean 寫會得到 INVALID_ARGUMENT,且 Terraform plan 中該 policy 會悄悄變成未強制。
常見的企業組織政策
對於生產環境,架構師通常會實施以下「黃金政策」:
- 限制資源使用: 僅允許在特定地理區域使用服務,以符合資料落地法規。
- 停用外部 IP 地址: 預設防止 VM 擁有公共 IP,以減少攻擊面。
- 強制執行一致的儲存桶層級存取: 確保 GCS 儲存桶對所有存取控制都使用 IAM,而不是舊有的 ACL。
- 限制共用 VPC 宿主專案: 控制哪些專案可用作網路中心 (Network Hubs)。
- 停用服務帳戶金鑰創建: 強制使用短期憑證和 Workload Identity。
Dry-Run 模式與 Custom Constraints
Dry-run policies
新 constraint 上線到 production 有風險 — 一個寫錯的 gcp.resourceLocations 可能會打掛在 us-central1 建立 bucket 的 CI/CD pipeline。Organization Policy Service 對大多數 constraint 都支援 dry-run 模式:政策會被評估,原本要被拒絕的請求會以 Policy Denied audit log(cloudaudit.googleapis.com/policy)記錄下來,但實際的 API call 仍會通過。
gcloud org-policies set-policy policy.yaml --update-mask=dryRunSpec
Dry-run spec 與 live spec 是分開的兩個欄位,可以並行運作。建議流程:先開 dry-run 跑 1-2 週、用 protoPayload.metadata.dryRun=true 撈 audit log、修掉違反的 workload、再把 dry-run spec 升級為 live。
Custom constraints
當預定義的 constraint 都無法表達你要的規則(例如「所有 GKE cluster 必須設定 release_channel = REGULAR」),就改用 Custom Organization Policies。你定義一段 CEL (Common Expression Language) 表達式,在資源 create/update 時對著 resource 的 JSON 表示式做評估。
name: organizations/123456789/customConstraints/custom.gkeReleaseChannel
resourceTypes:
- container.googleapis.com/Cluster
methodTypes:
- CREATE
- UPDATE
condition: "resource.releaseChannel.channel == 'REGULAR'"
actionType: ALLOW
displayName: "Require REGULAR release channel on GKE clusters"
Custom constraints 支援一份策展過的 resource type 清單(GCE、GKE、Cloud SQL、GCS、BigQuery、Cloud Run 等持續增加)。它們走的是與預定義 constraint 一樣的 admission path,所以也支援 dry-run。
新 custom constraint 一律先搭配 dry-run 上線,等 audit log 一個帳單週期都沒有誤判,再切到 live 版本。CEL 表達式若參考到 optional 欄位(resource.foo.bar)可能會丟 NoSuchField 並意外變成 deny-all — dry-run 能在你被半夜三點呼叫之前先攔下這種 bug。
IAM Deny Policies 與 IAM Conditions
Org Policy 是 resource 層級的護欄;IAM Deny Policies 與 IAM Conditions 是 identity 層級的護欄。PCA 情境題常常考你選對工具。
IAM Deny Policies (iam.googleapis.com/DenyPolicy)
Deny policy 掛在 Organization、Folder 或 Project 節點上,對指定主體明確拒絕權限 — 就算他們在別處有 roles/owner 也一樣。Deny rule 在 allow rule 之前被評估,所以這是唯一能真正跨整個組織封死某項權限的手段。
name: policies/cloudresourcemanager.googleapis.com%2Forganizations%2F123456789/denypolicies/block-key-creation
rules:
- denyRule:
deniedPrincipals:
- principalSet://goog/public:all
deniedPermissions:
- iam.googleapis.com/serviceAccountKeys.create
exceptionPrincipals:
- principal://iam.googleapis.com/projects/-/serviceAccounts/[email protected]
使用情境:不依賴 boolean Org Policy 就強制「禁止建立 service account key」、跨所有專案封掉已淘汰的權限、實作 break-glass 例外主體。
IAM Conditions
Conditions 是掛在 IAM allow binding 上的 CEL 表達式,用來限制角色「何時」適用 — 基於 request 時間、resource 名稱、resource tag 或 request 屬性。範例:只在名稱以 prod- 開頭的 bucket 上、且僅在 09:00-18:00 UTC 內,授予 roles/storage.admin。
- role: roles/storage.admin
members: ["group:[email protected]"]
condition:
title: "Only prod buckets in business hours"
expression: |
resource.name.startsWith("projects/_/buckets/prod-") &&
request.time.getHours("UTC") >= 9 &&
request.time.getHours("UTC") < 18
三個工具、三個層次:Org Policy 管 resource 形狀、Deny Policies 直接擋 permission、Conditions 收斂 allow 何時生效。
Resource Manager Tags 與 Labels
GCP 有兩個表面看起來很像的 key-value 機制掛在 resource 上。PCA 出題者最愛拿來考混淆。
Labels
Resource 上的自由格式 key-value({"env": "prod", "team": "payments"})。Labels 是描述性 metadata — 它們會流進帳單匯出、log filter 和 gcloud --filter 查詢,但完全沒有 IAM 或 policy 語意。任何對該 resource 有 edit 權限的人都能改 label。
Resource Manager Tags
Tags(tagKeys/tagValues,屬於 cloudresourcemanager.googleapis.com)是擁有自己 IAM 的一等治理物件。Tag 在 Org 或 Project 層級建立、繫結到資源、沿著階層向下繼承。關鍵差別在於 tag 可以被以下機制引用:
- IAM Conditions —
resource.matchTag('123456789/env', 'prod')讓你只在 prod-tagged 資源上授予某個角色。 - Org Policy Conditions — 讓 constraint 僅在 tag 符合時生效,例如只對
env=prod的 VM 強制compute.vmExternalIpAccessdeny。 - VPC Firewall rules — Secure tags(Resource Manager tag 的子集)取代舊有的 network tag,用於 firewall target。
- Hierarchical Firewall Policies — 在 folder/org 層級用 tag 做 target。
| 面向 | Labels | Resource Manager Tags |
|---|---|---|
| 範圍 | 單一資源 | 階層式、可繼承 |
| IAM 控制 | 無 | tagUser、tagAdmin 角色 |
| Policy 可掛勾 | 否 | 是(IAM、Org Policy、FW) |
| 帳單匯出 | 有 | 有 |
| 每個 resource 上限 | 64 | 50 |
PCA 情境題中要一眼認出的三大 slug 家族:iam.*(如 iam.disableServiceAccountKeyCreation、iam.allowedPolicyMemberDomains)管 identity surface;compute.*(如 compute.requireShieldedVm、compute.vmExternalIpAccess、compute.trustedImageProjects)管 VM 形狀;gcp.*(如 gcp.resourceLocations、gcp.restrictServiceUsage)管跨服務控制,例如資料落地與哪些 API 能夠啟用。
設計階層:最佳實踐
- 生產/非生產環境分離: 對生產 (Prod) 和開發 (Dev) 使用不同的資料夾,以便對生產環境應用更嚴格的政策和 IAM 控制。
- 共用服務資料夾: 為日誌記錄、監控和中心網路(共用 VPC)等集中式資源創建一個資料夾。
- 避免過深嵌套: 保持階層簡單。3-4 層資料夾通常就足夠了。過深的嵌套會使 IAM 和政策的故障排除變得非常困難。
- 自動化: 使用 Terraform 來管理你的階層。在企業環境中,切勿在控制台中手動創建資料夾和專案。
Org Policy 與 IAM:職責分離
PCA 反覆出現的情境:組織希望由資安團隊定義「什麼可以存在」,由應用團隊管理「誰可以做什麼」,兩邊都不能互相拆台。
乾淨的職責分離
- Org Policy Administrator (
roles/orgpolicy.policyAdmin):給 SecOps / 治理團隊。可以在 Org 與 Folder 層級設 constraint,但不能授予 IAM 角色。 - Project IAM Admin (
roles/resourcemanager.projectIamAdmin):應用團隊在自己的專案內持有。可以在該專案授予任何角色 — 但無法授予能繞過 Org Policy 的權限。
這套模型成立的關鍵是:Org Policy 在 Resource Manager admission layer 被評估,不是走 IAM permission check。App team admin 可以一直發 roles/compute.admin;只要 SecOps 設了 constraints/compute.disableNestedVirtualization,無論 IAM 怎麼配,沒有任何 compute admin 能建立 nested-virt VM。
模型會破功的兩種情況
roles/orgpolicy.policyAdmin被授予在專案層級 — 如果一個專案 admin 同時持有這個角色,他可以對該專案setOrgPolicy來在地覆蓋父 folder 的政策(只要該 constraint 不是從上層用denyAll: true強制)。最佳實踐:orgpolicy.policyAdmin只授予在 Org 節點。- 沒有
RESTORE_DEFAULT的 Boolean constraint — 如果某個 folder 把父層enforce: true改成enforce: false來停用父政策,必須要靠深度稽核才會抓到。可定期跑gcloud org-policies list --show-unset把這些 override 攤出來,或在有支援的 constraint 上採用inheritFromParent語意。
稽核與偵測
- Policy Analyzer(
policy-troubleshooter與新版 Policy Analyzer for Org Policies)讓你針對 live policy graph 問「這個請求為什麼被拒/被允許?」。 - Asset Inventory 會匯出每個節點上完整的
OrgPolicy集合 — 倒進 BigQuery 並對 diff 設告警。 - Cloud Audit Logs 以
protoPayload.serviceName=orgpolicy.googleapis.com過濾,可以撈出每一次SetOrgPolicy/SetCustomConstraint變更。
PCA 考試常用這種敘述:「SecOps 團隊必須保證任何開發者(即使握有 project owner)都不能建立 public bucket」。正確答案永遠是分層組合:constraints/storage.publicAccessPrevention(在 org 節點的 Org Policy)+ 對非 admin 主體設 storage.buckets.setIamPolicy 的 IAM Deny Policy。只選 IAM 是錯的,因為 owner 可以改 IAM;只選 Org Policy 也是錯的,因為它擋不住既有 bucket 上被惡意改寫的 legacy ACL。
常見問題 — 資源階層與組織政策
Q1. IAM 和組織政策 (Organization Policies) 有什麼區別?
IAM 側重於「誰」(身分)及其「權限」。組織政策側重於「什麼」(限制),並應用於資源本身,無論是誰在執行操作。
Q2. 一個專案可以屬於兩個資料夾嗎?
不可以。每個專案都有且僅有一個父項(資料夾或組織節點)。
Q3. 如果組織政策與 IAM 權限發生衝突會怎樣?
組織政策優先。 如果使用者擁有 IAM 角色「Compute Admin」(允許創建 VM),但設有「停用在歐洲創建 VM」的組織政策,則該使用者將無法在歐洲創建 VM。
Q4. 我可以在強制執行之前測試組織政策嗎?
可以,使用 測試模式 (Dry-run mode)(針對某些限制)。這允許你查看哪些操作會被封鎖,而不會實際中斷服務。
Q5. 我如何獲得組織節點 (Organization Node)?
你必須擁有 Google Workspace 或 Cloud Identity 帳戶。當你該網域下創建第一個專案時,組織節點會自動生成。
最後的架構師提示
在 PCA 考試中,資源階層的核心在於 大規模治理 (Governance at Scale)。如果情境涉及「在 500 個專案中強制執行合規性」,答案是 Organization Policy。如果涉及「用於帳單和存取的邏輯分組」,答案是 Folders。請務必記住,政策繼承 (Policy inheritance) 是企業範圍內安全性的關鍵。如果你想「阻止所有開發人員創建公共儲存桶」,你可以在 組織 或 資料夾 層級應用該政策。