<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>模組負一：還沒有 infra 的環境怎麼盡量做好 on Tarragon</title><link>https://tarrragon.github.io/blog/infra/before-infra/</link><description>Recent content in 模組負一：還沒有 infra 的環境怎麼盡量做好 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 26 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/infra/before-infra/index.xml" rel="self" type="application/rss+xml"/><item><title>手動環境的可控底線與納管準備</title><link>https://tarrragon.github.io/blog/infra/before-infra/manual-environment-baseline/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/infra/before-infra/manual-environment-baseline/</guid><description>&lt;p>手動起家是絕大多數服務的常態起點。從一個人在 Console 點出第一台 EC2 驗證想法，到小團隊接手開始長出更多資源，環境會經歷一段「全部靠手動、沒有任何程式碼描述」的階段。這個階段在&lt;a href="https://tarrragon.github.io/blog/infra/00-infra-mindset/#%e6%88%90%e7%86%9f%e5%ba%a6%e9%9a%8e%e6%a2%af" data-link-title="模組零：infra 是什麼，為什麼 day 1 就要鋪地基" data-link-desc="基礎設施的責任邊界、成熟度階梯，以及地基為什麼總在環境爆炸時才被看見">成熟度階梯&lt;/a>（從全手動到全程式碼治理的五階分級）上屬於第零階，它的責任是把自己管理成「可控的手動」，而不是假裝已經納管。可控意味著三件事：高風險操作有護欄、關鍵變更有痕跡、現實長什麼樣有紀錄。做好這三件事，當下出事的成本降低，未來把資源 import 進 IaC 的成本也跟著降低。&lt;/p>
&lt;h2 id="判讀自己是否可控">判讀自己是否可控&lt;/h2>
&lt;p>可控的手動環境能在五分鐘內回答以下問題：&lt;/p>
&lt;ol>
&lt;li>production 有哪些對外開放的 port？&lt;/li>
&lt;li>上週誰動過資料庫參數，動了什麼？&lt;/li>
&lt;li>刪掉某台機器會不會連帶弄壞別的東西？&lt;/li>
&lt;li>現在用了幾把長期 access key，每把用在哪裡？&lt;/li>
&lt;li>有沒有一份清單能對照 Console 上的資源，確認沒有漏掉的？&lt;/li>
&lt;/ol>
&lt;p>五題都能答的團隊不多，目標也不是一次全通。辨識出哪些區域不可見，按傷害代價從高到低逐一收斂，就是這一章的路線。&lt;/p>
&lt;h2 id="護欄先上在回退代價最高的操作">護欄先上在回退代價最高的操作&lt;/h2>
&lt;p>手動環境沒有 IaC 的 &lt;code>plan&lt;/code> / &lt;code>diff&lt;/code> 當預檢，人為操作直接生效。護欄的優先級看的是失誤的回退代價，不是操作頻率。回退代價最高的三類操作各自有最低成本的防線。&lt;/p>
&lt;h3 id="長期憑證外洩">長期憑證外洩&lt;/h3>
&lt;p>長期 access key 一旦外流，攻擊者拿到的是不會過期的權限。回退代價高的原因不只是撤銷這把 key 本身，而是要找出所有使用它的地方同步更換 — 而「所有使用它的地方」在手動環境裡幾乎沒有完整清單。一把用了半年的 key 可能已經被複製到 CI 環境變數、某個同事的測試腳本、一個早已被遺忘但還在跑的 cron job 裡。&lt;/p>
&lt;p>最低成本的護欄分三步。第一步是盤點：列出帳號裡所有長期 access key，記下建立時間、上次使用時間與對應用途。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">aws iam generate-credential-report
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">aws iam get-credential-report --output text --query Content &lt;span class="p">|&lt;/span> base64 -d&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>第二步是替換路徑。對人類操作者，改用會過期的登入工作階段（如 AWS IAM Identity Center 的臨時憑證，幾小時後自動失效）。對跑在雲上的自動化（EC2 上的腳本、ECS task），改用平台原生的角色綁定 — instance profile 或 task role 會自動輪替短期憑證，程式碼不需要存任何 key。對跑在雲外的 CI/CD（如 GitHub Actions），改用 OIDC 聯合（見&lt;a href="https://tarrragon.github.io/blog/infra/02-identity-credentials/" data-link-title="模組二：身分與憑證地基 — IAM 與 OIDC" data-link-desc="IAM role / policy 設計、最小權限，以及用 OIDC 短期憑證取代長期 access key">模組二：身分與憑證地基&lt;/a>）。&lt;/p>
&lt;p>第三步是輪替紀律。把還在用的長期 key 設定定期輪替提醒（60 天或 90 天，對齊 AWS IAM credential report 的建議週期），每次輪替時問自己：這把 key 能不能這次就換成臨時憑證，讓它成為最後一次輪替？&lt;/p>
&lt;h3 id="刪除-production-資源">刪除 production 資源&lt;/h3>
&lt;p>在 Console 選中一個 security group 按刪除，平台可能只問「確定嗎？」就直接執行，不會告訴你有三個 EC2 instance 正在引用這個 group。EBS volume 被刪除後，上面的資料就不存在了 — 除非之前有做 snapshot，而手動環境裡有沒有做 snapshot 通常取決於某個人的記憶。&lt;/p>
&lt;p>對承載狀態的資源，最低成本的護欄是開啟平台的刪除保護：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">aws rds modify-db-instance &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --db-instance-identifier payments-prod &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --deletion-protection &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --apply-immediately
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">aws ec2 modify-instance-attribute &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --instance-id i-0abc123 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-api-termination&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>RDS 有 &lt;code>deletion_protection&lt;/code>，EC2 有 &lt;code>termination_protection&lt;/code>，S3 bucket 可以開 MFA delete。這些機制把「一鍵刪除」變成「先關保護再刪除」兩步操作，擋不住蓄意刪除，但能擋住手滑跟批次操作的誤傷。&lt;/p>
&lt;p>刪除保護之外，備份是另一道防線。手動環境裡至少確認 RDS 的自動備份是開著的（預設保留 7 天），以及 S3 bucket 的 versioning 是開著的。S3 bucket 的 versioning 預設是關的，一個沒開 versioning 的 bucket，覆寫或刪除物件後就回不去了。&lt;/p>
&lt;h3 id="網路規則的大改">網路規則的大改&lt;/h3>
&lt;p>手動調整 VPC 路由、subnet 關聯的 route table、或 security group 的入站規則，影響範圍跨越多個服務，而且在手動環境裡沒有版本控制可以 diff 改了什麼。一條路由改錯，某些 private subnet 的服務可能瞬間失去出站能力。&lt;/p>
&lt;p>最低成本的護欄是「改之前先把現況存下來」：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">aws ec2 describe-security-groups &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --group-ids sg-0abc123 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --output json &amp;gt; sg-backup-&lt;span class="k">$(&lt;/span>date +%Y%m%d&lt;span class="k">)&lt;/span>.json&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>用 CLI 把當前的 security group 規則、route table 設定匯出一份 JSON。改完後如果出問題，這份 JSON 就是回退的依據。這不是自動回退 — 手動環境沒有那個能力 — 但至少讓「改回去」有個明確的目標狀態。網路地基的系統性設計在&lt;a href="https://tarrragon.github.io/blog/infra/03-network-foundation/" data-link-title="模組三：網路地基 — VPC 與分層" data-link-desc="VPC、public / private subnet 切分、route table、NAT、security group 設計">模組三：網路地基&lt;/a>展開。&lt;/p></description><content:encoded><![CDATA[<p>手動起家是絕大多數服務的常態起點。從一個人在 Console 點出第一台 EC2 驗證想法，到小團隊接手開始長出更多資源，環境會經歷一段「全部靠手動、沒有任何程式碼描述」的階段。這個階段在<a href="/blog/infra/00-infra-mindset/#%e6%88%90%e7%86%9f%e5%ba%a6%e9%9a%8e%e6%a2%af" data-link-title="模組零：infra 是什麼，為什麼 day 1 就要鋪地基" data-link-desc="基礎設施的責任邊界、成熟度階梯，以及地基為什麼總在環境爆炸時才被看見">成熟度階梯</a>（從全手動到全程式碼治理的五階分級）上屬於第零階，它的責任是把自己管理成「可控的手動」，而不是假裝已經納管。可控意味著三件事：高風險操作有護欄、關鍵變更有痕跡、現實長什麼樣有紀錄。做好這三件事，當下出事的成本降低，未來把資源 import 進 IaC 的成本也跟著降低。</p>
<h2 id="判讀自己是否可控">判讀自己是否可控</h2>
<p>可控的手動環境能在五分鐘內回答以下問題：</p>
<ol>
<li>production 有哪些對外開放的 port？</li>
<li>上週誰動過資料庫參數，動了什麼？</li>
<li>刪掉某台機器會不會連帶弄壞別的東西？</li>
<li>現在用了幾把長期 access key，每把用在哪裡？</li>
<li>有沒有一份清單能對照 Console 上的資源，確認沒有漏掉的？</li>
</ol>
<p>五題都能答的團隊不多，目標也不是一次全通。辨識出哪些區域不可見，按傷害代價從高到低逐一收斂，就是這一章的路線。</p>
<h2 id="護欄先上在回退代價最高的操作">護欄先上在回退代價最高的操作</h2>
<p>手動環境沒有 IaC 的 <code>plan</code> / <code>diff</code> 當預檢，人為操作直接生效。護欄的優先級看的是失誤的回退代價，不是操作頻率。回退代價最高的三類操作各自有最低成本的防線。</p>
<h3 id="長期憑證外洩">長期憑證外洩</h3>
<p>長期 access key 一旦外流，攻擊者拿到的是不會過期的權限。回退代價高的原因不只是撤銷這把 key 本身，而是要找出所有使用它的地方同步更換 — 而「所有使用它的地方」在手動環境裡幾乎沒有完整清單。一把用了半年的 key 可能已經被複製到 CI 環境變數、某個同事的測試腳本、一個早已被遺忘但還在跑的 cron job 裡。</p>
<p>最低成本的護欄分三步。第一步是盤點：列出帳號裡所有長期 access key，記下建立時間、上次使用時間與對應用途。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws iam generate-credential-report
</span></span><span class="line"><span class="ln">2</span><span class="cl">aws iam get-credential-report --output text --query Content <span class="p">|</span> base64 -d</span></span></code></pre></div><p>第二步是替換路徑。對人類操作者，改用會過期的登入工作階段（如 AWS IAM Identity Center 的臨時憑證，幾小時後自動失效）。對跑在雲上的自動化（EC2 上的腳本、ECS task），改用平台原生的角色綁定 — instance profile 或 task role 會自動輪替短期憑證，程式碼不需要存任何 key。對跑在雲外的 CI/CD（如 GitHub Actions），改用 OIDC 聯合（見<a href="/blog/infra/02-identity-credentials/" data-link-title="模組二：身分與憑證地基 — IAM 與 OIDC" data-link-desc="IAM role / policy 設計、最小權限，以及用 OIDC 短期憑證取代長期 access key">模組二：身分與憑證地基</a>）。</p>
<p>第三步是輪替紀律。把還在用的長期 key 設定定期輪替提醒（60 天或 90 天，對齊 AWS IAM credential report 的建議週期），每次輪替時問自己：這把 key 能不能這次就換成臨時憑證，讓它成為最後一次輪替？</p>
<h3 id="刪除-production-資源">刪除 production 資源</h3>
<p>在 Console 選中一個 security group 按刪除，平台可能只問「確定嗎？」就直接執行，不會告訴你有三個 EC2 instance 正在引用這個 group。EBS volume 被刪除後，上面的資料就不存在了 — 除非之前有做 snapshot，而手動環境裡有沒有做 snapshot 通常取決於某個人的記憶。</p>
<p>對承載狀態的資源，最低成本的護欄是開啟平台的刪除保護：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws rds modify-db-instance <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  --db-instance-identifier payments-prod <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  --deletion-protection <span class="se">\
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="se"></span>  --apply-immediately
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl">aws ec2 modify-instance-attribute <span class="se">\
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="se"></span>  --instance-id i-0abc123 <span class="se">\
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="se"></span>  --disable-api-termination</span></span></code></pre></div><p>RDS 有 <code>deletion_protection</code>，EC2 有 <code>termination_protection</code>，S3 bucket 可以開 MFA delete。這些機制把「一鍵刪除」變成「先關保護再刪除」兩步操作，擋不住蓄意刪除，但能擋住手滑跟批次操作的誤傷。</p>
<p>刪除保護之外，備份是另一道防線。手動環境裡至少確認 RDS 的自動備份是開著的（預設保留 7 天），以及 S3 bucket 的 versioning 是開著的。S3 bucket 的 versioning 預設是關的，一個沒開 versioning 的 bucket，覆寫或刪除物件後就回不去了。</p>
<h3 id="網路規則的大改">網路規則的大改</h3>
<p>手動調整 VPC 路由、subnet 關聯的 route table、或 security group 的入站規則，影響範圍跨越多個服務，而且在手動環境裡沒有版本控制可以 diff 改了什麼。一條路由改錯，某些 private subnet 的服務可能瞬間失去出站能力。</p>
<p>最低成本的護欄是「改之前先把現況存下來」：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws ec2 describe-security-groups <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  --group-ids sg-0abc123 <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  --output json &gt; sg-backup-<span class="k">$(</span>date +%Y%m%d<span class="k">)</span>.json</span></span></code></pre></div><p>用 CLI 把當前的 security group 規則、route table 設定匯出一份 JSON。改完後如果出問題，這份 JSON 就是回退的依據。這不是自動回退 — 手動環境沒有那個能力 — 但至少讓「改回去」有個明確的目標狀態。網路地基的系統性設計在<a href="/blog/infra/03-network-foundation/" data-link-title="模組三：網路地基 — VPC 與分層" data-link-desc="VPC、public / private subnet 切分、route table、NAT、security group 設計">模組三：網路地基</a>展開。</p>
<h3 id="該先做什麼">該先做什麼</h3>
<p>這三類護欄的共同判準是：護欄成本低（幾條 CLI 指令或 Console 設定）、失誤代價高（憑證外洩、資料遺失、服務中斷）。判讀某個資源該不該現在就加護欄，問自己一個問題：「這個資源出事的回退時間是分鐘級、小時級、還是不可回退？」不可回退的（資料刪除、key 外洩）優先加；分鐘級可回退的（重啟一個 stateless service）可以排後面。</p>
<h2 id="讓變更留下痕跡">讓變更留下痕跡</h2>
<p>變更留痕的責任是讓「誰、在什麼時候、改了什麼、為什麼」事後可追溯。IaC 的 git history 天然提供這件事，手動環境得靠人為紀律補上。</p>
<h3 id="人工變更日誌">人工變更日誌</h3>
<p>最低限度是一份變更日誌，可以只是 repo 裡的一個 markdown 檔或團隊共用文件。一條記錄至少包含四個欄位：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## 2026-06-20
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">-</span> **操作者**：alice
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> **資源**：sg-0abc123 (payments-api-prod)
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> **變更**：新增 ingress rule, port 8080 from 10.0.0.0/16
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> **原因**：內部監控服務需要存取 health check endpoint
</span></span><span class="line"><span class="ln">7</span><span class="cl">- <span class="gs">**回退方式**</span>：刪除該 ingress rule</span></span></code></pre></div><p>格式不需要精美，需要的是「每次都寫」。常見陷阱是只在「大改動」時才記錄，結果真正出事的往往是某次以為無關緊要的小調整 — 改了一個 parameter group 的值、調了一條路由的目標、把某個 instance 的 security group 換了一個。判準簡化成一句：只要這個操作別人事後可能需要知道，就記。</p>
<h3 id="平台稽核日誌">平台稽核日誌</h3>
<p>和人工日誌互補的是平台的稽核日誌（如 AWS CloudTrail、GCP Audit Log）。稽核日誌自動記錄 API 層級「發生了什麼」— 某個 IAM user 在某個時間對某個資源呼叫了哪個 API — 不依賴人為紀律、也不會漏。但它只記錄事實，不記錄意圖。它告訴你 security group 在幾點被改，卻不告訴你改的原因。人寫的變更日誌補上的正是「為什麼」這一段。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws cloudtrail describe-trails <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  --query <span class="s1">&#39;trailList[].{Name:Name,S3Bucket:S3BucketName}&#39;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">aws cloudtrail lookup-events <span class="se">\
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="se"></span>  --lookup-attributes <span class="nv">AttributeKey</span><span class="o">=</span>EventName,AttributeValue<span class="o">=</span>AuthorizeSecurityGroupIngress <span class="se">\
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="se"></span>  --max-items <span class="m">10</span></span></span></code></pre></div><p>CloudTrail 在 AWS 帳號裡預設開啟 management event 的 90 天查閱。手動環境裡至少確認 management event 的 trail 存在且在寫入 — 這是事後回推「到底誰動了什麼」的最後防線。兩者一起，事故排查時才能從「哪裡變了」一路追到「為什麼改、能不能安全回退」。</p>
<h2 id="命名與-tagging-從手動階段就開始">命名與 tagging 從手動階段就開始</h2>
<p>命名規範與資源標籤讓每個資源自帶「我是誰、屬於哪個服務、誰負責、哪個環境」的身分資訊。手動點出來的資源若名稱是 <code>test-2</code>、<code>new-db-final</code>、<code>temp-sg</code>，日後納管時得靠人逐一辨認哪個還在用、屬於哪條業務線，考古成本遠高於當初多打幾個字。</p>
<h3 id="命名規範">命名規範</h3>
<p>從手動階段就固定一套命名規則，讓名稱本身攜帶足夠的上下文。一個實用的格式是 <code>{service}-{component}-{env}</code>：</p>
<table>
  <thead>
      <tr>
          <th>資源類型</th>
          <th>命名範例</th>
          <th>攜帶的資訊</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>EC2 instance</td>
          <td><code>payments-api-prod</code></td>
          <td>服務 + 角色 + 環境</td>
      </tr>
      <tr>
          <td>Security group</td>
          <td><code>payments-api-prod-sg</code></td>
          <td>同上 + 資源類型</td>
      </tr>
      <tr>
          <td>RDS instance</td>
          <td><code>payments-db-prod</code></td>
          <td>服務 + 資源類型 + 環境</td>
      </tr>
      <tr>
          <td>S3 bucket</td>
          <td><code>acme-payments-assets-dev</code></td>
          <td>組織 + 服務 + 用途 + 環境</td>
      </tr>
  </tbody>
