跳至主要內容
跳到課程內容
課程目錄
基礎架構管理與 AI/ML 基礎設施即程式碼與自動化
0%
11 / 25 中級 85 分鐘 00:00

考試領域 Managing implementation

基礎設施即程式碼與自動化

用 Terraform、Infrastructure Manager、Cloud Build 與 Config Connector 建立可審核、可回溯的基礎設施變更流程

2026年3月13日 更新:2026年10月28日
本課目錄26 個段落

這課怎麼做?

先讀變更與 state 的觀念,再做離線 plan 審核,依四回合證據寫出審核結論。最後用原有的 landing zone 情境,把判讀方法帶回團隊設計。

觀念閱讀約 50 分鐘;離線練習約 35 分鐘,可分次進行。離線部分只需瀏覽器與自己的筆記/文字編輯器,不需安裝 Terraform、登入雲端或啟用 Billing。這課提供的是合成 plan 片段,不是部署 Lab;判讀成果是逐項變更表與審核備忘錄。

IaC 管的是變更,不只是建立資源

手動在 Console 建一個 VPC 並不難,困難的是半年後回答:當時為什麼這樣設?誰改過?測試環境和正式環境差在哪裡?如果重建,會不會少一條路由?

Infrastructure as Code(IaC)把期望設定寫成可版本控制的檔案,讓變更可以 review、preview、執行和追蹤。它能提高一致性,但不會自動保證每次結果完全相同:provider version、API default、外部資料、quota 和人工變更都可能造成差異。因此 IaC 還要搭配版本鎖定、state、測試和 drift 管理。

IaC 受控變更系統:版本化設定、受保護且版本化的 State 與實際雲端資源三方持續比對;變更依序通過格式檢查、plan、人工 review、policy gate 與 apply,drift 與執行結果再回到下一輪

圖解:版本化設定、受保護的 state 與實際資源是三份必須持續比對的真相;變更還要經過檢查、plan、人工 review、policy gate 與 apply,並把 drift 和執行結果帶回下一輪。

Terraform 的三份真相

排查 Terraform 問題時,分開看三件事:

  1. Configuration:程式碼希望環境長什麼樣子。
  2. State:Terraform 記得哪些 resource instance,以及它們和遠端資源的對應。
  3. Remote system:Google Cloud 現在真正存在的資源。

terraform plan 會比較這三者並提出動作。它不是永久有效的承諾。未指定 saved plan 的 terraform apply 會重新規劃;指定 saved plan 則執行該檔案記錄的動作,不會自動納入後來修改的 configuration。輸入或環境變動時,應依流程重新產生並審核 plan;即使使用 saved plan,遠端變動與執行期錯誤仍可能讓操作失敗,不能把它當成服務一定健康的保證。兩種模式見 terraform apply。

查看流程與節點文字

箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。

flowchart TB
  C["Configuration<br/>期望狀態"] --> P["terraform plan"]
  S["State<br/>Resource identity<br/>與已知屬性"] --> P
  R["Remote system<br/>Google Cloud<br/>實際狀態"] --> P
  P --> D["Proposed create/<br/>update/<br/>replace/delete"]
  D --> V{"Reviewer 驗證<br/>blast radius<br/>且 inputs 未變?"}
  V -->|"否"| C
  V -->|"是"| A["terraform apply"]
  A --> R
  A --> S
  M["Console/<br/>API 手動變更"] --> R
  R -->|"下一次<br/>refresh/plan"| P
Plan 是三份狀態在某一時刻的比較,不是永久承諾;configuration、state、remote system 或輸入改變後都要重新產生並審核。

Provider 與版本

google provider 提供穩定版資源與欄位;google-beta 可在功能正式進入 stable provider 前提供支援。Beta 不等於永遠不能用於 production,stable 也不代表沒有升級風險。應根據功能成熟度、支援狀態和變更風險決定,並鎖定 provider constraints、提交 dependency lock file、在升級前跑 plan 和測試。

State 要像敏感營運資料一樣保護

團隊不應把 production state 留在個人筆電。使用 Cloud Storage remote backend 時,至少考慮:

  • 開啟 uniform bucket-level access 與 public access prevention。
  • 用 IAM 限制能讀寫 state 的 principal,因為 state 可能包含敏感值。
  • 開啟 Object Versioning,並用 lifecycle policy 控制舊版本成本。
  • 視要求使用 CMEK、audit logs 與分離管理專案。
  • 避免 force_destroy,並為 bucket 刪除設保護流程。

