化與測(cè)試:從Terraform到多賬號(hào)治理)
簡(jiǎn)介面向AWS架構(gòu)師與DevOps工程師這份資源匯集了AWS Landing Zone自動(dòng)化與測(cè)試的可復(fù)用代碼和操作文檔。核心覆蓋測(cè)試環(huán)境搭建、AWS Organizations組織與OU創(chuàng)建、Core賬戶初始化及安全基線配置適用于企業(yè)級(jí)多賬戶治理和云環(huán)境合規(guī)落地場(chǎng)景。資源共69個(gè)文件主要為json策略模板、yml配置模板、md說(shuō)明文檔另含少量Ruby輔助腳本與測(cè)試腳本整體約1.85MB。其中json用于定義IAM權(quán)限與SCP策略yml用于CloudFormation及Config規(guī)則md則梳理了環(huán)境準(zhǔn)備與操作流程結(jié)構(gòu)清晰便于按模塊查閱。目前已有310人學(xué)習(xí)下載。通過(guò)壓縮包可快速獲得CloudFormation工具、多層級(jí)VPC模板、CloudTrail和VPC Flow Logs配置示例以及組織管理、聯(lián)邦訪問(wèn)、成員賬戶治理等腳本能顯著縮短Landing Zone環(huán)境搭建與驗(yàn)證周期。1. AWS Landing Zone 的復(fù)雜度不在搭建而在自動(dòng)化與測(cè)試提到 AWS Landing Zone落地區(qū)很多團(tuán)隊(duì)的第一反應(yīng)是“搭一次就完事”把組織、賬號(hào)、預(yù)算和日志中心創(chuàng)建出來(lái)然后開(kāi)始跑業(yè)務(wù)。我在實(shí)際交付里反復(fù)看到一個(gè)翻車畫(huà)面——過(guò)了半年安全基線升級(jí)或業(yè)務(wù)要新開(kāi)一個(gè)賬號(hào)腳本一重放網(wǎng)絡(luò)與權(quán)限規(guī)則被沖得七零八落沒(méi)人說(shuō)得清哪條參數(shù)是哪一輪變更留下的。Landing Zone 的本質(zhì)不是一次性搭建而是一套需要被自動(dòng)化、被測(cè)試、被反復(fù)重放的云上基建骨架。這篇筆記沿著“自動(dòng)化什么 → 用什么代碼落地 → 怎么驗(yàn)證 → 踩過(guò)哪些坑”往下走配可直接抄下來(lái)的命令與參數(shù)解釋適合負(fù)責(zé)多賬號(hào)治理、正在為審計(jì)準(zhǔn)備配置證據(jù)的從業(yè)者。2. 自動(dòng)化的到底是什么組織、身份與審計(jì)日志三條主線Landing Zone 不等于一臺(tái)虛擬機(jī)或一份配置文件它是一組多賬號(hào)環(huán)境的治理基線。自動(dòng)化改造的第一步不是寫代碼而是搞清楚哪些東西必須被“版本化”。我在項(xiàng)目里見(jiàn)過(guò)不少團(tuán)隊(duì)把精力花在自動(dòng)創(chuàng)建賬號(hào)上卻忽略了賬號(hào)周邊那些真正決定安全邊界的綁定關(guān)系結(jié)果環(huán)境看起來(lái)自動(dòng)化了治理卻失控了。2.1 先分清自動(dòng)化對(duì)象組織、身份與日志三條主線第一層是組織OrganizationOU 樹(shù)、服務(wù)控制策略SCP、賬號(hào)的注冊(cè)與移入移出。這一層解決的是“某個(gè)賬號(hào)在什么權(quán)限邊界內(nèi)運(yùn)行”。第二層是身份統(tǒng)一身份中心或 SSO權(quán)限集Permission Set與賬號(hào)的綁定關(guān)系。這一層解決的是“誰(shuí)可以進(jìn)哪個(gè)賬號(hào)、拿到什么角色”。第三層是審計(jì)與合規(guī)集中日志賬戶、S3 日志桶、CloudTrail 和 Config 的投遞關(guān)系。這一層解決的是“出了事能不能查、查到的證據(jù)是否完整”。很多剛接觸的人把“Landing Zone 自動(dòng)化”理解為“自動(dòng)建賬號(hào)”這是誤區(qū)。賬號(hào)只是入口真正需要被重放的是圍繞賬號(hào)的綁定關(guān)系和安全規(guī)則。一個(gè)新建賬號(hào)如果忘記綁定權(quán)限集或者沒(méi)被移入正確的 OU它就在治理范圍之外。賬號(hào)工廠的自動(dòng)化和這三條主線是綁定的賬號(hào)創(chuàng)建成功的定義應(yīng)該是“賬號(hào)已存在、已加入正確 OU、已綁定默認(rèn)權(quán)限集、日志已開(kāi)始投遞”而不是僅僅收到一個(gè)創(chuàng)建成功的回執(zhí)。2.2 三條自動(dòng)化路線放在同一張表里看邊界控制臺(tái)手工操作、CLI/SDK 腳本、基礎(chǔ)設(shè)施即代碼這三條路線各有適用場(chǎng)景。落地實(shí)踐中它們不是互斥關(guān)系但最終能不能“被測(cè)試”差別很大。路線適用階段狀態(tài)管理可測(cè)試性主要風(fēng)險(xiǎn)控制臺(tái)手工點(diǎn)一次性探索 / 小型演示環(huán)境無(wú)無(wú)漏配、不可重放CLI/SDK 腳本批量遷移 / 臨時(shí)操作弱腳本級(jí)重復(fù)執(zhí)行會(huì)重復(fù)創(chuàng)建基礎(chǔ)設(shè)施即代碼長(zhǎng)期治理 / 變更管理有狀態(tài)文件強(qiáng)學(xué)習(xí)曲線高控制臺(tái)和 CLI 腳本的共同問(wèn)題是沒(méi)有狀態(tài)。你不知道哪些資源是腳本創(chuàng)建的哪些是另一個(gè)人手工改過(guò)的環(huán)境一旦變大排查就變成考古。IaC 的核心價(jià)值不只是“用代碼描述資源”而是把狀態(tài)文件作為唯一事實(shí)來(lái)源后面才談得上自動(dòng)化測(cè)試。我一般建議團(tuán)隊(duì)把 Landing Zone 的骨架用 Terraform 或 CloudFormation 管理控制臺(tái)只留給緊急排查CLI 腳本可以留著做一次性運(yùn)維但不要把關(guān)鍵變更押在臨時(shí)命令上。2.3 動(dòng)手前先摸底三條 AWS CLI 確認(rèn)命令任何代碼化改造之前先把現(xiàn)狀摸清楚。這里假設(shè)你已經(jīng)用 CLI 配置好了憑證并且有 Organizations 的讀取權(quán)限。三條命令分別覆蓋“根”“OU”“賬號(hào)歸屬”這是后面寫代碼要依賴的三種數(shù)據(jù)。# 1. 查看當(dāng)前組織結(jié)構(gòu)和根OU信息 aws organizations list-roots --output table # 2. 查看現(xiàn)有OU列表確認(rèn)邏輯分層是否合理 aws organizations list-organizational-units --parent-id r-xxxx --output table # 3. 查看賬號(hào)列表以及每個(gè)賬號(hào)所在的OU for id in $(aws organizations list-accounts --query Accounts[?StatusACTIVE].Id --output text); do echo Account: $id aws organizations list-parents --child-id $id --output text done第一條命令返回組織的根 ID通常形式是r-xxxx。第二條命令的--parent-id要填上一條命令查到的根 ID返回當(dāng)前根下直接掛著的 OU。第三條命令遍歷所有活躍賬號(hào)逐個(gè)查詢父節(jié)點(diǎn)如果賬號(hào)掛在 OU 下面返回 OU ID如果賬號(hào)直接掛在根下返回根 ID。很多歷史項(xiàng)目里會(huì)看到一批賬號(hào)直接掛在根下這意味著它們不受任何 OU 層級(jí)的 SCP 約束把它們重排進(jìn) OU 正是自動(dòng)化改造的第一步。在實(shí)際操作中我會(huì)把摸底結(jié)果存成 JSON 快照放進(jìn)倉(cāng)庫(kù)的baseline/目錄后續(xù)測(cè)試斷言直接讀這份快照。這樣第一次交付就形成了基線后面每一次變更都能對(duì)照它做差異分析而不是靠記憶判斷環(huán)境是否漂移。3. 把 Landing Zone 寫進(jìn)代碼Terraform 模塊拆分與最小可跑樣例選 Terraform 不是因?yàn)樗?CloudFormation 酷而是因?yàn)闇y(cè)試這件事在 Terraform 生態(tài)里更成熟。這一章從選型理由講到模塊拆分再落到一段能跑的最小代碼以及批量接入賬號(hào)的兩種方式。3.1 為什么選 Terraform 而不選 CloudFormation狀態(tài)、測(cè)試與遷移成本Terraform 的terraform plan天然是一份 diff 報(bào)告能直接輸出成 JSON 放進(jìn)測(cè)試流程state文件把資源歸屬講得很清楚。CloudFormation 是云平臺(tái)原生能力不用額外維護(hù)狀態(tài)鎖但它的變更集要回控制臺(tái)查看腳本化不如 Terraform 順手。對(duì)于已有大量 CloudFormation 的團(tuán)隊(duì)不必推翻更常見(jiàn)的做法是用 Terraform 管 Landing Zone 骨架CloudFormation 留給業(yè)務(wù)應(yīng)用。兩者不沖突很多團(tuán)隊(duì)把 SCP、OU 和權(quán)限集用 Terraform 管理業(yè)務(wù)棧繼續(xù)用 CloudFormation互不干擾。這里要注意一個(gè)認(rèn)知Landing Zone 一旦引入 Control Tower 這類托管服務(wù)Terraform 直接新建的資源會(huì)被托管服務(wù)接管一部分生命周期。比如 Control Tower 會(huì)強(qiáng)制管理某些 SCP 或日志投遞。代碼里要對(duì)這類被托管資源做只讀聲明或配置lifecycle否則每次 plan 都會(huì)看到平臺(tái)側(cè)做出的變更被標(biāo)記為漂移。這一塊是后面避坑章第一個(gè)大坑的來(lái)源提前理解可以省掉很多排查時(shí)間。3.2 模塊拆分組織、身份、日志與安全三塊怎么切我一般按“組織層、身份層、觀測(cè)與安全層”拆三個(gè)模塊每個(gè)模塊一個(gè)獨(dú)立目錄內(nèi)部按資源類型再拆文件。模塊負(fù)責(zé)資源典型文件organizationOU、SCP、賬號(hào)注冊(cè)ou.tf、scp.tf、account-factory.tfidentity身份中心實(shí)例、權(quán)限集、賬號(hào)分配sso.tf、assignment.tfobservability日志賬戶、S3 桶、CloudTrail、Configlogs.tf、audit.tf拆分的依據(jù)是生命周期。OU 和 SCP 變動(dòng)頻率低身份分配變動(dòng)頻率高日志規(guī)則隨賬號(hào)增長(zhǎng)會(huì)不斷追加。把它們混在一個(gè)目錄里一次賬號(hào)工廠擴(kuò)容會(huì)牽動(dòng)權(quán)限和日志文件review 的人很難判斷意圖。還要注意不要把一個(gè)環(huán)境的配置硬編碼在模塊里用var.environment傳入后面測(cè)試才能同時(shí)跑多套環(huán)境而不互相干擾。3.3 托管 Landing Zone 的最小 Terraform 代碼下面這段代碼覆蓋了組織聲明、OU 創(chuàng)建、SCP 創(chuàng)建與附加。如果你的環(huán)境已經(jīng)啟用了 Control Tower組織資源建議改用data aws_organizations_organization讀取現(xiàn)網(wǎng)避免和托管服務(wù)沖突。# versions.tf —— 鎖定 provider 版本測(cè)試環(huán)境與生產(chǎn)環(huán)境保持一致 terraform { required_version 1.5 required_providers { aws { source hashicorp/aws version ~ 5.0 } } } # 組織與OU如果已啟用 Control Tower把這里改成 data 源讀取現(xiàn)網(wǎng) resource aws_organizations_organization this { aws_service_access_principals [ controltower.amazonaws.com, sso.amazonaws.com, ] enabled_policy_types [SERVICE_CONTROL_POLICY] } resource aws_organizations_organizational_unit workloads { name Workloads parent_id aws_organizations_organization.this.roots[0].id } resource aws_organizations_organizational_unit security { name Security parent_id aws_organizations_organization.this.roots[0].id } # 一條示例SCP禁止刪除審計(jì)追蹤 resource aws_organizations_policy deny_cloudtrail_delete { name DenyCloudTrailDelete description 示例SCP禁止刪除審計(jì)追蹤 type SERVICE_CONTROL_POLICY content jsonencode({ Version 2012-10-17 Statement [ { Sid DenyCloudTrailDelete Effect Deny Action [ cloudtrail:DeleteTrail, cloudtrail:StopLogging ] Resource * } ] }) } # 把SCP附加到 Workloads OU resource aws_organizations_policy_attachment deny_cloudtrail_on_workloads { policy_id aws_organizations_policy.deny_cloudtrail_delete.id target_id aws_organizations_organizational_unit.workloads.id }aws_service_access_principals聲明允許 Control Tower 和 SSO 服務(wù)訪問(wèn)組織這是后續(xù)自動(dòng)化必須要做的。enabled_policy_types啟用 SCP如果這個(gè)參數(shù)一開(kāi)始沒(méi)開(kāi)后面掛策略會(huì)報(bào)錯(cuò)。SCP 的content用jsonencode生成避免手寫 JSON 字符串的轉(zhuǎn)義問(wèn)題。注意CloudTrail:StopLogging也要禁止只禁止 DeleteTrail 攔不住攻擊者先停日志再刪事件。接下來(lái)是身份綁定。下面這段代碼實(shí)現(xiàn)“每個(gè)目標(biāo)賬號(hào)自動(dòng)綁定 PowerUser 權(quán)限集”。# sso.tf —— 每個(gè)賬號(hào)進(jìn)入 Landing Zone 后必須綁定一個(gè)默認(rèn)權(quán)限集 data aws_ssoadmin_instances this {} resource aws_ssoadmin_permission_set power { name PowerUser instance_arn tolist(data.aws_ssoadmin_instances.this.arns)[0] session_duration PT8H } resource aws_ssoadmin_account_assignment power_assign { for_each toset(var.target_account_ids) instance_arn aws_ssoadmin_permission_set.power.instance_arn permission_set_arn aws_ssoadmin_permission_set.power.arn principal_id data.aws_identitystore_user.admin.user_id principal_type USER target_id each.value target_type AWS_ACCOUNT }data.aws_ssoadmin_instances讀取身份中心實(shí)例 ARNfor_each遍歷目標(biāo)賬號(hào) ID 列表principal_id來(lái)自 identitystore 數(shù)據(jù)源。session_duration用 ISO8601 格式PT8H表示 8 小時(shí)。這段代碼的意圖是“每個(gè)新賬號(hào)自動(dòng)綁定權(quán)限集”賬號(hào)工廠跑完后身份同步需要幾分鐘測(cè)試腳本要等待這個(gè)異步過(guò)程不要在 apply 后立刻斷言。提示如果已啟用 Control Tower不要試圖用 Terraform 直接管理它已經(jīng)占用的 OU 和 SCP先 import 進(jìn)狀態(tài)再用 lifecycle 聲明哪些屬性由平臺(tái)托管。3.4 賬號(hào)工廠把新賬號(hào)批量接入的兩種寫法批量接入賬號(hào)最常見(jiàn)的有兩種做法。一種是 Terraform 內(nèi)用aws_organizations_account聲明一批賬號(hào)適合測(cè)試和小規(guī)模交付。另一種是外部腳本調(diào)用aws organizations create-account批量創(chuàng)建再把返回的賬號(hào) ID 寫回變量適合生產(chǎn)環(huán)境因?yàn)橘~號(hào)創(chuàng)建是異步的Terraform 狀態(tài)和實(shí)際完成時(shí)間可能不同步。# account-factory.tf —— 賬號(hào)清單驅(qū)動(dòng) locals { new_accounts { demo-shared { email demo-sharedexample.com, ou aws_organizations_organizational_unit.workloads.id } demo-prod { email demo-prodexample.com, ou aws_organizations_organizational_unit.workloads.id } demo-security { email demo-securityexample.com, ou aws_organizations_organizational_unit.security.id } } } resource aws_organizations_account this { for_each local.new_accounts name each.key email each.value.email parent_id each.value.ou }for_each的 key 就是賬號(hào)名后面歸屬和標(biāo)簽都以它為索引。parent_id決定賬號(hào)放進(jìn)哪個(gè) OU這一步直接決定了它受哪些 SCP 約束。注意郵箱只能用一次測(cè)試賬號(hào)如果反復(fù)創(chuàng)建需要先關(guān)閉原賬號(hào)再換新郵箱。真實(shí)環(huán)境中我不會(huì)用 Terraform 直接創(chuàng)建生產(chǎn)賬號(hào)而是用腳本批量創(chuàng)建再導(dǎo)入狀態(tài)因?yàn)閏reate-account是異步的直接讓 Terraform 管理容易出現(xiàn)“資源已創(chuàng)建但狀態(tài)未更新”的中間態(tài)。4. 給 Landing Zone 做回歸測(cè)試三層驗(yàn)證與最小命令集很多團(tuán)隊(duì)只做terraform plan就以為測(cè)試過(guò)了其實(shí) plan 只能證明語(yǔ)法正確、云平臺(tái)認(rèn)可這次變更不能證明“身份能登錄、日志能落桶、權(quán)限集生效”?;貧w測(cè)試要按成本從低到高分三層做。4.1 三層驗(yàn)證策略靜態(tài)檢查、漂移檢測(cè)、行為驗(yàn)證靜態(tài)檢查負(fù)責(zé)在代碼階段攔截低級(jí)錯(cuò)誤比如格式問(wèn)題、語(yǔ)法錯(cuò)誤、明顯越權(quán)的策略。漂移檢測(cè)負(fù)責(zé)發(fā)現(xiàn)真實(shí)環(huán)境與代碼不一致比如有人手工改了 SCP 或日志桶策略。行為驗(yàn)證負(fù)責(zé)確認(rèn)整條鏈路真的能跑通比如新賬號(hào)能登錄、日志對(duì)象出現(xiàn)在集中桶里。層級(jí)要發(fā)現(xiàn)什么執(zhí)行時(shí)機(jī)常用工具靜態(tài)檢查語(yǔ)法錯(cuò)誤、安全配置、越權(quán)策略每次提交terraform validate、tflint、checkov漂移檢測(cè)真實(shí)環(huán)境與代碼不一致、手工改動(dòng)定時(shí) / 每次 planterraform plan、AWS Config行為驗(yàn)證SSO 能登錄、權(quán)限集生效、日志投遞發(fā)版后 / 定期自寫斷言腳本三層不能互替。plan 看到 No changes 不代表行為正確行為驗(yàn)證通過(guò)不代表一年后不漂移。所以流水線里面三層順序執(zhí)行最后一層人工抽檢。4.2 靜態(tài)檢查階段terraform 與 checkov 的最小命令集# 1. 格式與語(yǔ)法檢查 terraform fmt -check -recursive terraform validate # 2. 安全策略掃描把結(jié)果當(dāng)作CI門禁 checkov -d . --framework terraform \ --skip-check CKV_AWS_272 \ --quiet # 3. 計(jì)劃階段輸出為文件供下游斷言 terraform plan -outtfplan.bin terraform show -json tfplan.bin tfplan.jsonterraform validate不需要初始化后端也能跑適合在 git 鉤子里做快速反饋。checkov默認(rèn)規(guī)則很嚴(yán)格第一次跑可能被大量告警嚇到我會(huì)先人工評(píng)審后再用--skip-check排除那些明確接受風(fēng)險(xiǎn)的規(guī)則并把排除理由寫進(jìn)代碼注釋。terraform plan -out保存二進(jìn)制計(jì)劃后續(xù)必須用terraform show -json來(lái)解析計(jì)劃 JSON 里包含每個(gè)資源的actions字段斷言腳本可以直接判斷是否有“替換”操作出現(xiàn)在不該出現(xiàn)的資源上。4.3 行為驗(yàn)證階段用臨時(shí)賬號(hào)走通 SSO、預(yù)算與日志鏈路前置準(zhǔn)備是創(chuàng)建一個(gè)臨時(shí)測(cè)試賬號(hào)然后輪詢等待創(chuàng)建成功。# 行為驗(yàn)證腳本片段創(chuàng)建臨時(shí)測(cè)試賬號(hào)并等待可用 TMP_ACCOUNTdemo-test-$(date %s) aws organizations create-account --email tmp-${TMP_ACCOUNT}example.com \ --account-name $TMP_ACCOUNT # 輪詢等待 CREATE_SUCCEEDED aws organizations list-accounts --query \ Accounts[?Name${TMP_ACCOUNT}].Status --output text賬號(hào)創(chuàng)建是異步的輪詢間隔建議 5 秒一次最長(zhǎng)等 10 分鐘。賬號(hào)創(chuàng)建成功后還要等身份同步。這里郵箱必須每次不同否則create-account會(huì)直接報(bào)沖突。測(cè)試腳本跑完記得把臨時(shí)賬號(hào)關(guān)閉或移出避免占用郵箱額度。接下來(lái)是斷言腳本。這份腳本把基線快照當(dāng)作唯一事實(shí)來(lái)源本地和 CI 共用同一個(gè)文件。# verify_landing_zone.py —— 針對(duì)基線快照做斷言 import boto3 import json baseline json.load(open(baseline/org.json)) org boto3.client(organizations) sso boto3.client(sso-admin) # 斷言1所有 OU 都在預(yù)期路徑里 roots org.list_roots()[Roots] ou_list org.list_organizational_units(ParentIdroots[0][Id]) actual_ous {ou[Name] for ou in ou_list[OrganizationalUnits]} expect_ous set(baseline[ous]) assert expect_ous actual_ous, f缺失 OU: {expect_ous - actual_ous} # 斷言2每個(gè)生產(chǎn)賬號(hào)都綁定了至少一個(gè)權(quán)限集 for acct in baseline[prod_accounts]: assignments sso.list_account_assignments( AccountIdacct, InstanceArnbaseline[sso_instance_arn], PermissionSetArnbaseline[power_permission_set_arn], ) assert assignments.get(AccountAssignments), f賬號(hào) {acct} 未綁定權(quán)限集list_account_assignments返回的AccountAssignments列表如果是空的說(shuō)明權(quán)限綁定沒(méi)生效不用去控制臺(tái)人肉確認(rèn)。prod_accounts列表來(lái)自baseline/org.json我把生產(chǎn)賬號(hào)名單單獨(dú)存文件臨時(shí)測(cè)試賬號(hào)自動(dòng)被過(guò)濾避免誤傷。4.4 把驗(yàn)證結(jié)果固化成斷言清單行為驗(yàn)證的斷言應(yīng)該沉淀成一張清單每次發(fā)版后照著跑。我常用的一張表長(zhǎng)這樣驗(yàn)證項(xiàng)命令 / 方法預(yù)期結(jié)果組織結(jié)構(gòu)與基線一致比較 list-ous 輸出與基線 JSONOU 集合與基線相同賬號(hào)歸屬正確遍歷 list-parents賬號(hào)父節(jié)點(diǎn)不在根下權(quán)限集綁定上述 python 斷言每個(gè)生產(chǎn)賬號(hào)至少一條記錄日志到達(dá)集中桶s3api head-object 輪詢5 分鐘內(nèi)對(duì)象存在預(yù)算告警生成測(cè)試賬單收到事件通知多寫一條斷言之后排障時(shí)間就少一塊。手工改了配置第二天定時(shí)任務(wù)跑漂移檢測(cè)發(fā)通知這就是測(cè)試的長(zhǎng)期價(jià)值。不要嫌腳本啰嗦這些斷言是環(huán)境健康度的體溫計(jì)。5. AWS Landing Zone 自動(dòng)化與測(cè)試避坑手冊(cè)從狀態(tài)文件到最小權(quán)限下面的問(wèn)題都是項(xiàng)目里反復(fù)出現(xiàn)的按“現(xiàn)象 → 原因 → 解決”寫你可以直接對(duì)照日志特征排查。5.1 狀態(tài)文件失同步、SSO 權(quán)限不生效與日志斷流三個(gè)隱蔽場(chǎng)景坑一幾周前還能正常跑的terraform apply這次一跑就報(bào)ConflictException或AlreadyExists定位到 Control Tower 托管的資源。最常見(jiàn)的原因是有人手工改了資源比如手動(dòng)給某個(gè) OU 加了 SCP或調(diào)整了日志桶策略導(dǎo)致真實(shí)環(huán)境和 Terraform 狀態(tài)文件脫節(jié)。Landing Zone 平臺(tái)服務(wù)還有自己的編排Terraform 不知道這些外部變更重放時(shí)兩邊打架。解決方法是先把這些資源terraform import進(jìn)狀態(tài)再看 plan 的 diff如果差異來(lái)自平臺(tái)托管屬性給對(duì)應(yīng)資源加lifecycle { ignore_changes [...] }明確告訴 Terraform 哪些屬性由平臺(tái)代管并在代碼注釋里寫明“此屬性由平臺(tái)接管禁止手工改動(dòng)”??佣沦~號(hào)創(chuàng)建成功SSO 里也能看到賬號(hào)但用戶登錄后角色權(quán)限還是舊版或者根本沒(méi)有權(quán)限集。原因是aws_ssoadmin_account_assignment的傳播是異步的控制臺(tái)頁(yè)面有時(shí)顯示已分配但服務(wù)端還沒(méi)完成。另一部分情況是權(quán)限集改了內(nèi)聯(lián)策略但沒(méi)有重新觸發(fā) AccountAssignment 更新Terraform 看不出差異。解決方法是把list_account_assignments的輪詢斷言寫進(jìn)測(cè)試腳本等傳播完成再往下走如果權(quán)限集是手工改的用aws sso-admin describe_permission_set和代碼逐項(xiàng)對(duì)照。等待時(shí)間一般幾分鐘CI 里必須輪詢判定不能固定 sleep 30 秒就完事。坑三新建子賬號(hào)后CloudTrail 日志沒(méi)有出現(xiàn)在集中 S3 桶里檢查發(fā)現(xiàn)寫入角色沒(méi)有權(quán)限。最常見(jiàn)的原因是日志中心 S3 桶的 bucket policy 里只允許了之前那批賬號(hào)新建賬號(hào)不在白名單內(nèi)。解決方法是賬號(hào)工廠模塊同時(shí)維護(hù)一個(gè) bucket policy 變量每次新增賬號(hào)就把賬號(hào) ID 追加進(jìn)策略條件如果賬號(hào)很多改用組織級(jí)條件鍵aws:PrincipalOrgID整個(gè)組織內(nèi)的賬號(hào)自動(dòng)放行以后不用來(lái)回改策略。我一般直接上組織級(jí)條件省掉后續(xù)每次擴(kuò)容都要改日志桶策略的負(fù)擔(dān)。5.2 OU 共置與最小權(quán)限差異自動(dòng)化測(cè)試的另外兩個(gè)坑坑四測(cè)試腳本做“清理多余賬號(hào)”時(shí)把生產(chǎn)環(huán)境里的賬號(hào)一起刪了或移走了。原因是測(cè)試賬號(hào)和生產(chǎn)賬號(hào)共用一個(gè) Landing Zone測(cè)試腳本拿到的是完整賬號(hào)列表沒(méi)有按環(huán)境過(guò)濾。解決方法是給每個(gè)賬號(hào)打強(qiáng)制標(biāo)簽Environment: prod|test所有自動(dòng)化測(cè)試先按標(biāo)簽過(guò)濾更保險(xiǎn)的做法是測(cè)試賬號(hào)放在獨(dú)立 OU并用單獨(dú)的賬號(hào)工廠流程管理。不要心疼那點(diǎn)成本這個(gè)隔離能擋掉最狠的一次事故。坑五同樣一段 Terraform 代碼本地apply成功一進(jìn) CI 流水線就報(bào)AccessDenied日志指向某個(gè)iam:*操作。原因是本地用的管理員角色流水線用 OIDC 或臨時(shí)憑證最小權(quán)限策略沒(méi)覆蓋完全。另一個(gè)常見(jiàn)誘因是本地環(huán)境變量里殘留了舊的AWS_PROFILE導(dǎo)致本地根本沒(méi)用到預(yù)期的角色。解決方法是在流水線第一步強(qiáng)制打印當(dāng)前身份并把 CI 角色的權(quán)限也寫成 Terraform 模塊和 Landing Zone 一起版本化。aws sts get-caller-identity這一條命令輸出Account、Arn、UserId??吹?Arn 里的角色名和你預(yù)期不一致直接排查環(huán)境變量而不是花兩小時(shí)看權(quán)限策略。我在所有自動(dòng)化流程里都把這條命令放在最前面成本極低但能排除一大類“本地能跑、流水線不能跑”的玄學(xué)問(wèn)題。6. 把自動(dòng)化接進(jìn)交付流程的最后一步本地閘門與變更影響表自動(dòng)化代碼寫好了測(cè)試腳本也有了最后一步是把它裝進(jìn)團(tuán)隊(duì)的日常交付流程而不是放在某個(gè)人的電腦里。6.1 用 git pre-push 鉤子做低成本回歸在團(tuán)隊(duì)倉(cāng)庫(kù)里放一個(gè)共享鉤子每次推送前先跑一遍靜態(tài)檢查擋掉最基礎(chǔ)的格式和掃描問(wèn)題。腳本寫到.githooks/pre-push然后讓每個(gè)開(kāi)發(fā)者執(zhí)行一次git config core.hooksPath .githooks。#!/usr/bin/env bash # .githooks/pre-push —— 每次推送前先過(guò)一遍靜態(tài)檢查 set -euo pipefail echo [pre-push] terraform fmt terraform fmt -check -recursive echo [pre-push] terraform validate terraform validate -no-color echo [pre-push] checkov scan checkov -d . --framework terraform --quiet-no-color是為了讓輸出在本地終端和 CI 里保持一致。set -euo pipefail保證任何一步失敗就中斷推送。這套鉤子不能替代完整的 plan 和行為驗(yàn)證但能讓同事不用等 CI 排隊(duì)就發(fā)現(xiàn)問(wèn)題。注意core.hooksPath指向的是目錄而不是單個(gè)文件鉤子文件要有可執(zhí)行權(quán)限。提示core.hooksPath 配置只需要每個(gè)開(kāi)發(fā)者執(zhí)行一次鉤子本身放進(jìn) git 倉(cāng)庫(kù)這樣整個(gè)團(tuán)隊(duì)用的是同一份檢查邏輯。6.2 變更影響表每次改動(dòng)前先回答三個(gè)問(wèn)題我不要求每個(gè)團(tuán)隊(duì)都上正式的變更管理工具一張表就夠用。改任何 Landing Zone 相關(guān)代碼前先回答三行內(nèi)容這次改動(dòng)涉及哪些資源會(huì)影響多少個(gè)賬號(hào)有沒(méi)有對(duì)應(yīng)的行為驗(yàn)證腳本?;卮鸩怀鰜?lái)的變更不要進(jìn)流水線。問(wèn)題回答改的是 OU / SCP / 身份 / 日志哪一類資源例如新增一個(gè) SCP 并附加到 Workloads OU影響哪些賬號(hào)例如3 個(gè)生產(chǎn)賬號(hào)需同步驗(yàn)證權(quán)限行為驗(yàn)證怎么做例如跑 verify_landing_zone.py 日志投遞斷言我自己吃過(guò)一次虧改了一個(gè)看起來(lái)只影響日志投遞的變量結(jié)果把整個(gè)組織的 OU 路徑帶偏一批賬號(hào)一夜之間脫離了 SCP 保護(hù)。從那以后凡是 Landing Zone 的改動(dòng)我默認(rèn)先跑一遍行為驗(yàn)證再提交。這個(gè)方向值不值得投入要看你的賬號(hào)規(guī)模和管理頻率但只要超過(guò)五個(gè)賬號(hào)自動(dòng)化與測(cè)試的投入一定會(huì)被審計(jì)和排障時(shí)間成倍補(bǔ)回來(lái)。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取