- date
- entry
- 013
- topic
- tooling
- rev
- 1
跑任何 aws 指令之前,先問「我是誰」
多帳號環境下最貴的錯誤不是打錯指令,是在錯的帳號上打對的指令。唯讀查詢會成功、但回你錯的答案;寫入會成功、但東西建在別人家。CLI 全程不會問你確不確定。
典型的一天長這樣。你要查客戶 A 的某個資源:
aws ec2 describe-instances --region ap-northeast-1 \
--filters "Name=tag:Name,Values=api-server"
# → 空的
沒有錯誤、沒有 AccessDenied,就是空的。合理的結論是「這台不存在」,於是你開始查 CI 是不是沒建起來、或者問客戶是不是刪掉了。
實際發生的事是:半小時前你為了另一件事 export AWS_PROFILE=other,那個 shell 還開著。你查的是另一個帳號,而那個帳號裡確實沒有這台機器。
空結果和「不存在」是兩件不同的事,但 CLI 把它們印成一樣的輸出。
憑證是怎麼被決定的
會踩到這個坑,是因為「用哪組憑證」不是你當下打的那行指令決定的,而是一串優先順序決定的。簡化版本(AWS CLI v2):
- 命令列參數(
--profile) - 環境變數(
AWS_PROFILE、AWS_ACCESS_KEY_ID那一組) ~/.aws/credentials與~/.aws/config裡的default- 容器或 EC2 instance metadata 提供的角色
第 2 項是實務上最常出事的一層,原因很單純:環境變數會活得比你的記憶久。它跟著那個 shell 分頁、跟著 tmux session、跟著你昨天沒關的視窗,而它不會在任何提示字元上顯示。
第 4 項則是另一種情境:在 EC2 上跑指令時,如果沒有明確憑證,它會用機器的 instance role——那通常跟你在本機的身分完全不同。
一行檢查
aws sts get-caller-identity
{
"UserId": "AIDA...",
"Account": "111111111111",
"Arn": "arn:aws:iam::111111111111:user/deploy"
}
這條指令不需要任何權限(每個 IAM 身分都能問自己是誰),也不會改任何東西。Account
那一欄就是答案。
要在腳本裡用的話,讓它變成一道閘門:
EXPECTED_ACCOUNT=111111111111
ACTUAL=$(aws sts get-caller-identity --query Account --output text)
if [ "$ACTUAL" != "$EXPECTED_ACCOUNT" ]; then
echo "WRONG ACCOUNT: $ACTUAL (expected $EXPECTED_ACCOUNT)" >&2
exit 1
fi
任何會改動東西的腳本開頭都該有這五行。它的成本是一次 API 呼叫,擋掉的是「在正式帳號上跑了測試環境的清理腳本」這種事。
危險程度不對稱
在錯帳號上執行,後果分兩級,而且差很多:
- 唯讀 → 錯誤的結論。查不到就以為不存在,然後基於這個錯誤前提做下一步——最糟的情況是「重建一份已經存在的東西」,於是同一個資源在兩個帳號各有一份
- 寫入 → 真實破壞。資源建在錯的帳號(帳單和權限都跟著跑掉),或者更糟:刪除指令的目標名稱剛好在兩個帳號都存在
第一級之所以值得單獨講,是因為它看起來沒事。它不會出現在任何事故報告裡,只會表現成「那次查了很久都查不出來」。
成功執行不代表執行在對的地方。
讓它不用靠記得
「每次都先確認身分」是一條紀律,而紀律在趕的時候會失效。幾個把它變成結構的做法:
-
把帳號顯示在提示字元上。shell prompt 裡加一段目前的
AWS_PROFILE,這樣你不必主動想起來去查——沒設就顯示空白,那本身也是資訊 -
不要有
defaultprofile。沒有 default 就沒有「不小心用到預設帳號」這件事,忘了加--profile會直接報錯。錯誤訊息比錯誤結果好 -
profile 名稱用帳號用途,不用公司名。
prod/staging/client-a-prod比縮寫好——名稱要在你看到它的瞬間就能判斷危不危險 -
把帳號 ID 寫進專案文件。「這個專案在
111111111111」是一句話的事,而它讓上面那道閘門有一個可比對的常數
這跟用權限而不是紀律擋住手改 prod 是同一個思路:需要人記得的步驟,在壓力最大的時候最容易被跳過,而那正是它唯一需要生效的時刻。
順帶一提 region
同樣的問題有一個小一號的版本:region。沒指定時它會取設定檔的預設值,而那個預設值可能是你三年前設的。症狀一樣——查不到、以為不存在。
我的做法是每個指令都明寫 --region。多打幾個字,換掉一整類「東西在別的區域」的困惑。
事後只剩 CloudTrail
如果真的在錯的帳號上做了事,唯一能重建現場的是 CloudTrail:誰、什麼時候、用什麼身分、呼叫了什麼 API。
值得知道它在,但不要把它當成防線——它是事後查得出來的工具,不是事前擋得住的工具。查到的時候,事情已經做完了。
如果只記得一件事
空結果不是答案,它是兩個答案的疊加:「這個帳號裡沒有」和「你不在那個帳號」。 在把空結果當成結論之前,先花一次 API 呼叫確認你問的是哪一家。
修訂紀錄
- 改用 ARN 比對:原本建議的帳號 ID 比對會放行 root,見 023
- 首次發布