GCS backend 支援 state locking,能降低同時 apply 的衝突,但 pipeline 仍應避免多個 job 操作同一份 state。一般也不建議直接替 state bucket 加不可逆的 retention lock;Terraform 需要更新和刪除 state object,鎖錯可能讓正常操作卡住。

可參考 Google 的 Store Terraform state in Cloud Storage 範例,再依組織要求縮小權限。

Module 是介面,不是複製貼上

Module 可以把常見的 VPC、project 或服務設定封裝成團隊介面。好 module 通常具備:

  • 清楚而少量的 inputs,不把 provider 每個欄位都原樣暴露。
  • 安全預設值,危險選項需要明確開啟。
  • Outputs 能支援後續組合,不洩漏不必要的敏感資料。
  • Semantic version、upgrade notes 和自動化測試。
  • Ownership,知道誰負責 review provider 變更與修補。

Cloud Foundation Toolkit(CFT)提供 Google 維護的 Terraform modules 和 blueprints,可作為 landing zone 與常見架構的參考或起點。採用前仍要 review 版本、輸入、組織政策與營運模型;官方 module 不是「不用理解就直接套用」。

三種常見執行方式

自管 Terraform pipeline

把 Terraform 放在 Cloud Build、GitHub Actions 或其他 CI 平台,適合已有成熟 pipeline、需要自訂測試和多雲整合的團隊。相對地,團隊要自行管理 runner、credentials、state、concurrency 和 approval flow。

Infrastructure Manager

Infrastructure Manager(Infra Manager)是 Google Cloud 的受管理 IaC 服務,以 Terraform configuration 部署和管理 Google Cloud infrastructure。它提供 deployment、revision、preview,並替每個 deployment 管理相關 state 和執行環境。

它適合希望使用 Terraform、但不想自己組整套 apply runner 和 state workflow 的團隊。Infra Manager 管的是基礎設施生命週期;應用版本的漸進式交付仍要用 Cloud Build、Cloud Deploy 或其他交付工具。詳情可看 Infrastructure Manager overview。

Config Connector

Config Connector 透過 Kubernetes CRDs 和 controllers 管理支援的 Google Cloud resources。團隊提交 Kubernetes object,controller 持續把實際資源 reconcile 到 spec 所描述的 desired state,並在 status 和 events 回報結果。

它適合已經以 Kubernetes API、RBAC 和 GitOps 工作的團隊,但要接受:

  • Reconciliation 是 eventual consistency,不是 apply 完立刻全部就緒。
  • 支援資源和欄位要查 Config Connector reference。
  • Controller 需要雲端權限,刪除 policy 與 resource ownership 要先定義。
  • Kubernetes etcd 不是一句話就能取代所有 state 思考;desired state、observed status 和外部 resource lifecycle 仍要管理。

Terraform 和 Config Connector 都能管理 Google Cloud 資源。選擇重點是團隊工作流、控制面、資源支援與 ownership,避免同一個資源同時被兩套 controller 管理。

查看流程與節點文字

箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。

flowchart LR
  Q["先決定團隊要使用的<br/>Source of truth 與 control plane"] --> W{"現有 CI 是否成熟,且需要<br/>自訂測試/多雲整合?"}
  W -->|"是"| T["Self-managed Terraform pipeline<br/>自行管理 runner、state、identity、approval"]
  W -->|"否"| G{"要用 Terraform 管 Google Cloud,<br/>但不想自建 apply runtime?"}
  G -->|"是"| I["Infrastructure Manager<br/>Deployment、revision、preview、managed state"]
  G -->|"否"| K{"團隊已用 Kubernetes API、<br/>RBAC 與 GitOps?"}
  K -->|"是"| C["Config Connector<br/>CRDs+continuous reconciliation"]
  K -->|"否"| E["補齊需求與營運能力後再選"]
  T --> O["每個 resource 指定唯一 owner/controller"]
  I --> O
  C --> O
差別在 source of truth、執行環境與 reconciliation 模型;無論選哪一種,同一資源都不應同時交給多個 controller 管理。

一條安全的 Terraform pipeline

