上一篇提到,Control Tower 與 AWS Organizations 可以透過 Controls、SCP 等機制建立帳號層級的治理邊界,不過帳號允許使用某項 AWS 服務,不代表其中每個 IAM User 或 Role 都能直接操作該服務。
假設某位工程師需要把部署檔上傳到指定的 S3 Bucket,他檢查部署使用的 IAM Role,確認 Policy 已經允許 s3:PutObject:
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::company-release-prod/*"
}
但實際上傳檔案時,仍然收到 AccessDenied。
既然已經有 Allow,AWS 為什麼還是不允許這次操作?
AWS 收到請求後,不會只檢查其中一份 Policy,而是根據呼叫身分、操作、資源、條件,以及所有適用的政策一起判斷結果。
最基本的規則可以先記成三句:
Allow,預設就是拒絕。Allow,請求才有可能通過。Deny,最後仍然是拒絕。一次請求可能同時受到以下政策影響:
| 政策類型 | 主要用途 | 是否能直接授予權限 |
|---|---|---|
| Identity-based Policy | 定義 IAM User 或 Role 可以執行哪些操作 | 可以 |
| Resource-based Policy | 從 S3 Bucket、SQS Queue 等資源端決定誰可以存取 | 可以 |
| Permissions Boundary | 限制 IAM User 或 Role 最多能取得哪些權限 | 不會 |
| Session Policy | 限制這一次暫時憑證工作階段的權限 | 不會 |
| SCP | 限制成員帳號中的身分最多可以執行哪些操作 | 不會 |
| RCP | 限制組織內資源最多可以接受哪些存取 | 不會 |
Permissions Boundary、Session Policy、SCP 與 RCP 比較像權限的上限,它們不會主動把權限授予某個 Role,只會進一步限制原本可以使用的範圍。
因此,即使 Identity-based Policy 已經有 Allow,只要這項操作超出其他政策允許的範圍,最後仍然會被拒絕。

圖:IAM 身分的有效權限,是 Identity-based Policy、Permissions Boundary 與 SCP 共同允許的範圍。此圖為簡化示意,未包含 Session Policy、Resource-based Policy 與 RCP;任何適用的明確
Deny仍會優先拒絕請求。
看到 AccessDenied 時,不能只根據 Role Policy 裡已經有 Allow,就直接判斷是哪一份政策出了問題。
同樣是 s3:PutObject 失敗,可能的原因包括:
因此,我會先確認實際發出請求的身分:
aws sts get-caller-identity
接著核對錯誤中的 Action、Resource 與完整訊息。有些 AccessDenied 訊息會進一步指出,是 Identity-based Policy 沒有允許、Permissions Boundary 限制了操作,或 SCP 中存在明確的 Deny;但並非所有服務都會提供相同程度的說明。
如果訊息只有簡短的 AccessDenied,就要回頭確認這次請求實際經過哪些政策與服務,而不是繼續在同一份 Role Policy 裡增加 Allow。
同樣是權限不足,背後可能代表完全不同的事情:可能是 Role 根本沒有取得權限,也可能是組織刻意限制這項操作,還可能是資源本身不接受這次存取。
這些政策看起來都在處理「權限」,實際上負責的問題並不相同:Identity-based Policy 回答的是「這個身分可以做什麼」;Permissions Boundary 與 SCP 設定的是「最多不能超過哪裡」;Resource-based Policy 與 RCP 則從資源端決定「這個資源願意接受誰的存取」。
因此,處理權限需求時,不能只問 Policy 裡有沒有 Allow,也不能看到 AccessDenied 就隨便挑一份政策放寬,更重要的是先判斷:這項規則要授予誰權限、限制多大的範圍,以及應該由身分端、組織端還是資源端負責。
不同政策解決的是不同範圍的問題,我通常會先確認這項規則是在授予工作需要的權限,還是在設定不能跨越的邊界,舉幾個簡單的例子來說明,當然實現方式有很多,這邊也分享一下我的作法。
| 權限需求 | 適合放置的位置 | 原因 |
|---|---|---|
| 部署 Role 可以把檔案上傳到指定 S3 Bucket | Identity-based Policy | 屬於特定 Role 完成工作需要的權限 |
| 某個 IAM Role 最多只能操作核准的服務 | Permissions Boundary | 限制這個 Role 能取得的權限上限 |
| 所有成員帳號都不能停止 CloudTrail | SCP | 屬於整個組織都不能違反的限制 |
| 集中日誌 Bucket 不得接受組織外部存取 | RCP,必要時搭配 Bucket Policy | 從資源端限制可以接受的存取範圍 |
| 單一 Bucket 只允許指定 Role 存取 | Bucket Policy | 規則只和該資源有關,不必影響整個組織 |
可以把判斷方式簡化成以下幾個方向:
規則放得越上層,涵蓋範圍越大,但例外也越難處理;規則越靠近特定 Role 或資源,調整比較靈活,但一致性需要由各團隊自行維持。
例如,「所有成員帳號都不能停止 CloudTrail」可以優先放在 SCP,因為它不會隨不同 Role 改變,但如果只是某個部署 Role 需要上傳檔案,把 Bucket 名稱與 s3:PutObject 寫進 SCP,就會讓組織政策混入應用程式細節,未來 Bucket 更名或部署方式改變時,也必須修改影響範圍更大的組織政策。
相反地,如果「不能停止 CloudTrail」只寫在個別 Role Policy,其他 Role 仍然可能取得這項權限,也無法真正形成組織層級的邊界。
政策不是放得越集中越好,而是要放在和影響範圍相符的位置。
回到一開始的案例,s3:PutObject 只屬於部署 Role 的工作權限,因此我會選擇放在 Identity-based Policy,而不是把特定 Bucket ARN 寫進影響整個組織的 SCP 或 RCP。
如果這個 Role 還套用了 Permissions Boundary,該操作也必須落在 Boundary 允許的範圍內;但 Boundary 只負責限制上限,不會取代 Identity-based Policy 授予權限。
換句話說,工作所需的權限盡量放在靠近身分或資源的位置;所有帳號都不能違反的規則,才提升到 Organizations 層。 規則放得越上層,影響範圍越大,例外也越難處理。
AccessDenied 不一定代表權限給得不夠多,更可能代表另一層政策不允許這次請求,真正要確認的不是還能在哪裡補一條 Allow,而是哪一層正在限制它,以及那項限制是否符合原本的設計。
今天討論的仍然是組織內部如何判斷與放置權限,如果呼叫端與資源分屬不同帳號,除了呼叫者本身要有權限,目標帳號也必須願意接受這次存取。下一篇會繼續拆解跨帳號存取,看看 AssumeRole、Trust Policy 與 Resource-based Policy 分別解決什麼問題。