</table>
<p>命名不需要完美或涵蓋所有維度，需要的是一致。同類資源都用同一套格式，人眼掃一頁 Console 就能分辨「這個屬於 payments 的 prod」跟「這個屬於 auth 的 dev」。不一致的命名（有些用底線、有些用連字號、有些帶 env 有些不帶）會在日後盤點時讓每個資源都變成需要考古的謎題。</p>
<h3 id="最小-tag-集合">最小 tag 集合</h3>
<p>標籤至少包含三個維度：</p>
<table>
  <thead>
      <tr>
          <th>Tag</th>
          <th>問的問題</th>
          <th>典型值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>service</code></td>
          <td>這屬於誰</td>
          <td><code>payments-api</code> / <code>auth</code></td>
      </tr>
      <tr>
          <td><code>env</code></td>
          <td>哪個環境</td>
          <td><code>prod</code> / <code>staging</code> / <code>dev</code></td>
      </tr>
      <tr>
          <td><code>owner</code></td>
          <td>出事找誰</td>
          <td><code>team-payments</code> / <code>platform</code></td>
      </tr>
  </tbody>
</table>
<p>手動階段的 tag 靠人工填。在 Console 建資源時順手加 tag 幾乎零成本 — 多打三行字而已。但如果沒有約定「哪些 tag 是必填」，多數人會跳過。最低限度的紀律是：在團隊文件裡寫下「建任何資源前先填這三個 tag」，並在每次盤點時檢查有沒有漏標的資源。</p>
<p>這套規則在導入 IaC 後直接升級成 Terraform 的 <code>default_tags</code> — 自動套用、不靠人記（見<a href="/blog/infra/08-governance-habits/" data-link-title="模組八：治理好習慣 — 規模長大後不失控的最小節奏" data-link-desc="tagging 規範、secrets 不進 code、成本可見性、最小可行節奏，規模長大後不失控">模組八：治理好習慣</a>）。先在手動階段建立習慣，導入 IaC 時只是換一個強制機制，而不是從零學起一套分類法。</p>
<h2 id="盤點現有資源作為納管輸入">盤點現有資源作為納管輸入</h2>
<p>資源盤點把「現實長什麼樣」寫成一份清單，它是日後納管的直接輸入。接手別人建的環境時，盤點的範圍和方法更完整的版本見<a href="/blog/infra/takeover/" data-link-title="接手維運：別人建的環境怎麼接管" data-link-desc="接手前人的專案時，怎麼在不搞壞東西的前提下盤點現況、建立維運能力、逐步正規化 — 從無 SSH 的 FTP 環境到有半套 IaC 的雲端環境都適用">接手維運模組</a>。手動環境裡最難管理的是未標記的閒置資源 — 測試用的 EC2、實驗用的 RDS — 持續計費但沒有標籤，無法用查詢系統性找出，也無法確認是否仍有服務依賴。</p>
<h3 id="盤點方法">盤點方法</h3>
<p>按資源類型分批拉，每批存一份 JSON 或 CSV 進 repo：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">aws ec2 describe-instances <span class="se">\
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="se"></span>  --query <span class="s1">&#39;Reservations[].Instances[].[InstanceId,InstanceType,State.Name,Tags[?Key==`Name`].Value|[0],Tags[?Key==`env`].Value|[0]]&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="se"></span>  --output table
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">aws rds describe-db-instances <span class="se">\
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="se"></span>  --query <span class="s1">&#39;DBInstances[].[DBInstanceIdentifier,Engine,DBInstanceClass,MultiAZ,DeletionProtection]&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="se"></span>  --output table
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">aws ec2 describe-security-groups <span class="se">\
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="se"></span>  --query <span class="s1">&#39;SecurityGroups[].[GroupId,GroupName,IpPermissions]&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="se"></span>  --output json &gt; security-groups-<span class="k">$(</span>date +%Y%m%d<span class="k">)</span>.json
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">aws s3api list-buckets --query <span class="s1">&#39;Buckets[].Name&#39;</span></span></span></code></pre></div><h3 id="盤點後的三件事">盤點後的三件事</h3>
<p>這份清單同時服務三個目的。</p>
<p><strong>當下的安全盤查</strong>：security group 清單裡有沒有不該開的對外 port？有沒有 EC2 直接掛著公網 IP 卻不是 load balancer？用 <code>0.0.0.0/0</code> 搜一遍 security group 的輸出，命中的每一條都要能說出「這個全開是故意的、理由是什麼」。</p>
<p><strong>未來 IaC import 的範圍界定</strong>：哪些資源該先 import。判準是「改動頻率」與「改錯代價」的乘積 — 頻繁改動且改錯代價高的（security group、IAM role）先排進來，很少動的（一個已經穩定的 S3 bucket）可以排後面。</p>
<p><strong>成熟度評估的事實基礎</strong>：成熟度階梯的定位（見<a href="/blog/infra/00-infra-mindset/" data-link-title="模組零：infra 是什麼，為什麼 day 1 就要鋪地基" data-link-desc="基礎設施的責任邊界、成熟度階梯，以及地基為什麼總在環境爆炸時才被看見">模組零：infra 是什麼</a>）需要知道「全手動到底有多少資源、分布在幾個帳號、跨幾個 region」，這份清單就是評估的輸入。</p>
<h3 id="盤點的節奏">盤點的節奏</h3>
<p>第一次盤點最花時間，因為很多資源的用途需要考古。之後每月或每季重跑一次比對差異 — 重點是看「上次到這次之間長出了什麼新資源」。如果每次比對都發現大量未標記的新資源，這本身就是一個訊號：手動操作的可見性不足，該考慮導入 IaC 了。</p>
<h2 id="資源與信任不足下的高槓桿取捨">資源與信任不足下的高槓桿取捨</h2>
<p>當時間、人力或上層信任都不足，無法一次把上面每件事做齊時，取捨原則是先做「失誤代價高且護欄成本低」的少數幾件：</p>
<table>
  <thead>
      <tr>
          <th>護欄</th>
          <th>實施成本</th>
          <th>失誤代價</th>
          <th>優先級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>長期 key 盤點</td>
          <td>低</td>
          <td>極高</td>
          <td>立刻做</td>
      </tr>
      <tr>
          <td>刪除保護</td>
          <td>低</td>
          <td>極高</td>
          <td>立刻做</td>
      </tr>
      <tr>
          <td>變更日誌</td>
          <td>低</td>
          <td>中</td>
          <td>第二順位</td>
      </tr>
      <tr>
          <td>命名規範</td>
          <td>近零</td>
          <td>累積</td>
          <td>新資源立刻套用</td>
      </tr>
      <tr>
          <td>資源盤點</td>
          <td>中</td>
          <td>累積</td>
          <td>有空就做</td>
      </tr>
      <tr>
          <td>存量重命名</td>
          <td>高</td>
          <td>累積</td>
          <td>等有餘力</td>
      </tr>
  </tbody>