Production pipeline 可以拆成這幾步:

  1. PR 執行 fmt、validate、lint、module tests 與 policy checks。
  2. 使用固定版本的 Terraform 和 providers 產生 plan。
  3. 保存 machine-readable plan artifact、摘要與執行身分,供 reviewer 檢查 create/update/delete/replace。
  4. 高風險環境需要獨立核准;核准者不應只看「plan succeeded」。
  5. Apply 使用專用 service account 和最小權限,優先透過短效憑證/service account impersonation,不在 repository 存長效 key。
  6. Apply 後執行 smoke test、policy verification 與監控檢查。
  7. 失敗時保存 log 和 state,不要讓多個人同時手動重跑。

不要在每個 merge 後無條件自動 apply production。是否自動化到哪一步,要看變更風險、blast radius、法規與團隊的復原能力。

查看流程與節點文字

箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。

sequenceDiagram
  participant D as Developer
  participant C as CI
  participant R as Reviewer/Approver
  participant A as Apply Service Account
  participant G as Google Cloud
  participant O as Audit/Monitoring
  D->>C: Open PR with versioned configuration
  C->>C: fmt、validate、lint、tests、policy checks
  C->>C: 使用固定 Terraform/Provider 產生 plan
  C-->>R: Plan artifact+create/update/delete/replace 摘要
  alt Reviewer 拒絕或 inputs 已改變
    R-->>D: 要求修正並重新 plan
  else 核准精確 plan
    R->>C: Approve deployment
    C->>A: 取得短效 credential/impersonation
    A->>G: Apply with least privilege
    G-->>C: Resource results+state update
    C->>G: Smoke test+policy verification
    C->>O: Logs、plan identity、result、alerts
    C-->>D: Deployment status
  end
Reviewer 核准的是一份可辨識刪除與替換的精確 plan;apply 使用短效、最小權限身分,部署後還要留下 state、驗證與稽核結果。

Source repository 的現況

Cloud Build 可以和 GitHub、GitLab、Bitbucket 等來源整合,也可依需求使用 Secure Source Manager。Cloud Source Repositories 從 2024-06-17 起已不提供給新客戶,因此不應再把它寫成所有新架構的預設程式碼來源。既有客戶要依官方支援狀態規劃。

GitOps、Config Sync 與 Policy Controller

GitOps 把版本庫當作 desired configuration 的來源,controller 持續同步和回報 drift。在 GKE 環境中:

  • Config Sync 同步 Kubernetes configuration。
  • Policy Controller 對送進 Kubernetes API 的 objects 執行 policy constraints。
  • Config Connector 可把支援的 Google Cloud resources 表示成 Kubernetes objects。

因此「Policy Controller 能禁止公開 Cloud Storage bucket」只有在該 bucket 透過受支援的 Config Connector object 管理、且 constraint 有涵蓋時才成立。它不會自動攔住所有人在 Console、Terraform 或 API 建的任意 bucket。組織層級的強制控制仍要評估 Organization Policy、IAM 與其他 Google Cloud controls。

Drift 也不應一律自動改回去。緊急 incident 期間的手動修復若被 controller 立刻覆蓋,反而會擴大問題。團隊要定義 break-glass 流程、暫停 reconciliation 的方式,以及事後把改動帶回 Git 的責任。

查看流程與節點文字

箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。

flowchart TB
  D["偵測 Desired<br/>與 Actual drift"] --> I{"正在處理緊急 Incident?"}
  I -->|"否"| R["Review drift cause<br/>與 blast radius"]
  R --> C{"哪一份狀態<br/>應成為真相?"}
  C -->|"Git 正確"| X["經核准後<br/>Reconcile/<br/>Apply 回去"]
  C -->|"Actual change<br/>應保留"| A["把變更帶回<br/>Code/State<br/>再 review 與 apply"]
  I -->|"是"| P["啟動 Break-glass<br/>必要時暫停<br/>reconciliation"]
  P --> F["執行最小修復<br/>並完整 Audit"]
  F --> B["Incident 穩定後<br/>把修復編碼回<br/>Git 與 tests"]
  B --> V["重新 plan,<br/>確認不會逆轉修復"]
  V --> S["恢復<br/>reconciliation"]
  X --> M["監控 Desired=Actual"]
  A --> M
  S --> M
一般 drift 要 review 後決定回復或採納;incident 的 break-glass 變更則先保命、完整記錄,穩定後必須回寫 Git、重跑 plan,再恢復 controller。

Deployment Manager 已結束支援

