使用 Azure CLI 探索與 Azure Verified Modules (AVM),將現有 Azure 資源匯入 Terraform。當需要逆向工程現有 Azure 基礎架構、從現有訂閱/資源群組/資源 ID 產生基礎架構即程式碼、對應相依性、從下載的模組原始碼推導精確匯入位址、防止配置漂移,以及產出可供驗證與規劃的 AVM 型 Terraform 檔案時使用。
將基礎架構匯入為程式碼 (Azure -> Terraform with AVM)
使用探索資料與 Azure Verified Modules,將現有 Azure 基礎架構轉換為可維護的 Terraform 程式碼。
何時使用此技能
當使用者要求以下事項時使用此技能:
- 將現有 Azure 資源匯入 Terraform
- 從現有 Azure 環境產生 IaC
- 處理 AVM 支援的所有 Azure 資源類型(並記錄合理的非 AVM 替代方案)
- 從訂閱或資源群組重建基礎架構
- 對應已探索 Azure 資源之間的相依性
- 使用 AVM 模組而非手寫的
azurerm_*資源
先決條件
- 已安裝 Azure CLI 並通過驗證 (
az login) - 可存取目標訂閱或資源群組
- 已安裝 Terraform CLI
- 可連線至 Terraform Registry 與 AVM 索引來源
輸入參數
| 參數 | 必要 | 預設值 | 說明 |
|---|---|---|---|
subscription-id |
否 | 使用中 CLI 內容 | 用於訂閱範圍探索與內容設定的 Azure 訂閱 |
resource-group-name |
否 | 無 | 用於資源群組範圍探索的 Azure 資源群組 |
resource-id |
否 | 無 | 用於特定資源範圍探索的一或多個 Azure ARM 資源 ID |
至少需要提供 subscription-id、resource-group-name 或 resource-id 其中之一。
逐步工作流程
1) 收集必要範圍(強制)
在執行探索指令前,要求提供以下其中一個範圍:
- 訂閱範圍:
<subscription-id> - 資源群組範圍:
<resource-group-name> - 特定資源範圍:一或多個
<resource-id>值
範圍處理規則:
- 將 Azure ARM 資源 ID(例如
/subscriptions/.../providers/...)視為雲端資源識別碼,而非本機檔案系統路徑。 - 僅將資源 ID 用於 Azure CLI 的
--ids參數(例如az resource show --ids <resource-id>)。 - 除非使用者明確表示為本機檔案路徑,否則切勿將資源 ID 傳遞給檔案讀取指令(
cat、ls、read_file、glob 搜尋)。 - 若使用者已提供一個有效的範圍,除非指令因缺少必要資訊而失敗,否則不要要求額外的範圍輸入。
- 不要詢問可從已提供範圍值回答的後續問題。
若缺少範圍,請明確要求並停止。
2) 驗證並設定內容
僅執行所選範圍所需的指令。
對於訂閱範圍:
az login
az account set --subscription <subscription-id>
az account show --query "{subscriptionId:id, name:name, tenantId:tenantId}" -o json
預期輸出:包含 subscriptionId、name 和 tenantId 的 JSON 物件。
對於資源群組或特定資源範圍,仍需要 az login,但如果使用中內容已正確,則 az account set 為選用。
使用特定資源範圍時,優先使用直接基於 --ids 的指令,除非具體指令需要,否則避免額外的訂閱或資源群組探索提示。
3) 執行探索指令
使用所選範圍探索資源。確保取得所有必要資訊以準確產生 Terraform。
# 訂閱範圍
az resource list --subscription <subscription-id> -o json
# 資源群組範圍
az resource list --resource-group <resource-group-name> -o json
# 特定資源範圍
az resource show --ids <resource-id-1> <resource-id-2> ... -o json
預期輸出:包含 Azure 資源中繼資料(id、type、name、location、tags、properties)的 JSON 物件或陣列。
4) 在產生程式碼前解析相依性
解析匯出的 JSON 並對應:
- 父子關係(例如:NIC -> Subnet -> VNet)
properties中的跨資源參考- Terraform 建立的順序
重要:產生以下文件並儲存至專案根目錄的 docs 資料夾。
exported-resources.json:包含所有探索到的資源及其中繼資料,包括相依性和參考。EXPORTED-ARCHITECTURE.MD:根據探索到的資源及其關係,提供人類可讀的架構概覽。
5) 選擇 Azure Verified Modules(必要)
對每種資源類型使用最新的 AVM 版本。
Terraform Registry
- 搜尋 "avm" + 資源名稱
- 透過 "Partner" 標籤篩選以找到官方 AVM 模組
- 範例:搜尋 "avm storage account" → 篩選 Partner
官方 AVM 索引
注意: 以下連結始終指向主分支上最新版本的 CSV 檔案。這意味著檔案可能會隨時間變更。如果您需要特定時間點的版本,請考慮在 URL 中使用特定的發行標籤。
- Terraform Resource Modules:
https://raw.githubusercontent.com/Azure/Azure-Verified-Modules/refs/heads/main/docs/static/module-indexes/TerraformResourceModules.csv - Terraform Pattern Modules:
https://raw.githubusercontent.com/Azure/Azure-Verified-Modules/refs/heads/main/docs/static/module-indexes/TerraformPatternModules.csv - Terraform Utility Modules:
https://raw.githubusercontent.com/Azure/Azure-Verified-Modules/refs/heads/main/docs/static/module-indexes/TerraformUtilityModules.csv
個別模組資訊
如果 .terraform 資料夾中沒有本機可用的模組資訊,請使用 web 工具或其他合適的 MCP 方法取得模組資訊。
使用 AVM 來源:
- Registry:
https://registry.terraform.io/modules/Azure/<module>/azurerm/latest - GitHub:
https://github.com/Azure/terraform-azurerm-avm-res-<service>-<resource>
當 AVM 模組存在時,優先使用 AVM 模組而非手寫的 azurerm_* 資源。
從 GitHub 儲存庫取得模組資訊時,儲存庫根目錄中的 README.md 檔案通常包含模組的所有詳細資訊,例如:https://raw.githubusercontent.com/Azure/terraform-azurerm-avm-res-<service>-<resource>/refs/heads/main/README.md
5a) 在撰寫任何程式碼前閱讀模組 README(強制)
此步驟不可省略。 在為模組撰寫任何一行 HCL 之前,請先取得並閱讀該模組的完整 README。不要依賴對原始 azurerm provider 的知識或先前使用其他 AVM 模組的經驗。
對於每個選定的 AVM 模組,取得其 README:
https://raw.githubusercontent.com/Azure/terraform-azurerm-avm-res-<service>-<resource>/refs/heads/main/README.md
或者,如果模組已在 terraform init 後下載:
cat .terraform/modules/<module_key>/README.md
從 README 中,在撰寫程式碼前提取並記錄以下內容:
- 必要輸入 — 模組所需的每個輸入。此處列出的任何子資源(NIC、擴充功能、子網路、公用 IP)均由模組內部管理。請不要為這些資源建立獨立的模組區塊。
- 選用輸入 — 確切的 Terraform 變數名稱及其宣告的
type。不要假設它們與原始azurermprovider 的參數名稱或區塊形狀相符。 - 使用範例 — 檢查使用的資源群組識別碼(
parent_id與resource_group_name)、子資源的表達方式(內聯 map 與獨立模組),以及每個輸入預期的語法。
將模組規則視為模式,而非假設
使用以下教訓作為導致匯入失敗的常見不匹配類型的範例。不要假設這些確切名稱適用於每個 AVM 模組。務必驗證每個選定模組的 README 和 variables.tf。
avm-res-compute-virtualmachine(任何版本)
network_interfaces是必要輸入。NIC 由 VM 模組擁有。切勿在 VM 模組旁建立獨立的avm-res-network-networkinterface模組 — 在network_interfaces下內聯定義每個 NIC。- TrustedLaunch 透過頂層布林值
secure_boot_enabled = true和vtpm_enabled = true表達。security_type參數僅存在於os_disk下,用於機密 VM 磁碟加密,不得用於 TrustedLaunch。 boot_diagnostics是bool,而非物件。使用boot_diagnostics = true;如果需要儲存體 URI,請使用單獨的boot_diagnostics_storage_account_uri變數。- 擴充功能透過
extensionsmap 在模組內部管理。請勿建立獨立的擴充功能資源。
avm-res-network-virtualnetwork(任何版本)
- 此模組由 AzAPI provider 支援,而非
azurerm。使用parent_id(完整的資源群組資源 ID 字串)來指定資源群組,而非resource_group_name。 - README 中的每個範例都顯示
parent_id;沒有範例顯示resource_group_name。
所有 AVM 模組的通用結論:
- 在建立同級模組之前,從必要輸入確定子資源的歸屬。
- 從選用輸入和
variables.tf確定接受的變數名稱和類型。 - 從 README 使用範例確定識別碼樣式和輸入形狀。
- 不要從原始
azurerm_*資源推斷參數名稱。
6) 產生 Terraform 檔案
在撰寫 Import 區塊前 — 檢查模組原始碼(強制)
在 terraform init 下載模組後,在撰寫任何 import {} 區塊前,檢查每個模組的原始碼以確定確切的 Terraform 資源位址。切勿憑記憶撰寫匯入位址。
步驟 A — 識別 provider 和資源標籤
grep "^resource" .terraform/modules/<module_key>/main*.tf
這會顯示模組使用的是 azurerm_* 還是 azapi_resource 標籤。例如,avm-res-network-virtualnetwork 暴露的是 azapi_resource "vnet",而非 azurerm_virtual_network "this"。
步驟 B — 識別子模組和巢狀路徑
grep "^module" .terraform/modules/<module_key>/main*.tf
如果子資源在子模組中管理(子網路、擴充功能等),匯入位址必須包含每個中間模組標籤:
module.<root_module_key>.module.<child_module_key>["<map_key>"].<resource_type>.<label>[<index>]
步驟 C — 檢查 count 與 for_each
grep -n "count\|for_each" .terraform/modules/<module_key>/main*.tf
任何使用 count 的資源都需要在匯入位址中包含索引。當 count = 1(例如,條件式 Linux 與 Windows 選擇)時,位址必須以 [0] 結尾。使用 for_each 的資源使用字串鍵,而非數字索引。
已知匯入位址模式(來自經驗教訓的範例)
這些僅為範例。將它們用作推理的模板,然後從目前匯入中模組的下載原始碼推導確切位址。
| 資源 | 正確的匯入 to 位址模式 |
|---|---|
| AzAPI 支援的 VNet | module.<vnet_key>.azapi_resource.vnet |
| 子網路(巢狀,基於 count) | module.<vnet_key>.module.subnet["<subnet_name>"].azapi_resource.subnet[0] |
| Linux VM(基於 count) | module.<vm_key>.azurerm_linux_virtual_machine.this[0] |
| VM NIC | module.<vm_key>.azurerm_network_interface.virtualmachine_network_interfaces["<nic_key>"] |
| VM 擴充功能(預設 deploy_sequence=5) | module.<vm_key>.module.extension["<ext_name>"].azurerm_virtual_machine_extension.this |
| VM 擴充功能(deploy_sequence=1–4) | module.<vm_key>.module.extension_<n>["<ext_name>"].azurerm_virtual_machine_extension.this |
| NSG-NIC 關聯 | module.<vm_key>.azurerm_network_interface_security_group_association.this["<nic_key>-<nsg_key>"] |
產出:
providers.tf:包含azurermprovider 和必要的版本限制main.tf:包含 AVM 模組區塊和明確的相依性variables.tf:用於環境特定值outputs.tf:用於金鑰 ID 和端點terraform.tfvars.example:包含佔位符值
比較即時屬性與模組預設值(強制)
在撰寫初始配置後,將每個探索到的即時資源的每個非零屬性與對應 AVM 模組 variables.tf 中宣告的預設值進行比較。任何即時值與模組預設值不同的屬性都必須在 Terraform 配置中明確設定。
特別注意以下屬性類別,這些是無聲配置漂移的常見來源:
- 逾時值(例如,公用 IP
idle_timeout_in_minutes預設為4;實際部署通常使用30) - 網路原則旗標(例如,子網路
private_endpoint_network_policies預設為"Enabled";現有子網路通常為"Disabled") - SKU 與配置(例如,公用 IP
sku、allocation_method) - 可用區域(例如,VM 區域、公用 IP 區域)
- 儲存體與資料庫資源的備援與複寫設定
使用明確的 az 指令取得完整的即時屬性,例如:
az network public-ip show --ids <resource_id> --query "{idleTimeout:idleTimeoutInMinutes, sku:sku.name, zones:zones}" -o json
az network vnet subnet show --ids <resource_id> --query "{privateEndpointPolicies:privateEndpointNetworkPolicies, delegation:delegations}" -o json
不要僅依賴 az resource list 的輸出,因為它可能省略巢狀或計算屬性。
明確鎖定模組版本:
module "example" {
source = "Azure/<module>/azurerm"
version = "<latest-compatible-version>"
}
7) 驗證產生的程式碼
執行:
terraform init
terraform fmt -recursive
terraform validate
terraform plan
預期輸出:無語法錯誤、無驗證錯誤,且計劃與探索到的基礎架構意圖相符。
疑難排解
| 問題 | 可能原因 | 處理方式 |
|---|---|---|
az 指令因授權錯誤失敗 |
錯誤的租用戶/訂閱或缺少 RBAC 角色 | 重新執行 az login,驗證訂閱內容,確認所需權限 |
| 探索輸出為空 | 範圍不正確或範圍內無資源 | 重新檢查範圍輸入,再次執行範圍 list/show 指令 |
| 找不到資源類型的 AVM 模組 | 資源類型尚未被 AVM 涵蓋 | 對該類型使用原生 azurerm_* 資源,並記錄此缺口 |
terraform validate 失敗 |
缺少變數或未解析的相依性 | 新增必要的變數和明確的相依性,然後重新執行驗證 |
| 未知的參數或模組中找不到變數 | AVM 變數名稱與 azurerm provider 參數名稱不同 |
閱讀模組 README 的 variables.tf 或選用輸入區段以取得正確名稱 |
| Import 區塊失敗 — 在位址找不到資源 | 錯誤的 provider 標籤(azurerm_ 與 azapi_)、缺少子模組路徑或缺少 [0] 索引 |
執行 grep "^resource" .terraform/modules/<key>/main*.tf 和 grep "^module" 以找到確切位址 |
terraform plan 顯示已匯入資源有非預期的 ~ update |
即時值與 AVM 模組預設值不同 | 使用 az <resource> show 取得即時屬性,與模組預設值比較,新增明確值 |
| 子資源模組出現 "provider configuration not present" | 子資源被宣告為獨立模組,但父模組擁有它們 | 檢查 README 中的必要輸入,移除不正確的獨立模組,並使用父模組記錄的輸入結構來建模子資源 |
| 巢狀子資源匯入失敗,出現 "resource not found" | 缺少中間模組路徑、錯誤的 map 鍵或缺少索引 | 檢查原始碼中的模組區塊和 count/for_each;建立完整的巢狀匯入位址,包含所有模組區段和必要的鍵/索引 |
| 工具嘗試將 ARM 資源 ID 讀取為檔案路徑,或重複詢問範圍問題 | 資源 ID 未被視為 --ids 輸入,或代理未信任已提供的範圍 |
嚴格將 ARM ID 視為雲端識別碼,使用 az ... --ids ...,一旦存在一個有效範圍就停止重新提示 |
回應合約
回傳結果時,提供:
- 使用的範圍(訂閱、資源群組或資源 ID)
- 建立的探索檔案
- 偵測到的資源類型
- 選取的 AVM 模組及其版本
- 產生或更新的 Terraform 檔案
- 驗證指令結果
- 需要使用者輸入的開放缺口(若有)
代理執行規則
- 若缺少範圍,請勿繼續。
- 未列出探索到的檔案和驗證輸出前,請勿聲稱匯入成功。
- 在產生 Terraform 前,請勿跳過相依性對應。
- 優先使用 AVM 模組;明確說明每個非 AVM 替代方案的理由。
- 在撰寫程式碼前,閱讀每個 AVM 模組的 README。 必要輸入可識別模組擁有的子資源。選用輸入記錄了確切的變數名稱和類型。使用範例顯示 provider 特定的慣例(
parent_id與resource_group_name)。跳過 README 是 AVM 匯入中最常見的程式碼錯誤原因。 - 切勿假設 NIC、擴充功能或公用 IP 資源是獨立的。 對於任何 AVM 模組,除非 README 明確指出需要獨立模組,否則將子資源視為父模組擁有。在建立同級模組前檢查必要輸入。
- 切勿憑記憶撰寫匯入位址。 在
terraform init後,使用 grep 檢查下載的模組原始碼,以發現實際的 provider(azurerm與azapi)、資源標籤、子模組巢狀結構以及count與for_each的使用情況,然後再撰寫任何import {}區塊。 - 切勿將 ARM 資源 ID 視為檔案路徑。 資源 ID 應放在 Azure CLI 的
--ids參數和 API 查詢中,而非檔案 IO 工具。僅在提供實際工作區路徑時才讀取本機檔案。 - 當範圍已知時,最小化提示。 如果已提供訂閱、資源群組或特定資源 ID,請直接執行指令,僅在指令因缺少必要內容而失敗時才提出後續問題。
- 在
terraform plan顯示 0 個銷毀和 0 個非預期變更之前,請勿宣告匯入完成。 可接受+ create資源。任何對實際基礎架構資源的~ update或- destroy都必須解決。