</table>
<p>長期憑證盤點與刪除保護兩者加起來的實施時間可能不到一小時。命名與 tagging 的策略是「新的一律照規範、舊的等有餘力再補」，而不是停下來先整理全部存量。資源不足時怎麼跟上層談這些工作的優先級，在<a href="/blog/infra/09-driving-adoption/" data-link-title="模組九：怎麼把 infra 推動起來" data-link-desc="技術正確不等於推得動 — 信任赤字、期望值對齊、知識共享，infra 落地的組織課題">模組九：怎麼把 infra 推動起來</a>展開。</p>
<h2 id="該開始導入-iac-的訊號">該開始導入 IaC 的訊號</h2>
<p>手動環境到了某些訊號出現時，繼續手動的邊際成本會超過導入 IaC 的一次性成本。訊號是規模與協作的函數，不是時間的函數 — 一個人運維一個簡單服務，手動可能撐很久；三個人同時動一個稍微複雜的環境，幾週內就會踩到手動的極限。</p>
<p><strong>環境數量變多</strong>：當需要 dev、staging、production 三套幾乎一樣的環境，手動複製會在環境之間留下難以察覺的差異。某個人在 staging 加了一條 security group 規則，忘了在 prod 也加，結果 staging 測通了、prod 部署後服務連不上。IaC 用同一份程式碼複製環境，環境差異只存在於參數值。</p>
<p><strong>多人同時動資源</strong>：一個人手動操作還能靠記憶維護，兩三個人並行時，沒有 plan / review 的手動變更會互相覆蓋。A 改了一個設定解了自己的問題，B 幾天後改了另一個設定把 A 的修正覆蓋掉，事故原因得靠翻 CloudTrail 才查得到。</p>
<p><strong>環境爆炸頻率上升</strong>：如果「改一個設定結果弄壞別的東西」這類事故開始每月發生，代表手動環境的隱性依賴已經超過人腦能追蹤的上限。一個典型的隱性依賴：security group A 被 instance X 和 instance Y 同時引用，改 A 時只想著 X 的需求、忘了 Y 也依賴它，改完 Y 就斷了。</p>
<p><strong>合規或稽核要求</strong>：外部稽核（SOC 2、ISO 27001）開始要求「列出所有對外暴露的服務」「提供存取權限的變更紀錄」「證明 production 環境的變更有經過審查」。手動環境回答這些問題時，每次都是一場考古工程。IaC 加上 PR 流程後，答案就在 repo 裡。</p>
<p>任一訊號穩定出現，就是把第一個資源納入 IaC 的起點 — 前面做的命名、tagging、資源盤點此時直接成為 import 的輸入。第一步怎麼跨進去在<a href="/blog/infra/01-minimal-iac/" data-link-title="模組一：最小可行 IaC — state 地基與 Console 唯讀鐵律" data-link-desc="Terraform / OpenTofu 選型、remote state 與 lock，以及「Console 只能看不能改」鐵律">模組一：最小可行 IaC</a>。</p>
<p>在訊號出現前過早導入 IaC 也有代價：單人、單環境、低變更頻率時，IaC 的學習與維護成本可能高於它省下的手動工 — 寫一份 HCL、配一個 state backend、設一條 pipeline 的固定成本，在只有三個資源的環境裡不一定划得來。這裡的判準是等訊號、不是趕進度。</p>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/infra/takeover/" data-link-title="接手維運：別人建的環境怎麼接管" data-link-desc="接手前人的專案時，怎麼在不搞壞東西的前提下盤點現況、建立維運能力、逐步正規化 — 從無 SSH 的 FTP 環境到有半套 IaC 的雲端環境都適用">接手維運</a>：如果這個手動環境是接手來的，先走接手維運的盤點流程</li>
<li>→ <a href="/blog/infra/00-infra-mindset/" data-link-title="模組零：infra 是什麼，為什麼 day 1 就要鋪地基" data-link-desc="基礎設施的責任邊界、成熟度階梯，以及地基為什麼總在環境爆炸時才被看見">模組零：infra 是什麼</a>：成熟度階梯上「全手動」這一階的定位</li>
<li>→ <a href="/blog/infra/01-minimal-iac/" data-link-title="模組一：最小可行 IaC — state 地基與 Console 唯讀鐵律" data-link-desc="Terraform / OpenTofu 選型、remote state 與 lock，以及「Console 只能看不能改」鐵律">模組一：最小可行 IaC</a>：訊號出現後，第一步怎麼跨進 IaC</li>
<li>→ <a href="/blog/infra/02-identity-credentials/" data-link-title="模組二：身分與憑證地基 — IAM 與 OIDC" data-link-desc="IAM role / policy 設計、最小權限，以及用 OIDC 短期憑證取代長期 access key">模組二：身分與憑證地基</a>：長期憑證護欄的系統性設計</li>
<li>→ <a href="/blog/infra/03-network-foundation/" data-link-title="模組三：網路地基 — VPC 與分層" data-link-desc="VPC、public / private subnet 切分、route table、NAT、security group 設計">模組三：網路地基</a>：手動階段網路大改的回退考量、之後的系統性設計</li>
<li>→ <a href="/blog/infra/08-governance-habits/" data-link-title="模組八：治理好習慣 — 規模長大後不失控的最小節奏" data-link-desc="tagging 規範、secrets 不進 code、成本可見性、最小可行節奏，規模長大後不失控">模組八：治理好習慣</a>：tagging 在成本歸因與批次操作的後續價值</li>
<li>→ <a href="/blog/infra/09-driving-adoption/" data-link-title="模組九：怎麼把 infra 推動起來" data-link-desc="技術正確不等於推得動 — 信任赤字、期望值對齊、知識共享，infra 落地的組織課題">模組九：怎麼把 infra 推動起來</a>：資源不足時怎麼跟上層談優先級</li>
</ul>
]]></content:encoded></item></channel></rss>