Google Cloud Deployment Manager 已於 2026-03-31 結束支援,官方不再提供支援與新功能,預計 2027-06-30 之後關閉服務與 API。過去由 Deployment Manager 建立的個別 Google Cloud resources 仍會運作,也能用一般產品工具管理,不應再依賴 Deployment Manager 更新。

現在遇到舊 Deployment Manager 環境,工作不是在選擇題裡背「Terraform 比較新」,而是:

  1. 盤點 deployment、template、依賴和實際 resources。
  2. 決定使用 Infra Manager、自管 Terraform 或其他工具。
  3. Import/adopt 既有 resources,確認 configuration、state 和 remote system 一致。
  4. 先在低風險環境演練,避免 migration 意外重建 production resources。

官方狀態與遷移說明見 Deployment Manager deprecation。

查看流程與節點文字

箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。

flowchart TB
  I["Inventory deployments、templates、<br/>dependencies 與實際 resources"] --> O["確認 Resource ownership、<br/>criticality 與現有 drift"]
  O --> T{"選新的管理控制面"}
  T -->|"Managed Terraform"| M["Infrastructure<br/>Manager"]
  T -->|"Existing CI/Multi-cloud"| S["Self-managed<br/>Terraform"]
  T -->|"Kubernetes GitOps"| K["Config Connector<br/>僅限支援<br/>resources"]
  M --> C["撰寫等價 desired configuration"]
  S --> C
  K --> C
  C --> A["Import/Adopt,不先重建"]
  A --> P["Plan/Preview 必須顯示<br/>無意外 delete/replace"]
  P --> L["先在低風險環境演練"]
  L --> G{"驗證 ownership、state、<br/>permissions 與 rollback?"}
  G -->|"否"| C
  G -->|"是"| R["分批接管 Production resources"]
既有資源不會因 Deployment Manager 結束支援而消失;遷移核心是先盤點與選 owner,再 import/adopt,確認 preview 不會意外重建後才分批接管。

Cloud Shell 與 Cloud Code 放在哪裡?

Cloud Shell 提供瀏覽器中的臨時開發與管理環境,預裝 gcloud、kubectl 等常用工具,適合快速診斷和教學。Terraform CLI 自 2026 年 6 月 20 日起不再預裝:要在 Cloud Shell 用 Terraform,得照 HashiCorp 的說明自行安裝,可以寫進環境自訂腳本($HOME/.customize_environment)在啟動時安裝,或把執行檔裝在家目錄,讓它跨工作階段保留(見官方 Install Terraform)。Cloud Shell 不是 production pipeline,也不應成為只有某個人知道怎麼操作的部署主機。

Cloud Code 是 VS Code 與 IntelliJ 的開發擴充,協助本機開發、瀏覽 Google Cloud resources,以及處理 GKE、Cloud Run 等工作流。兩者提高個人操作效率,但治理仍要回到 repository、pipeline、IAM 和 audit trail。

離線練習:這份 plan 現在可以核准嗎?

本站合成、精簡的 plan 判讀教材;不是 provider 實測,也不是完整 terraform show -json 輸出。外層 review_context 是教材資料,不是 Terraform 欄位。此 JSON 不能交給 terraform apply。所有名稱、位址、密碼標記與修訂識別均為虛構。

下載本課合成證據(JSON)

可直接在下方閱讀,不必下載。下載檔案包含本課八列變更、一次 drift 與審核背景,不含參考判讀。最外層是本站教材封裝;plan_excerpt 才是依官方欄位語意編寫的精簡片段,省略了完整 configuration、planned values 等內容。它不是 saved binary plan、HCL 或可套用設定,也未由 Google provider 執行產生。

先固定審核範圍

  • 本次需求:升級 checkout 計算層並維持入口與結帳服務可用;未授權刪除封存資料、擴大 IAM 或逆轉事故修復。
  • 目標:demo-production,workspace checkout-demo;兩者都是虛構標籤。
  • 事故證據:INC-DEMO-7:應用已改在 TCP 8443 服務,值班人員將 checkout-internal 從 443 改成 8443;事故尚未結案,修復尚未回寫 Git。
  • 資料保留:demo-checkout-archive 仍含需保留的訂單封存;未提供可驗證的移轉副本、還原演練或刪除核准。

先用 5 分鐘建立八列變更表,保留 address、mode、actions 的原始順序、是否含刪除、影響與待查 owner。用 20 分鐘完成四回合,再用 10 分鐘寫審核備忘錄。先留下自己的判讀,再展開參考。

