mcpField
I
import-infrastructure-as-code

import-infrastructure-as-code

github.com/github云与 DevOps40.5k6.9k 次安装1 天前更新
代码仓库

关于 import-infrastructure-as-code

此 Skill 将发现的 Azure 基础设施转换为基于 Azure Verified Modules 的 Terraform 配置,面向云工程师,涵盖资源发现、依赖梳理、模块选择和差异检查。

主要功能

  1. 01按订阅、资源组或 ID 发现 Azure 资源
  2. 02梳理依赖并生成架构文档
  3. 03选择并检查 Azure Verified Modules
  4. 04生成 Terraform 配置和导入块
  5. 05将实时属性与模块默认值比较

适用场景

  1. 01将现有 Azure 环境重建为 Terraform
  2. 02记录已发现 Azure 资源之间的关系
  3. 03导入资源并优先采用 Azure Verified Modules
  4. 04检查生成的 Terraform 是否存在配置漂移

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/github/awesome-copilot --skill import-infrastructure-as-code

安装后在 Claude Code 中输入/import-infrastructure-as-code确认已加载。

详细介绍

导入基础设施即代码(Azure -> 使用 AVM 的 Terraform)

利用发现数据和 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/...)视为云资源标识符,而非本地文件系统路径。
  • 仅在 Azure CLI 的 --ids 参数中使用资源 ID(例如 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。

# Subscription scope
az resource list --subscription <subscription-id> -o json

# Resource group scope
az resource list --resource-group <resource-group-name> -o json

# Specific resource scope
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 索引

注意: 以下链接始终指向 main 分支上 CSV 文件的最新版本。正如预期的那样,这意味着这些文件可能会随时间变化。如果你需要某个时间点固定的版本,请考虑在 URL 中使用特定的发布标签。

  • Terraform 资源模块:https://raw.githubusercontent.com/Azure/Azure-Verified-Modules/refs/heads/main/docs/static/module-indexes/TerraformResourceModules.csv
  • Terraform 模式模块:https://raw.githubusercontent.com/Azure/Azure-Verified-Modules/refs/heads/main/docs/static/module-indexes/TerraformPatternModules.csv
  • Terraform 实用工具模块: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--/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 中,在编写代码之前提取并记录:

  1. 必需输入 — 模块所需的每一个输入。这里列出的任何子资源 (NIC、扩展、子网、公共 IP)都由模块内部管理。不要 为这些资源创建独立的模块块。
  2. 可选输入 — 确切的 Terraform 变量名称及其声明的 type。 不要假设它们与原始 azurerm provider 的参数名称或块形状相匹配。
  3. 用法示例 — 检查使用了哪个资源组标识符(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 变量。
  • 扩展通过 extensions 映射在模块内部管理。不要创建 独立的扩展资源。

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 文件
编写导入块之前 —— 检查模块源代码(强制)

在 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 支持的 VNetmodule.<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 NICmodule.<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,包含 azurerm provider 及所需的版本约束
  • 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 提供程序参数名不同阅读模块 README 的 variables.tf 或 Optional Inputs 部分以获取正确的名称
导入块失败 — 在地址处找不到资源提供程序标签错误(azurerm_ 与 azapi_)、缺少子模块路径或缺少 [0] 索引运行 grep "^resource" .terraform/modules/<key>/main*.tf 和 grep "^module" 以查找准确的地址
terraform plan 显示对已导入资源出现意外的 ~ update实际值与 AVM 模块默认值不同使用 az <resource> show 获取实际属性,与模块默认值比较,添加显式值
子资源模块给出 "provider configuration not present"子资源被声明为独立模块,尽管父模块拥有它们检查 README 中的 Required Inputs,移除错误的独立模块,并使用父模块记录的输入结构来建模子资源
嵌套子资源导入失败并提示 "resource not found"缺少中间模块路径、映射键错误或缺少索引检查源码中的模块块以及 count/for_each;构建包含所有模块段和所需键/索引的完整嵌套导入地址
工具尝试将 ARM 资源 ID 读取为文件路径,或反复询问范围问题资源 ID 未被当作 --ids 输入处理,或 Agent 未信任已提供的范围严格将 ARM ID 视为云标识符,使用 az ... --ids ...,并在出现一个有效范围后停止重复提示

响应约定

返回结果时,请提供:

  1. 使用的范围(订阅、资源组或资源 ID)
  2. 创建的发现文件
  3. 检测到的资源类型
  4. 选定的 AVM 模块及其版本
  5. 生成或更新的 Terraform 文件
  6. 验证命令结果
  7. 需要用户输入的未解决缺口(如有)

Agent 的执行规则

  • 如果缺少范围,则不要继续。
  • 在没有列出已发现文件和验证输出的情况下,不得声称导入成功。
  • 在生成 Terraform 之前,不得跳过依赖关系映射。
  • 优先使用 AVM 模块;对每个非 AVM 回退方案明确说明理由。
  • 在编写代码之前,阅读每个 AVM 模块的 README。 必需输入(Required Inputs)指明 该模块拥有哪些子资源。可选输入(Optional Inputs)记录确切的变量名称和 类型。用法示例展示特定于提供程序的约定(parent_id 与 resource_group_name)。跳过 README 是基于 AVM 的导入中出现 代码错误的最常见原因。
  • 绝不要假设 NIC、扩展或公共 IP 资源是独立的。 对于 任何 AVM 模块,除非 README 明确表明 需要单独的模块,否则将子资源视为父级拥有。在创建同级模块之前,检查必需输入(Required Inputs)。
  • 绝不要凭记忆编写导入地址。 在 terraform init 之后,grep 已下载的 模块源码,以在编写任何 import {} 块之前发现实际的提供程序(azurerm 与 azapi)、资源标签、 子模块嵌套以及 count 与 for_each 的用法。
  • 绝不要把 ARM 资源 ID 当作文件路径。 资源 ID 属于 Azure CLI --ids 参数和 API 查询,而不是文件 IO 工具。仅当提供了真实的工作区 路径时,才读取本地文件。
  • 当范围已知时,尽量减少提示。 如果已提供订阅、资源组或 特定资源 ID,则直接执行命令,仅当命令因缺少必需上下文而失败时 才提出后续问题。
  • 在 terraform plan 显示 0 个销毁和 0 个 非预期更改之前,不要宣布导入完成。 遥测 + create 资源是可以接受的。对真实基础设施资源的任何 ~ update 或 - destroy 都必须解决。

参考资料

评论

支持 Markdown