讀 JSON 時先辨認這些欄位

欄位本課要辨認的語意
resource_changes下一次提議執行的動作;保留 actions 的先後順序
resource_driftRefresh 時觀察到的外部變動,與下一次動作分開看
before/after變更前與目前可知的變更後內容;不能只靠 null 推論值會被清空
after_unknown對應位置為 true 時,表示值在 plan 時尚未確定
before_sensitive/after_sensitive標示需保護的值,不代表 JSON 已遮蔽或加密
replace_paths指出導致替換的欄位路徑;不應用它是否存在判定有無替換

表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。

format_version 是 JSON 表示格式,不是 Terraform CLI/provider 版本。實務上還要記錄工具與 dependency lock file。本課只討論下載片段中的欄位,不能用這個精簡練習取代完整 plan、policy 與部署驗證。欄位語意可對照 HashiCorp JSON output format。

本練習的欄位與命令模式於 2026-10-28 核對官方文件;IAM 權限範圍可另查 Google Cloud roles overview。案例中的核准範圍與事故狀態則是本課自訂條件。

四回合:先判讀,再核對理由

回合 1:替換順序與資料刪除

逐項寫出動作及先後順序。哪些包含刪除?先建後刪能否保證入口不中斷?封存 bucket 還缺什麼證據?

{
  "resource_changes": [
    {
      "address": "google_compute_instance.checkout",
      "mode": "managed",
      "type": "google_compute_instance",
      "name": "checkout",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "delete",
          "create"
        ],
        "before": {
          "name": "checkout-blue"
        },
        "after": {
          "name": "checkout-green"
        },
        "after_unknown": {},
        "before_sensitive": {},
        "after_sensitive": {},
        "replace_paths": [
          [
            "name"
          ]
        ]
      }
    },
    {
      "address": "google_compute_address.ingress",
      "mode": "managed",
      "type": "google_compute_address",
      "name": "ingress",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "create",
          "delete"
        ],
        "before": {
          "name": "ingress-blue",
          "address": "192.0.2.10"
        },
        "after": {
          "name": "ingress-green",
          "address": null
        },
        "after_unknown": {
          "address": true
        },
        "before_sensitive": {},
        "after_sensitive": {},
        "replace_paths": [
          [
            "name"
          ]
        ]
      }
    },
    {
      "address": "google_storage_bucket.archive",
      "mode": "managed",
      "type": "google_storage_bucket",
      "name": "archive",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "delete"
        ],
        "before": {
          "name": "demo-checkout-archive",
          "location": "ASIA-EAST1"
        },
        "after": null,
        "after_unknown": {},
        "before_sensitive": {},
        "after_sensitive": {}
      }
    }
  ]
}
先寫再看:回合 1 的參考判讀
  • checkout 是先刪後建的替換;ingress 是先建後刪的替換。兩者都包含 delete,不能只找 actions 等於 ["delete"]。replace_paths 說明這份教材中的替換原因;真實 plan 也可能因 taint 或其他原因替換,不能用這個欄位是否存在代替 actions。
  • 先建後刪不保證零中斷:仍需足夠 quota/容量、名稱與依賴相容性,以及新位址、DNS/流量與結帳旅程的切換驗證。這份片段沒有證明以上條件已成立。
  • archive 是單純刪除,且與保留需求衝突。暫緩這份 plan,找資料 owner 確認保留要求、可驗證副本與還原、核准與清理範圍;不能以「之後再建同名 bucket」視為資料回復。

回合 2:未確定值、敏感值與讀取

password 和 number 的 null 各代表什麼?讀 data source 算不算建立一個雲端資源?若真實 JSON 含敏感值,你會如何分享審核證據?

{
  "resource_changes": [
    {
      "address": "google_sql_user.checkout",
      "mode": "managed",
      "type": "google_sql_user",
      "name": "checkout",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "update"
        ],
        "before": {
          "name": "checkout",
          "password": "LAB_PLACEHOLDER_OLD"
        },
        "after": {
          "name": "checkout",
          "password": null
        },
        "after_unknown": {
          "password": true
        },
        "before_sensitive": {
          "password": true
        },
        "after_sensitive": {
          "password": true
        }
      }
    },
    {
      "address": "data.google_project.current",
      "mode": "data",
      "type": "google_project",
      "name": "current",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "read"
        ],
        "before": null,
        "after": {
          "project_id": "demo-checkout-project",
          "number": null
        },
        "after_unknown": {
          "number": true
        },
        "before_sensitive": {},
        "after_sensitive": {}
      }
    }
  ]
}
先寫再看:回合 2 的參考判讀
  • 兩個欄位都被 after_unknown 標為 true,代表 plan 時尚未確定,不能直接讀成清空、空字串或安全預設。unknown 是需要補驗證的資訊,不等於每種未知都必須拒絕;本例密碼輪替需驗證應用更新、連線與失敗處理。
  • SQL user 的 password 同時標為 sensitive。這兩個維度彼此獨立:已知值也可能敏感,未知值也可能敏感。LAB_PLACEHOLDER_OLD 只是合成標記;真實 saved plan/JSON 仍可能含明文敏感資料,不能把 sensitive 當成檔案已加密或完成遮蔽。
  • data.google_project.current 的 mode 是 data、動作是 read;它不是一次 managed resource create,也不是變更失敗。分享時只提供必要且已檢查遮蔽的摘要,原始 artifact 存在受控位置並限制存取與保留。

回合 3:Drift 與權限擴大

比較 resource_drift 與 resource_changes:哪個是已觀察到的改動,哪個是接下來要做的動作?現在 apply 會如何影響事故修復?IAM 的 create 數量少,就代表低風險嗎?

{
  "resource_changes": [
    {
      "address": "google_compute_firewall.checkout",
      "mode": "managed",
      "type": "google_compute_firewall",
      "name": "checkout",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "update"
        ],
        "before": {
          "name": "checkout-internal",
          "allow": [
            {
              "protocol": "tcp",
              "ports": [
                "8443"
              ]
            }
          ]
        },
        "after": {
          "name": "checkout-internal",
          "allow": [
            {
              "protocol": "tcp",
              "ports": [
                "443"
              ]
            }
          ]
        },
        "after_unknown": {},
        "before_sensitive": {},
        "after_sensitive": {}
      }
    },
    {
      "address": "google_project_iam_member.release",
      "mode": "managed",
      "type": "google_project_iam_member",
      "name": "release",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "create"
        ],
        "before": null,
        "after": {
          "project": "demo-checkout-project",
          "role": "roles/owner",
          "member": "serviceAccount:release@demo-checkout-project.iam.gserviceaccount.com"
        },
        "after_unknown": {},
        "before_sensitive": {},
        "after_sensitive": {}
      }
    }
  ],
  "resource_drift": [
    {
      "address": "google_compute_firewall.checkout",
      "mode": "managed",
      "type": "google_compute_firewall",
      "name": "checkout",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "update"
        ],
        "before": {
          "name": "checkout-internal",
          "allow": [
            {
              "protocol": "tcp",
              "ports": [
                "443"
              ]
            }
          ]
        },
        "after": {
          "name": "checkout-internal",
          "allow": [
            {
              "protocol": "tcp",
              "ports": [
                "8443"
              ]
            }
          ]
        },
        "after_unknown": {},
        "before_sensitive": {},
        "after_sensitive": {}
      }
    }
  ]
}
先寫再看:回合 3 的參考判讀
  • resource_drift 記錄觀察到的 443 → 8443;resource_changes 提議的是 8443 → 443。不能把 drift 再加進下一次操作計數,也不能只看 update 就忽略它會逆轉尚在使用的事故修復。
  • 依 INC-DEMO-7 先和 incident owner 確認要保留的修復,把適當設定與測試回寫 Git,再重新產生並審核 plan。這不是一律接受 Console 變更,也不是立即 apply 以消除 drift。
  • roles/owner 是本次未授權的權限擴大。即使只有一個 create,也應暫緩,請身分 owner 提出最小權限需求與驗證;不能用低變更數替代權限影響審查。

回合 4:核准的是哪一份 plan?

這列 no-op 能證明整個系統安全嗎?plan-A 的核准能否沿用到 plan-B?你會留下什麼資訊,讓執行者能確認核准對象?

{
  "review_context": {
    "requested_change": "升級 checkout 計算層並維持入口與結帳服務可用;未授權刪除封存資料、擴大 IAM 或逆轉事故修復。",
    "environment": "demo-production",
    "workspace": "checkout-demo",
    "previous_approval": {
      "artifact_id": "plan-A",
      "source_revision": "demo-revision-a",
      "decision": "只核准 plan-A,不含後續新 plan"
    },
    "candidate": {
      "artifact_id": "plan-B",
      "source_revision": "demo-revision-b",
      "decision": "待審核"
    },
    "incident": "INC-DEMO-7:應用已改在 TCP 8443 服務,值班人員將 checkout-internal 從 443 改成 8443;事故尚未結案,修復尚未回寫 Git。",
    "archive": "demo-checkout-archive 仍含需保留的訂單封存;未提供可驗證的移轉副本、還原演練或刪除核准。",
    "missing_evidence": [
      "替換期間的容量、入口切換與使用者旅程驗證",
      "新位址的 DNS/流量切換與回復驗證",
      "密碼輪替後的應用連線與失敗處理",
      "release 身分的最小權限需求"
    ]
  },
  "resource_changes": [
    {
      "address": "google_compute_network.shared",
      "mode": "managed",
      "type": "google_compute_network",
      "name": "shared",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "no-op"
        ],
        "before": {
          "name": "demo-shared",
          "auto_create_subnetworks": false
        },
        "after": {
          "name": "demo-shared",
          "auto_create_subnetworks": false
        },
        "after_unknown": {},
        "before_sensitive": {},
        "after_sensitive": {}
      }
    }
  ]
}
先寫再看:回合 4 的參考判讀
  • no-op 只表示這列在這次 plan 沒有提議動作;它不能證明其他資源、有效 IAM、未納管資源或整體服務都安全。片段也不是完整 plan,不能拿來核准真實 production。
  • 舊核准僅對應 plan-A/demo-revision-a,候選已是 plan-B/demo-revision-b,不能沿用。本例有未授權刪除、權限擴大與逆轉修復等具體阻擋,決策應為暫緩,列出 owner 與重審條件。
  • 真實流程應綁定 source revision、環境/workspace/backend、工具與 dependency lock file、輸入及精確 saved-plan artifact(例如受控 artifact ID 與雜湊)、審核者與驗證條件。JSON 是閱讀用表示;核准與 apply 要對上原始 saved plan。重新產生 plan 必須重新審核;saved-plan apply 不會再詢問互動確認,核准關卡要在執行前完成。

交付:逐項變更表與審核備忘錄

先核對八列 resource_changes 是否都已列入你的變更表,將「資源列數」和「動作次數」分開。兩種替換都各含一次 create 與 delete,data read 與 no-op 另列;resource_drift 不再加進提議動作計數。寫完再展開核對。

完成變更表後再看:本例的計數與阻擋條件

本例共八列:先刪後建替換一列、先建後刪替換一列、單純刪除一列、update 兩列、create 一列、data read 一列、no-op 一列。依這些 managed resource 動作計,為 3 add、2 change、3 destroy;兩列替換同時計入 add 與 destroy,不能把八列直接讀成八次新增。

本例應暫緩 plan-B:封存刪除、roles/owner 與逆轉事故修復都超出本次核准範圍;替換、入口切換與密碼輪替的驗證也還缺證據。after_unknown 本身不等於失敗,no-op 也不等於整體安全。結論要指向具體資源、owner 與重審條件,不能只寫「有風險」。

可複製這份空白範本到自己的筆記;每個阻擋項目都要填入具體 address、證據與負責人。

IaC 變更審核備忘錄
候選 artifact/source revision:
目標環境/workspace/backend 識別:
本次授權範圍:

八列變更表(每列保留 address、mode、actions 原始順序):
1.
2.
3.
4.
5.
6.
7.
8.
動作計數(add/change/destroy;read/no-op 另列):

決策(暫緩/需補證據/可送核准)與理由:
阻擋項目(address、證據、owner、解除條件):
Drift 的保留或回復決策與 incident owner:
未確定值的取得時點、部署後驗證與失敗處理:
敏感 artifact 的受控位置、遮蔽摘要與存取/保留規則:
下一份 plan 的識別、重審者與使用者旅程驗證:

完成標準是:八列無遺漏、替換順序與計數正確、分開 unknown/sensitive 與 drift/提議動作、列出具體阻擋,並把審核綁定到正確 artifact。這份範本沒有自動評分或核准能力;閱讀、複製與下載不會替你完成課程或改寫既有工作坊草稿。請自行保存筆記;本練習沒有建立雲端資源,結束時只需決定是否保留自己下載的合成檔案。

回到真實 pipeline 前

真實流程中的 saved plan、JSON 與 log 都可能含敏感資料;不要直接貼到公開 PR。只分享必要且已檢查遮蔽的摘要,原始 artifact 放在受控位置。sensitive 是資訊標記,不是加密;詳見 terraform show 與 saved plan 的資料保護注意事項。

若 CI 使用 terraform plan -detailed-exitcode,0 是成功且無差異,1 是錯誤,2 是成功且有差異;2 不是部署核准,也不應被當成一般失敗後直接略過。這與 terraform plan -json 的串流 UI 事件不同:本課讀的是 terraform show -json 形式的 plan 表示。真實執行仍需身分、backend/locking、完整 plan、政策與部署後驗證;本練習沒有執行這些操作。

情境練習:建立多團隊 landing zone

假設平台團隊要替 20 個產品團隊建立 folders、projects、Shared VPC 和安全基線。

動手前先寫下你的設計順序:state 怎麼切、誰來 review plan、apply 用什麼身分、部署後要驗證什麼。寫完再展開對照。

寫完再展開:參考設計

可以這樣設計:

  1. 先定義 folder/project ownership、billing、network 和 security boundaries。
  2. 評估 CFT modules,但用自己的 tests 和 policies 驗證輸出。
  3. 將 organization、network 和 workload stacks 拆成 blast radius 合理的 states,不把全公司塞進一份 state。
  4. State bucket 放在受控管理專案,開啟 versioning、PAP、UBLA 和最小權限。
  5. PR pipeline 產生 plan;平台 reviewer 檢查 replace/delete 和 IAM 變更;production apply 使用專用 service account。
  6. 部署後驗證 Organization Policy、log sinks、Shared VPC attachments 和必要告警。
  7. 每次 provider/module 升級先在測試 organization 或低風險 stack 驗證。

這樣 IaC 才是治理流程,而不是把 Console 點擊翻譯成 HCL。

課後檢查

往下看重點之前,先試著回答這幾題;答不出來,括號裡是該回去重讀的那一節。

  • 排查 Terraform 問題時,要分開看哪三份狀態?(回〈Terraform 的三份真相〉)
  • Remote state 為什麼要限制讀寫、保留版本,還要避免同時 apply?(回〈State 要像敏感營運資料一樣保護〉)
  • GitOps 的 policy 能不能擋住所有人在 Console 或 Terraform 建的資源?為什麼?(回〈GitOps、Config Sync 與 Policy Controller〉)
  • 你的八列變更表是否漏算替換中的 delete?為什麼本例必須暫緩 plan-B?(回〈四回合:先判讀,再核對理由〉)

本課重點

  • IaC 提高可重現性,但 provider、default、state、quota 和人工變更仍會造成差異。
  • Terraform configuration、state 和 remote resources 是三個要分開檢查的狀態。
  • Remote state 要限制讀寫、保留版本並避免互相衝突的 apply。
  • CFT 是可評估的 module/blueprint 起點,不是每個 landing zone 都必須照抄。
  • Infra Manager 管受管理的 Terraform infrastructure deployment;Config Connector 使用 Kubernetes reconciliation。
  • Production pipeline 要保留 plan、核准、短效身分、apply 結果與驗證紀錄。
  • Deployment Manager 已在 2026-03-31 結束支援,既有資源要遷移管理方式。
  • GitOps policy 只會控制它實際看得到的 objects,不能取代組織層 controls。

延伸閱讀

下一步

下一課會進入安全與合規。你會看到 IaC 流程中的 service account、組織政策和資料邊界,如何和 IAM 架構接在一起。

恭喜完成課程!

你已完成所有課程內容,做得太棒了!

25 課完成
22 小時 25 分鐘 預估課程時數
— 天連續學習

鍵盤快捷鍵

先關閉此說明再使用快捷鍵;輸入筆記時不會觸發。

導覽
← / j 上一課
→ / k 下一課
操作
c 標記完成 / 取消完成
n 展開 / 收合筆記
b 收藏 / 取消收藏
f 專注模式
其他
? 快捷鍵說明
Esc 關閉彈窗

課程目錄

徽章解鎖!
Nimbus

學習教練 Nimbus

你的雲端學習夥伴

依已保存的資源進度與學習目標提供練習建議。完成度不代表掌握程度或應考能力。

0
連續天數
0
確認讀完
0
收藏
0%
資源完成度
練習建議
    推薦下一步
    開始