Skill: sdk-team/alibabacloud-terraform-import (version 0.0.1-beta.1)

--- name: alibabacloud-terraform-import description: This skill should be used when the user asks to "导入阿里云资源到 Terraform", "terraform import 阿里云", "将现有云资源纳入 Terraform 管理", "阿里云资源迁移 Terraform", "生成 terraform state", "import alicloud resources", "阿里云 IaC 迁移", "阿里云 Terraform 导入", or needs to manage existing Alibaba Cloud resources with Terraform. Guides users step-by-step through environment check, authentication, resource discovery, HCL generation, state import, validation, and dependency graph. Supports both one-time migration and incremental sync. version: 0.1.0 --- # 阿里云资源 Terraform 导入向导 分步引导将阿里云账号下的现有资源导入到本地 Terraform 管理。自动执行环境检查、资源发现、HCL 生成、state 导入等操作，用户仅需在关键决策点确认。 支持两种模式： - **一次性迁移**：完整走完 Phase 1-9 - **增量同步**：已有 Terraform 工作目录时，直接跳到增量同步模式 ## 资源支持范围 - **理论支持范围**：alicloud provider 中有 Read 方法的资源（即能从云上读取状态的资源）。大多数资源可通过 `terraform import` 命令或 `import` 块（Terraform 1.5+）直接导入；少数未实现 Importer 的资源可通过手动构造 tfstate 导入，但需精确匹配 provider 的 state 结构，存在一定风险。可通过 `https://registry.terraform.io/providers/aliyun/alicloud/latest/docs` 查询各资源的支持情况。 - **预置资源**：约 50 种常用资源（网络、计算、存储、数据库、负载均衡、DNS、RAM、ACK、FC），有经过验证的 API 发现命令、HCL 模板和 import ID 格式，见 `references/` - **预置范围之外**：会临时查 provider 文档给出命令和模板，准确性依赖文档内容，建议用户在执行前确认 --- ## Phase 1: 环境检查 执行以下命令检测环境： ```bash echo "=== aliyun CLI ===" && aliyun --version 2>&1 || echo "NOT_INSTALLED" echo "=== terraform ===" && terraform --version 2>&1 || echo "NOT_INSTALLED" echo "=== python3 ===" && python3 --version 2>&1 || echo "NOT_INSTALLED" echo "=== OS ===" && uname -s 2>&1 ``` 根据输出自动处理缺失工具： - aliyun CLI 缺失（macOS）：执行 `brew install aliyun-cli` - aliyun CLI 缺失（Linux）：执行 `curl -sSL --connect-timeout 10 --max-time 120 https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz | tar xz && sudo mv aliyun /usr/local/bin/` - terraform 缺失（macOS）：执行 `brew tap hashicorp/tap && brew install hashicorp/tap/terraform` - terraform 缺失（Linux）：提示用户前往 https://developer.hashicorp.com/terraform/install 手动安装（需要 root 权限） 全部就绪后进入 Phase 2。 **配置 aliyun CLI AI-Mode：** 所有 aliyun CLI 命令必须配置 AI-Mode 标识，以便阿里云识别 AI 调用场景并提供优化支持。在执行任何 aliyun CLI 命令前，先执行： ```bash # 启用 AI-Mode（必需） aliyun configure ai-mode enable # 设置 User-Agent 标识（必需，注意需要 --user-agent 参数名） aliyun configure ai-mode set-user-agent --user-agent [REDACTED_SECRET] # 更新插件到最新版本（推荐） aliyun plugin update ``` **禁用 AI-Mode（如需）：** ```bash aliyun configure ai-mode disable ``` 后续所有 aliyun CLI 命令都会自动携带 AI-Mode 标识和 User-Agent。所有命令必须使用插件模式（小写连字符格式，如 `aliyun vpc describe-vpcs`）。 **插件模式参数格式说明：** - 所有参数名使用小写连字符（lowercase-hyphenated）格式，不使用驼峰式 - **Region 参数有两种，含义不同**： - `--biz-region-id`：**业务参数**，对应 API 文档中的 `RegionId`，作为请求体参数传给 API。当 API 文档标注 `RegionId` 为 required 时必须使用此参数 - `--region`：**框架参数**，控制 CLI 连接哪个地域的 endpoint（如 `vpc.cn-shanghai.aliyuncs.com`），不会作为业务参数传给 API - 规则：优先使用 `--biz-region-id`（覆盖所有场景）。`--region` 仅在 API 的 `RegionId` 为可选参数时可替代（如 `describe-vswitches`，设置了 endpoint 地域后 API 会默认在该 region 查询） - 分页参数：使用 `--page-size`（而非 `--PageSize`） - 其他 API 参数：API 文档中的驼峰参数名加 `biz-` 前缀 + 小写连字符化（如 `VpcId` → `--vpc-id`，`InstanceIds` → `--instance-ids`）。可通过 `aliyun <product> <command> --help` 确认实际参数名 --- ## Phase 2: 认证配置 本 Skill 依赖阿里云默认凭证链进行认证，支持以下方式（按优先级）： 1. 环境变量（ALICLOUD_ACCESS_KEY / ALICLOUD_SECRET_KEY）— **最高优先级** 2. aliyun CLI 配置文件（~/.aliyun/config.json） 3. ECS RAM 角色（仅限在 ECS 上运行时） **重要**：Terraform alicloud provider 和 aliyun CLI 各自独立读取凭证，且均优先使用环境变量。当环境变量与 CLI profile 指向不同账号时，统一以环境变量为准。 **验证认证：** 通过默认凭证链验证身份（不显式传递 AK/SK）： ```bash aliyun sts get-caller-identity 2>&1 ``` 分析输出： - 确认 AccountId 和 UserId，记录 Region - **若凭证来源不符合预期**（如环境变量与 CLI profile 指向不同账号），告知用户存在凭证冲突，建议统一凭证配置后重试 若报错，提示用户： - 检查是否已配置凭证（通过 `aliyun configure` 或环境变量） - 检查权限（需要 ReadOnlyAccess 或对应资源的 Describe 权限） - 参考 `references/ram-policies.md` 了解所需权限 --- ## Phase 3: 工作目录初始化 询问用户以下信息： - 目标工作目录路径（默认 `~/alicloud-terraform`） - 目标 Region（若已从 Phase 2 推断出则确认，否则询问） - 是否已有 Terraform 工作目录（若已有则跳过本 Phase） 用户确认后，自动完成以下操作： 1. 创建工作目录及 `.import/` 子目录（存放导入过程的中间产物） 2. 在目标目录生成 `provider.tf`（参考 `examples/provider.tf`），将 region 默认值替换为用户确认的 Region 3. 在目标目录执行 `terraform init` 4. 分析 init 输出，确认 provider 下载成功 5. **（可选增强）** 执行 `terraform providers schema -json`，提取所有资源中字段名以 `_id` 或 `_ids` 结尾的字段，构建**引用字段白名单**，供 Phase 6 使用。若 schema 输出过大导致解析超时，可跳过此步，Phase 6 将降级使用静态依赖规则解析引用 6. **（可选增强）** 调用 `iacservice list-resource-types`（分页获取全量），将返回的 `resourceType` 字段前缀 `ALIYUN` 替换为 `ACS`，建立 `terraformResourceType ↔ ResourceCode` 双向映射表，写入 `.import/resource-type-mapping.json`，供 Phase 4/5/6 使用。若调用失败或权限不足，降级使用 `references/resource-hub-api.md` 中的静态映射表。**跳过此步不影响核心导入流程**，仅影响 ResourceCenter 接口的资源类型转换 7. 告知用户初始化完成，进入 Phase 4 --- ## Phase 4: 资源发现 **步骤 0 — 查询 ResourceCenter 支持的资源类型（优先执行）** 执行 `aliyun resourcecenter list-resource-types` 获取接口 A（search-resources）和接口 B（list-resource-relationships）各自支持的资源类型列表，缓存供本 Phase 和 Phase 5 使用。详见 `references/resource-hub-api.md`。 若调用失败或权限不足，跳过步骤 0，全程使用原生产品 API 发现资源。 **步骤 1 — 发现资源** 对每种目标资源类型，按以下优先级选择发现方式： 1. 若该资源类型在接口 A 支持列表中 → 用接口 A（search-resources）批量查询，详见 `references/resource-hub-api.md` 2. 否则 → 降级使用 `references/api-commands.md` 中的原生产品 API 命令 若用户需要发现 `references/api-commands.md` 中没有的资源类型： - 先查 provider 文档（`https://registry.terraform.io/providers/aliyun/alicloud/latest/docs`）判断支持情况： - 有 `Import` 章节 → 支持 `terraform import`，按正常流程处理 - 无 `Import` 章节但 provider 支持该资源 → 可通过手动构造 tfstate 导入，告知用户风险后按需处理 - provider 完全不支持该资源 → 明确告知无法导入 - 确认支持后，查 aliyun CLI 文档（`https://help.aliyun.com/document_detail/110244.html`）或对应产品 API 文档给出发现命令 执行相应命令，收集输出后生成资源发现报告，汇总各类型数量和 ID 列表。 **以下为降级路径的原生 API 命令（接口 A 不支持时使用）：** > **参数格式说明**：插件模式使用小写连字符格式。Region 参数因产品而异：部分使用 `--biz-region-id`，部分使用 `--region`。若一种报错请尝试另一种。 **网络资源（先执行）：** ```bash REGION="cn-hangzhou" # 替换为实际 Region # VPC（使用 --biz-region-id） aliyun vpc describe-vpcs --biz-region-id $REGION --page-size 50 \ --output cols=VpcId,VpcName,CidrBlock,Status rows=Vpcs.Vpc 2>&1 # VSwitch（使用 --region） aliyun vpc describe-vswitches --region $REGION --page-size 50 \ --output cols=VSwitchId,VSwitchName,CidrBlock,ZoneId,VpcId rows=VSwitches.VSwitch 2>&1 # 安全组（使用 --biz-region-id） aliyun ecs describe-security-groups --biz-region-id $REGION --page-size 50 \ --output cols=SecurityGroupId,SecurityGroupName,VpcId rows=SecurityGroups.SecurityGroup 2>&1 # EIP（使用 --biz-region-id） aliyun vpc describe-eip-addresses --biz-region-id $REGION --page-size 50 \ --output cols=AllocationId,IpAddress,Status,InstanceId rows=EipAddresses.EipAddress 2>&1 # NAT 网关（使用 --biz-region-id） aliyun vpc describe-nat-gateways --biz-region-id $REGION --page-size 50 \ --output cols=NatGatewayId,Name,VpcId,Status rows=NatGateways.NatGateway 2>&1 ``` **计算资源：** ```bash # ECS 实例（使用 --biz-region-id） aliyun ecs describe-instances --biz-region-id $REGION --page-size 50 \ --output cols=InstanceId,InstanceName,Status,InstanceType,ZoneId rows=Instances.Instance 2>&1 # 云盘（使用 --biz-region-id） aliyun ecs describe-disks --biz-region-id $REGION --page-size 50 \ --output cols=DiskId,DiskName,Status,Size,Type,InstanceId rows=Disks.Disk 2>&1 ``` **存储资源：** ```bash # OSS Bucket（全局，不需要 Region） aliyun ossutil ls oss:// 2>&1 ``` **数据库资源：** ```bash # RDS（使用 --biz-region-id） aliyun rds describe-db-instances --biz-region-id $REGION --page-size 50 \ --output cols=DBInstanceId,DBInstanceDescription,Engine,EngineVersion,DBInstanceStatus rows=Items.DBInstance 2>&1 # Redis（KVStore，使用 --biz-region-id） aliyun r-kvstore describe-instances --biz-region-id $REGION --page-size 50 \ --output cols=InstanceId,InstanceName,InstanceStatus,InstanceType rows=Instances.KVStoreInstance 2>&1 # MongoDB（DDS，使用 --biz-region-id） aliyun dds describe-db-instances --biz-region-id $REGION --page-size 50 \ --output cols=DBInstanceId,DBInstanceDescription,DBInstanceStatus rows=DBInstances.DBInstance 2>&1 ``` **负载均衡：** ```bash # SLB（传统负载均衡，使用 --biz-region-id） aliyun slb describe-load-balancers --biz-region-id $REGION --page-size 50 \ --output cols=LoadBalancerId,LoadBalancerName,LoadBalancerStatus,VpcId rows=LoadBalancers.LoadBalancer 2>&1 # ALB（应用型负载均衡，使用 --region） aliyun alb list-load-balancers --region $REGION --max-results 50 2>&1 ``` **DNS：** ```bash # 云解析域名（全局，不需要 Region） aliyun alidns describe-domains --page-size 50 \ --output cols=DomainId,DomainName,RecordCount rows=Domains.Domain 2>&1 ``` **资源发现结果为空时的处理：** 若某类资源（如 NAT 网关）发现结果为空但用户预期存在，主动排查： - 确认资源是否在其他 Region - 确认当前凭证是否有该资源的 Describe 权限 - 确认资源是否已被释放（可查询操作审计日志） - 告知用户发现结果与预期不符，请用户确认后再继续 --- ## Phase 5: 选择导入范围和深度 展示资源发现报告，询问用户： - 全部导入还是选择特定资源类型？ - 是否按 VPC 范围过滤？ - 是否按 Tag 过滤？ - **导入深度策略**： 1. 仅导入已发现的资源（不查询关联资源，深度=0） 2. 导入一层关联资源（深度=1） 3. 导入完整依赖树（深度=无限，推荐） **构建资源依赖图（根据深度策略选择方式）** 根据用户选择的深度策略： **深度=0（仅导入已发现资源）：** - 跳过接口 B 调用 - 直接使用 `references/dependency-rules.md` 中的静态依赖规则对 Phase 4 发现的资源排序 - 生成导入顺序 **深度=1（导入一层关联资源）：** - 对 Phase 4 发现的每个资源，若其类型在接口 B 支持列表中 → 调用接口 B（list-resource-relationships）获取直接关联的资源 - 对返回的关联资源不再递归调用接口 B - 合并发现的资源和一层关联资源，对资源图做拓扑排序，生成导入顺序 **深度=无限（导入完整依赖树，推荐）：** - 从 Phase 4 发现的资源中，识别无父依赖的根资源（VPC、OSS Bucket、DNS 域名、RAM 用户等） - 对每个根资源，若其类型在接口 B 支持列表中 → 调用接口 B 获取其关联资源 - 对关联资源递归调用接口 B，直到没有新资源出现（新发现资源集合 == 空集） - 对不支持接口 B 的资源类型 → 降级使用 `references/dependency-rules.md` 中的静态依赖规则推断 - 合并所有关系树，对资源图做拓扑排序，生成导入顺序 **降级路径（接口 B 不可用时）的静态导入顺序：** ``` Layer 0: VPC、OSS Bucket（无依赖） Layer 1: VSwitch、安全组、EIP（依赖 VPC） Layer 2: NAT 网关、SLB（依赖 VPC + VSwitch） Layer 3: ECS 实例（依赖 VSwitch + 安全组） Layer 4: 云盘（依赖 ECS） Layer 5: RDS、Redis、MongoDB（依赖 VSwitch） Layer 6: DNS 记录（依赖域名） ``` 详细依赖规则参考 `references/dependency-rules.md`。 --- ## Phase 6: 生成 Terraform HCL 代码 按资源类型逐批处理。对每类资源： 1. 执行获取详情的命令（JSON 格式） 2. 解析 JSON，生成对应 .tf 文件并写入工作目录 每类资源一个文件，文件名格式为 `<产品>_<资源类型>.tf`： ``` vpc_vpcs.tf vpc_vswitches.tf vpc_security_groups.tf vpc_eips.tf vpc_nat_gateways.tf ecs_instances.tf ecs_disks.tf oss_buckets.tf rds_instances.tf kvstore_instances.tf dds_instances.tf slb_load_balancers.tf alidns_domains.tf alidns_records.tf ``` HCL 代码模板参考 `references/terraform-patterns.md`，示例参考 `examples/` 目录。 **跨资源引用解析（三层策略）** 生成每个资源的 HCL 时，对每个字段值按以下优先级判断是否替换为 terraform resource address： 1. **接口 B 关系优先**：若该资源是通过接口 B（list-resource-relationships）发现的，其父资源或关联资源已确定。结合 Phase 3 提取的引用字段白名单，找到当前资源中类型匹配的 `_id` 字段，直接替换为对应的 terraform resource address。关系是确定的，无需猜测。 2. **白名单 + 映射表双重匹配兜底**：对接口 B 未覆盖的资源，字段名在 Phase 3 提取的引用字段白名单中，且字段值在资源 ID 映射表中有对应条目，则替换为 terraform resource address。 3. **dependency-rules.md 静态规则兜底**：对以上两层均无法覆盖的边缘情况（复合 ID、非 `_id` 结尾的引用字段等），参考 `references/dependency-rules.md` 中的静态规则处理。 **资源 ID 映射表** 每生成一个资源块，立即向映射表追加一条记录： ``` <云资源 ID> → <terraform resource address> # 示例： vpc-bp1xxx → alicloud_vpc.vpc_prod_main vsw-bp1aaa → alicloud_vswitch.vsw_prod_a sg-bp1xxx → alicloud_security_group.sg_web ``` 映射表写入 `.import/id-mapping.json`，后续阶段查表使用。 **其他关键原则：** - 资源名称使用 `<type>_<name>` 格式（如 `alicloud_vpc.vpc_prod_main`） - 密码/密钥字段使用变量（`var.db_password`） - 保留原始 tags --- ## Phase 7: 导入 State 根据 Phase 5 生成的拓扑排序，生成完整的 `terraform import` 命令清单，展示给用户确认后按批次执行。 **批次 1 — 网络基础：** ```bash terraform import alicloud_vpc.vpc_<name> <vpc-id> terraform import alicloud_vswitch.vsw_<name> <vsw-id> terraform import alicloud_security_group.sg_<name> <sg-id> ``` **批次 2 — 网络附属：** ```bash terraform import alicloud_nat_gateway.nat_<name> <nat-id> terraform import alicloud_eip_address.eip_<name> <eip-id> ``` **批次 3 — 计算：** ```bash terraform import alicloud_instance.ecs_<name> <instance-id> terraform import alicloud_disk.disk_<name> <disk-id> ``` **批次 4 — 存储：** ```bash terraform import alicloud_oss_bucket.bucket_<name> <bucket-name> ``` **批次 5 — 数据库：** ```bash terraform import alicloud_db_instance.rds_<name> <db-id> terraform import alicloud_kvstore_instance.redis_<name> <redis-id> terraform import alicloud_mongodb_instance.mongo_<name> <mongo-id> ``` **批次 6 — 负载均衡：** ```bash terraform import alicloud_slb_load_balancer.slb_<name> <slb-id> ``` **批次 7 — DNS：** ```bash terraform import alicloud_dns_domain.domain_<name> <domain-name> ``` 每批执行后分析输出，处理常见错误： - `ResourceNotFound`：资源 ID 有误，重新确认 - `PermissionDenied`：需要对应资源的 Describe 权限 - `already managed`：资源已在 state 中，跳过 - ID 格式错误：参考 `references/resource-types.md` 中的 import 格式 --- ## Phase 8: 验证 执行以下命令验证导入结果： ```bash # 列出 state 中所有资源 terraform state list 2>&1 # 执行 plan，目标是 No changes terraform plan -refresh=true 2>&1 ``` 分析 plan 输出： - `No changes` → 验证通过，进入 Phase 9 - 有 diff → 分析原因，参考 `references/terraform-patterns.md` 中的常见 diff 修复模式，给出修复方案并告知用户，用户确认后执行修复 常见 diff 原因： - 标签格式不匹配（需调整 HCL 中的 tags 写法） - 密码字段无法从 API 读取（用 `ignore_changes` 忽略） - 计算字段差异（添加 `lifecycle { ignore_changes = [...] }`） - 默认值差异（显式设置与云上一致的值） --- ## Phase 9: 资源关系图 执行以下命令生成资源关系图： ```bash # 导出 state JSON terraform show -json > .import/tf_state.json 2>&1 # 提取依赖关系 python3 -c " import json, sys with open('.import/tf_state.json') as f: state = json.load(f) resources = state.get('values', {}).get('root_module', {}).get('resources', []) print(f'总资源数: {len(resources)}') for r in resources: deps = r.get('depends_on', []) if deps: for d in deps: print(f' {d} --> {r[\"address\"]}') " 2>&1 ``` 根据输出生成文本格式资源关系图（树状结构），展示： - 网络层 → 计算层 → 数据层的依赖链 - 安全组的横切引用 - 负载均衡的后端绑定关系 示例格式参考 `examples/dependency-graph.txt`。 --- ## 增量同步模式 已有 Terraform 工作目录时使用此模式，自动检测三类变化： **1. 检测新增资源（云上有，state 没有）：** 执行以下命令对比 state 和云上的 ID 列表： ```bash # 以 ECS 为例 terraform state list | grep alicloud_instance aliyun ecs describe-instances --biz-region-id $REGION --page-size 100 \ --output cols=InstanceId rows=Instances.Instance 2>&1 ``` 对比两个列表，找出差集，生成新增资源的 HCL + import 命令，告知用户确认后执行。 **2. 检测删除资源（state 有，云上没有）：** 对 state 中每个资源执行存在性检查，若 API 返回空则执行： ```bash terraform state rm alicloud_instance.<name> ``` **3. 检测配置漂移：** 执行以下命令检测漂移： ```bash terraform plan -refresh=true 2>&1 ``` 详细增量同步方案参考 `references/incremental-sync.md`。 --- ## 参考资料 - `references/resource-types.md` — 资源类型完整映射表（20+ 种资源） - `references/terraform-patterns.md` — HCL 代码模板 + 常见 diff 修复 - `references/state-management.md` — terraform state 命令参考 - `references/api-commands.md` — aliyun CLI 命令速查（原生产品 API 降级路径） - `references/dependency-rules.md` — 资源依赖层级和 import 顺序（接口 B 降级路径） - `references/resource-hub-api.md` — ResourceCenter 接口使用指南（list-resource-types / search-resources / list-resource-relationships） - `references/incremental-sync.md` — 增量同步详细方案

path: references/api-commands.md
content: # aliyun CLI 命令速查

按产品分组的资源发现命令，含分页参数和输出格式化。

**说明：**
- 标注 ✅ 的命令已通过测试账号验证，可直接使用
- 未标注的命令基于训练知识生成，执行前建议先用 `aliyun <product> help` 验证参数
- 文件中没有的资源类型，发现阶段由 Claude 现查 aliyun CLI 文档或 provider 文档给出命令
- **所有命令使用插件模式（小写连字符格式）**，参数名不使用驼峰式

---

## 通用参数说明

```bash
# 设置 Region（所有命令通用）
REGION="cn-hangzhou"

# Region 参数（两种，含义不同）：
# --biz-region-id  业务参数，对应 API 的 RegionId，传入请求体。API 要求 RegionId 时用此参数
# --region         框架参数，控制连接哪个地域的 endpoint，不传入 API 请求体
# 规则：优先使用 --biz-region-id（如 vpc describe-vpcs、ecs describe-instances）
#       仅当 API 的 RegionId 为可选时，--region 可替代（如 vpc describe-vswitches）

# 分页参数
--page-size 50       # 每页数量（最大 100）
--page-number 1      # 页码（从 1 开始）

# 输出格式化（表格形式）
--output cols=Field1,Field2 rows=Path.To.Array

# JSON 格式输出（用于解析详情）
# 默认输出即为 JSON，不需要额外参数
```

---

## 网络产品（VPC）

```bash
# ✅ VPC 列表（使用 --biz-region-id）
aliyun vpc describe-vpcs --biz-region-id $REGION --page-size 50 \
  --output cols=VpcId,VpcName,CidrBlock,Status rows=Vpcs.Vpc

# ✅ VPC 详情（JSON）
aliyun vpc describe-vpcs --biz-region-id $REGION --vpc-id vpc-bp1xxx

# ✅ VSwitch 列表（使用 --region）
aliyun vpc describe-vswitches --region $REGION --page-size 50 \
  --output cols=VSwitchId,VSwitchName,CidrBlock,ZoneId,VpcId rows=VSwitches.VSwitch

# ✅ VSwitch 详情
aliyun vpc describe-vswitches --region $REGION --vswitch-id vsw-bp1xxx

# 路由表列表
aliyun vpc describe-route-tables --biz-region-id $REGION --page-size 50 \
  --output cols=RouteTableId,RouteTableName,VpcId,RouteTableType rows=RouteTables.RouteTable

# EIP 列表（使用 --biz-region-id）
aliyun vpc describe-eip-addresses --biz-region-id $REGION --page-size 50 \
  --output cols=AllocationId,IpAddress,Status,InstanceId,InstanceType rows=EipAddresses.EipAddress

# EIP 详情
aliyun vpc describe-eip-addresses --biz-region-id $REGION --allocation-id eip-bp1xxx

# ✅ NAT 网关列表（使用 --biz-region-id）
aliyun vpc describe-nat-gateways --biz-region-id $REGION --page-size 50 \
  --output cols=NatGatewayId,Name,VpcId,Status,NatType rows=NatGateways.NatGateway

# NAT 网关详情
aliyun vpc describe-nat-gateways --biz-region-id $REGION --nat-gateway-id ngw-bp1xxx

# SNAT 条目列表
aliyun vpc describe-snat-table-entries --biz-region-id $REGION \
  --snat-table-id stb-bp1xxx --page-size 50 \
  --output cols=SnatEntryId,SnatIp,SourceCidr,Status rows=SnatTableEntries.SnatTableEntry

# VPN 网关列表
aliyun vpc describe-vpn-gateways --biz-region-id $REGION --page-size 50 \
  --output cols=VpnGatewayId,Name,VpcId,Status rows=VpnGateways.VpnGateway
```

---

## 计算产品（ECS）

```bash
# ✅ ECS 实例列表（使用 --biz-region-id）
aliyun ecs describe-instances --biz-region-id $REGION --page-size 50 \
  --output cols=InstanceId,InstanceName,Status,InstanceType,ZoneId,VpcAttributes.VpcId rows=Instances.Instance

# ✅ ECS 实例详情（JSON，包含所有属性）
aliyun ecs describe-instances --biz-region-id $REGION \
  --instance-ids '["i-bp1xxx"]'

# ✅ 安全组列表（使用 --biz-region-id）
aliyun ecs describe-security-groups --biz-region-id $REGION --page-size 50 \
  --output cols=SecurityGroupId,SecurityGroupName,VpcId,SecurityGroupType rows=SecurityGroups.SecurityGroup

# 安全组详情（含规则）
aliyun ecs describe-security-group-attribute --biz-region-id $REGION \
  --security-group-id sg-bp1xxx --direction ingress
aliyun ecs describe-security-group-attribute --biz-region-id $REGION \
  --security-group-id sg-bp1xxx --direction egress

# ✅ 云盘列表（按实例过滤）
aliyun ecs describe-disks --biz-region-id $REGION --instance-id i-bp1xxx --page-size 50 \
  --output cols=DiskId,DiskName,Status,Size,Type,Category rows=Disks.Disk

# 云盘详情
aliyun ecs describe-disks --biz-region-id $REGION --disk-ids '["d-bp1xxx"]'

# 镜像列表（自定义镜像）
aliyun ecs describe-images --biz-region-id $REGION --image-owner-alias self --page-size 50 \
  --output cols=ImageId,ImageName,Status,OSType rows=Images.Image

# 密钥对列表
aliyun ecs describe-key-pairs --biz-region-id $REGION --page-size 50 \
  --output cols=KeyPairName,KeyPairFingerPrint rows=KeyPairs.KeyPair

# 弹性伸缩组列表
aliyun ess describe-scaling-groups --biz-region-id $REGION --page-size 50 \
  --output cols=ScalingGroupId,ScalingGroupName,LifecycleState rows=ScalingGroups.ScalingGroup

# 伸缩配置列表
aliyun ess describe-scaling-configurations --biz-region-id $REGION \
  --scaling-group-id asg-bp1xxx --page-size 50 \
  --output cols=ScalingConfigurationId,ScalingConfigurationName,LifecycleState rows=ScalingConfigurations.ScalingConfiguration
```

---

## 存储产品（OSS）

```bash
# OSS Bucket 列表（全局，不需要 Region）
aliyun ossutil ls oss://

# OSS Bucket 详情
aliyun ossutil stat oss://bucket-name

# OSS Bucket ACL
aliyun ossutil bucket-acl oss://bucket-name

# OSS Bucket 版本控制
aliyun ossutil bucket-versioning oss://bucket-name

# OSS Bucket 生命周期
aliyun ossutil lifecycle --method get oss://bucket-name

# OSS Bucket CORS
aliyun ossutil cors --method get oss://bucket-name

# OSS Bucket 加密
aliyun ossutil bucket-encryption --method get oss://bucket-name

# NAS 文件系统列表
aliyun nas describe-file-systems --biz-region-id $REGION --page-size 50 \
  --output cols=FileSystemId,FileSystemType,Status,StorageType rows=FileSystems.FileSystem

# NAS 挂载点列表
aliyun nas describe-mount-targets --biz-region-id $REGION \
  --file-system-id fs-xxx --page-size 50 \
  --output cols=MountTargetDomain,Status,VpcId,VSwitchId rows=MountTargets.MountTarget
```

---

## 数据库产品（RDS）

```bash
# RDS 实例列表（使用 --biz-region-id）
aliyun rds describe-db-instances --biz-region-id $REGION --page-size 50 \
  --output cols=DBInstanceId,DBInstanceDescription,Engine,EngineVersion,DBInstanceStatus,DBInstanceClass rows=Items.DBInstance

# RDS 实例详情
aliyun rds describe-db-instance-attribute --biz-region-id $REGION \
  --db-instance-id rm-bp1xxx

# RDS 数据库列表
aliyun rds describe-databases --biz-region-id $REGION \
  --db-instance-id rm-bp1xxx \
  --output cols=DBName,DBStatus,CharacterSetName rows=Databases.Database

# RDS 账号列表
aliyun rds describe-accounts --biz-region-id $REGION \
  --db-instance-id rm-bp1xxx \
  --output cols=AccountName,AccountStatus,AccountType rows=Accounts.DBInstanceAccount

# RDS 白名单
aliyun rds describe-db-instance-ip-array-list --biz-region-id $REGION \
  --db-instance-id rm-bp1xxx

# RDS 连接地址
aliyun rds describe-db-instance-net-info --biz-region-id $REGION \
  --db-instance-id rm-bp1xxx
```

---

## 缓存产品（Redis/KVStore）

```bash
# Redis 实例列表（使用 --biz-region-id）
aliyun r-kvstore describe-instances --biz-region-id $REGION --page-size 50 \
  --output cols=InstanceId,InstanceName,InstanceStatus,InstanceType,EngineVersion rows=Instances.KVStoreInstance

# Redis 实例详情
aliyun r-kvstore describe-instance-attribute --biz-region-id $REGION \
  --instance-id r-bp1xxx

# Redis 白名单
aliyun r-kvstore describe-security-ips --biz-region-id $REGION \
  --instance-id r-bp1xxx
```

---

## 文档数据库（MongoDB/DDS）

```bash
# MongoDB 实例列表（使用 --biz-region-id）
aliyun dds describe-db-instances --biz-region-id $REGION --page-size 50 \
  --output cols=DBInstanceId,DBInstanceDescription,DBInstanceStatus,DBInstanceType rows=DBInstances.DBInstance

# MongoDB 实例详情
aliyun dds describe-db-instance-attribute --biz-region-id $REGION \
  --db-instance-id dds-bp1xxx

# MongoDB 白名单
aliyun dds describe-security-ips --biz-region-id $REGION \
  --db-instance-id dds-bp1xxx
```

---

## 负载均衡（SLB）

```bash
# SLB 实例列表（使用 --biz-region-id）
aliyun slb describe-load-balancers --biz-region-id $REGION --page-size 50 \
  --output cols=LoadBalancerId,LoadBalancerName,LoadBalancerStatus,AddressType,VpcId rows=LoadBalancers.LoadBalancer

# SLB 实例详情
aliyun slb describe-load-balancer-attribute --biz-region-id $REGION \
  --load-balancer-id lb-bp1xxx

# SLB 监听列表
aliyun slb describe-load-balancer-listeners --biz-region-id $REGION \
  --load-balancer-id lb-bp1xxx \
  --output cols=ListenerPort,ListenerProtocol,Status rows=Listeners

# SLB 后端服务器
aliyun slb describe-health-status --biz-region-id $REGION \
  --load-balancer-id lb-bp1xxx

# ALB 实例列表（使用 --region）
aliyun alb list-load-balancers --region $REGION --max-results 50

# ALB 监听列表
aliyun alb list-listeners --region $REGION --max-results 50

# ALB 服务器组列表
aliyun alb list-server-groups --region $REGION --max-results 50
```

---

## DNS（云解析）

```bash
# 域名列表（全局，不需要 Region）
aliyun alidns describe-domains --page-size 50 \
  --output cols=DomainId,DomainName,RecordCount,GroupName rows=Domains.Domain

# 域名解析记录列表
aliyun alidns describe-domain-records --domain-name example.com --page-size 500 \
  --output cols=RecordId,RR,Type,Value,TTL,Status rows=DomainRecords.Record

# DNS 分组列表
aliyun alidns describe-domain-groups --page-size 50 \
  --output cols=GroupId,GroupName rows=DomainGroups.DomainGroup
```

---

## 安全与身份（RAM）

```bash
# RAM 用户列表
aliyun ram list-users --output cols=UserName,DisplayName,CreateDate rows=Users.User

# RAM 角色列表
aliyun ram list-roles --output cols=RoleName,RoleId,CreateDate rows=Roles.Role

# RAM 策略列表（自定义）
aliyun ram list-policies --policy-type Custom \
  --output cols=PolicyName,PolicyType,CreateDate rows=Policies.Policy

# RAM 用户绑定的策略
aliyun ram list-policies-for-user --user-name username \
  --output cols=PolicyName,PolicyType rows=Policies.Policy

# KMS 密钥列表
aliyun kms list-keys --page-size 100 \
  --output cols=KeyId,KeyArn rows=Keys.Key

# KMS 密钥详情
aliyun kms describe-key --key-id key-id
```

---

## 容器服务（ACK）

```bash
# ACK 集群列表
aliyun cs GET /clusters 2>&1

# ACK 节点池列表
aliyun cs GET /clusters/<cluster-id>/nodepools 2>&1
```

---

## 函数计算（FC）

```bash
# FC 服务列表
aliyun fc GET /services 2>&1

# FC 函数列表
aliyun fc GET /services/<service-name>/functions 2>&1
```

---

## 常见问题

**Q: 命令返回空结果**
- 检查 Region 是否正确
- 检查账号是否有对应资源的 Describe 权限
- 部分资源（如 OSS）是全局的，不需要 Region 参数

**Q: Region 参数报错**
- `--biz-region-id` 是业务参数（对应 API 的 RegionId），`--region` 是框架参数（控制 endpoint 地域）
- 优先使用 `--biz-region-id`；仅当 API 的 RegionId 为可选时 `--region` 可替代
- 若命令报 `--biz-region-id is required`，说明该 API 的 RegionId 是必填，不能仅用 `--region`
- 全局资源（OSS、DNS、RAM）不需要 Region 参数

**Q: 分页问题（资源超过 50 个）**
```bash
# 使用页码分页
aliyun vpc describe-vpcs --biz-region-id $REGION --page-size 50 --page-number 2

# 或使用 --pager 参数（aliyun CLI 支持自动分页合并）
aliyun ecs describe-instances --biz-region-id $REGION --page-size 100 --pager
```

**Q: 输出格式化不生效**
- 确认 `rows=` 路径正确（可先不加 `--output` 参数查看原始 JSON 结构）
- 嵌套字段用 `.` 分隔（如 `VpcAttributes.VpcId`）

**Q: 参数名不确定**
- 使用 `aliyun <product> <command> --help` 查看所有支持的参数
- 插件模式参数一律为小写连字符格式（如 `--instance-id`、`--vpc-id`）


path: references/dependency-rules.md
content: # 资源依赖关系规则

资源依赖层级图和 import 顺序约束。

---

## 依赖层级图

```
Layer 0（无依赖，可并行 import）
├── alicloud_vpc
├── alicloud_oss_bucket
├── alicloud_dns_domain
├── alicloud_ram_user
├── alicloud_ram_role
├── alicloud_ram_policy
├── alicloud_kms_key
├── alicloud_key_pair
└── alicloud_image

Layer 1（依赖 Layer 0）
├── alicloud_vswitch              → 依赖 alicloud_vpc
├── alicloud_security_group       → 依赖 alicloud_vpc
├── alicloud_route_table          → 依赖 alicloud_vpc
├── alicloud_eip_address          → 无依赖（可与 Layer 0 并行）
├── alicloud_oss_bucket_acl       → 依赖 alicloud_oss_bucket
├── alicloud_oss_bucket_policy    → 依赖 alicloud_oss_bucket
├── alicloud_dns_record           → 依赖 alicloud_dns_domain
└── alicloud_ram_user_policy_attachment → 依赖 alicloud_ram_user + alicloud_ram_policy

Layer 2（依赖 Layer 1）
├── alicloud_nat_gateway          → 依赖 alicloud_vpc + alicloud_vswitch
├── alicloud_vpn_gateway          → 依赖 alicloud_vpc
├── alicloud_slb_load_balancer    → 依赖 alicloud_vswitch（内网）
├── alicloud_alb_load_balancer    → 依赖 alicloud_vpc + alicloud_vswitch
├── alicloud_alb_server_group     → 依赖 alicloud_vpc
├── alicloud_security_group_rule  → 依赖 alicloud_security_group
└── alicloud_route_entry          → 依赖 alicloud_route_table

Layer 3（依赖 Layer 2）
├── alicloud_instance             → 依赖 alicloud_vswitch + alicloud_security_group
├── alicloud_db_instance          → 依赖 alicloud_vswitch
├── alicloud_kvstore_instance     → 依赖 alicloud_vswitch
├── alicloud_mongodb_instance     → 依赖 alicloud_vswitch
├── alicloud_polardb_cluster      → 依赖 alicloud_vswitch
├── alicloud_nas_file_system      → 无依赖（可与 Layer 0 并行）
├── alicloud_snat_entry           → 依赖 alicloud_nat_gateway
├── alicloud_forward_entry        → 依赖 alicloud_nat_gateway
├── alicloud_eip_association      → 依赖 alicloud_eip_address + 实例
├── alicloud_slb_listener         → 依赖 alicloud_slb_load_balancer
└── alicloud_alb_listener         → 依赖 alicloud_alb_load_balancer

Layer 4（依赖 Layer 3）
├── alicloud_disk                 → 无依赖（可与 Layer 0 并行）
├── alicloud_disk_attachment      → 依赖 alicloud_disk + alicloud_instance
├── alicloud_db_database          → 依赖 alicloud_db_instance
├── alicloud_db_account           → 依赖 alicloud_db_instance
├── alicloud_slb_attachment       → 依赖 alicloud_slb_load_balancer + alicloud_instance
├── alicloud_alb_server_group     → 依赖 alicloud_alb_load_balancer（服务器绑定）
├── alicloud_nas_mount_target     → 依赖 alicloud_nas_file_system + alicloud_vswitch
└── alicloud_polardb_database     → 依赖 alicloud_polardb_cluster

Layer 5（依赖 Layer 4）
├── alicloud_ess_scaling_group    → 依赖 alicloud_vswitch
├── alicloud_cs_managed_kubernetes → 依赖 alicloud_vpc + alicloud_vswitch
└── alicloud_fc_service           → 无依赖（可与 Layer 0 并行）

Layer 6（依赖 Layer 5）
├── alicloud_ess_scaling_configuration → 依赖 alicloud_ess_scaling_group
├── alicloud_ess_scaling_rule          → 依赖 alicloud_ess_scaling_group
├── alicloud_cs_kubernetes_node_pool   → 依赖 alicloud_cs_managed_kubernetes
└── alicloud_fc_function               → 依赖 alicloud_fc_service
```

---

## 推荐 import 顺序

按以下顺序执行 import，避免依赖错误：

```
批次 1：VPC、OSS Bucket、DNS 域名、RAM 资源、KMS 密钥
批次 2：VSwitch、安全组、路由表、EIP
批次 3：NAT 网关、SLB、ALB、安全组规则
批次 4：ECS 实例、RDS、Redis、MongoDB、NAS
批次 5：云盘挂载、EIP 绑定、SNAT/DNAT、SLB 监听、数据库账号
批次 6：弹性伸缩组、ACK 集群
批次 7：伸缩配置、节点池、FC 函数
```

---

## 各资源的关键依赖属性

### alicloud_vswitch
```hcl
vpc_id    = alicloud_vpc.<name>.id      # 必须引用，不能硬编码
zone_id   = "cn-hangzhou-h"             # 固定值，不引用其他资源
```

### alicloud_security_group
```hcl
vpc_id = alicloud_vpc.<name>.id
```

### alicloud_instance
```hcl
vswitch_id      = alicloud_vswitch.<name>.id
security_groups = [alicloud_security_group.<name>.id]
# 注意：security_groups 是列表，可引用多个安全组
```

### alicloud_nat_gateway
```hcl
vpc_id     = alicloud_vpc.<name>.id
vswitch_id = alicloud_vswitch.<name>.id  # Enhanced 类型必须指定
```

### alicloud_snat_entry
```hcl
snat_table_id     = split(",", alicloud_nat_gateway.<name>.snat_table_ids)[0]
source_vswitch_id = alicloud_vswitch.<name>.id
snat_ip           = alicloud_eip_address.<name>.ip_address
```

### alicloud_eip_association
```hcl
allocation_id = alicloud_eip_address.<name>.id
instance_id   = alicloud_instance.<name>.id
instance_type = "EcsInstance"  # 或 "Nat"、"SlbInstance" 等
```

### alicloud_db_instance
```hcl
vswitch_id = alicloud_vswitch.<name>.id
# 注意：RDS 不直接引用安全组，通过 security_ips 白名单控制访问
```

### alicloud_slb_load_balancer（内网）
```hcl
vswitch_id   = alicloud_vswitch.<name>.id
address_type = "intranet"
```

### alicloud_slb_attachment
```hcl
load_balancer_id = alicloud_slb_load_balancer.<name>.id
instance_ids     = [alicloud_instance.<name>.id]
# 注意：instance_ids 是列表
```

---

## 循环依赖检测

以下组合容易产生循环依赖，需要特别注意：

1. **ECS + 安全组**：ECS 引用安全组，安全组规则引用 ECS 的私有 IP
   - 解决：安全组规则使用 CIDR 而非实例 IP，或使用安全组 ID 互引

2. **SLB + ECS**：SLB 后端引用 ECS，ECS 安全组规则引用 SLB
   - 解决：安全组规则使用 SLB 的 IP 段，不直接引用 SLB 资源

3. **NAT + EIP**：NAT 网关需要 EIP，EIP 绑定需要 NAT 网关 ID
   - 解决：先 import NAT 网关，再 import EIP 绑定关系

---

## 跨 VPC 资源引用

当资源跨 VPC 时（如 VPC Peering），需要额外注意：

```hcl
# VPC Peering 连接
resource "alicloud_vpc_peer_connection" "peer_<name>" {
  peer_connection_name = "<name>"
  vpc_id               = alicloud_vpc.vpc_a.id
  accepting_ali_uid    = "<peer-account-id>"
  accepting_region_id  = "cn-beijing"
  accepting_vpc_id     = "<peer-vpc-id>"
}
```

---

## 多 Region 资源

当需要管理多个 Region 的资源时，使用 provider alias：

```hcl
provider "alicloud" {
  alias      = "beijing"
  region     = "cn-beijing"
  access_key = var.access_key
  secret_key = var.secret_key
}

resource "alicloud_vpc" "vpc_beijing" {
  provider   = alicloud.beijing
  vpc_name   = "prod-beijing"
  cidr_block = "172.16.0.0/12"
}
```


path: references/incremental-sync.md
content: # 增量同步方案

检测阿里云资源变化并同步到 Terraform 的详细方案。

---

## 三类变化

| 变化类型 | 描述 | 检测方法 | 处理方式 |
|---------|------|---------|---------|
| 新增资源 | 云上有，state 没有 | 对比 API 列表 vs state list | 生成 HCL + terraform import |
| 删除资源 | state 有，云上没有 | 对 state 中每个资源做存在性检查 | terraform state rm |
| 配置漂移 | 两边都有，但配置不一致 | terraform plan -refresh=true | 更新 HCL 或 terraform apply |

---

## 检测新增资源

### ECS 实例

```bash
REGION="cn-hangzhou"

# 获取 state 中已管理的 ECS ID 列表
STATE_IDS=$(terraform state list | grep "^alicloud_instance\." | \
  xargs -I{} terraform state show {} 2>/dev/null | \
  grep '"id"' | awk '{print $3}' | tr -d '",')

# 获取云上当前所有 ECS ID
CLOUD_IDS=$(aliyun ecs describe-instances --biz-region-id $REGION --page-size 100 \
  --output cols=InstanceId rows=Instances.Instance 2>/dev/null | tail -n +2)

# 找出差集（云上有但 state 没有）
echo "=== 未纳管的 ECS 实例 ==="
for id in $CLOUD_IDS; do
  if ! echo "$STATE_IDS" | grep -q "$id"; then
    # 获取实例名称
    name=$(aliyun ecs describe-instances --biz-region-id $REGION \
      --InstanceIds "[\"$id\"]" 2>/dev/null | \
      python3 -c "import json,sys; d=json.load(sys.stdin); print(d['Instances']['Instance'][0].get('InstanceName','unknown'))" 2>/dev/null)
    echo "  $id ($name)"
  fi
done
```

### VPC

```bash
# 获取 state 中已管理的 VPC ID
STATE_VPC=$(terraform state list | grep "^alicloud_vpc\." | \
  xargs -I{} terraform state show {} 2>/dev/null | \
  grep '"id"' | awk '{print $3}' | tr -d '",')

# 获取云上所有 VPC ID
CLOUD_VPC=$(aliyun vpc describe-vpcs --biz-region-id $REGION --page-size 50 \
  --output cols=VpcId rows=Vpcs.Vpc 2>/dev/null | tail -n +2)

echo "=== 未纳管的 VPC ==="
for id in $CLOUD_VPC; do
  if ! echo "$STATE_VPC" | grep -q "$id"; then
    echo "  $id"
  fi
done
```

### 通用检测脚本

```python
#!/usr/bin/env python3
"""检测未纳管的阿里云资源"""
import subprocess
import json
import sys

REGION = "cn-hangzhou"

RESOURCE_CHECKS = [
    {
        "tf_type": "alicloud_instance",
        "api_cmd": f"aliyun ecs describe-instances --biz-region-id {REGION} --page-size 100",
        "id_path": "Instances.Instance[].InstanceId",
        "name_path": "Instances.Instance[].InstanceName",
    },
    {
        "tf_type": "alicloud_vpc",
        "api_cmd": f"aliyun vpc describe-vpcs --biz-region-id {REGION} --page-size 50",
        "id_path": "Vpcs.Vpc[].VpcId",
        "name_path": "Vpcs.Vpc[].VpcName",
    },
    {
        "tf_type": "alicloud_db_instance",
        "api_cmd": f"aliyun rds describe-db-instances --biz-region-id {REGION} --page-size 50",
        "id_path": "Items.DBInstance[].DBInstanceId",
        "name_path": "Items.DBInstance[].DBInstanceDescription",
    },
]

def get_state_ids(tf_type):
    result = subprocess.run(
        ["terraform", "state", "list"],
        capture_output=True, text=True
    )
    resources = [r for r in result.stdout.strip().split("\n") if r.startswith(f"{tf_type}.")]
    ids = set()
    for r in resources:
        show = subprocess.run(
            ["terraform", "state", "show", r],
            capture_output=True, text=True
        )
        for line in show.stdout.split("\n"):
            if '"id"' in line and "=" in line:
                ids.add(line.split("=")[1].strip().strip('"'))
    return ids

def get_cloud_ids(api_cmd, id_path):
    result = subprocess.run(api_cmd.split(), capture_output=True, text=True)
    data = json.loads(result.stdout)
    # 简化路径解析
    parts = id_path.split(".")
    current = data
    for part in parts:
        if "[]" in part:
            key = part.replace("[]", "")
            current = [item[key] for item in current.get(key, [])]
            break
        current = current.get(part, {})
    return set(current) if isinstance(current, list) else set()

for check in RESOURCE_CHECKS:
    state_ids = get_state_ids(check["tf_type"])
    cloud_ids = get_cloud_ids(check["api_cmd"], check["id_path"])
    new_ids = cloud_ids - state_ids
    removed_ids = state_ids - cloud_ids

    if new_ids:
        print(f"\n[新增] {check['tf_type']}:")
        for id in new_ids:
            print(f"  + {id}")

    if removed_ids:
        print(f"\n[删除] {check['tf_type']}:")
        for id in removed_ids:
            print(f"  - {id}")
```

---

## 检测删除资源

```bash
# 对 state 中每个 ECS 实例做存在性检查
echo "=== 检查 state 中的 ECS 实例是否仍存在 ==="
terraform state list | grep "^alicloud_instance\." | while read resource; do
  instance_id=$(terraform state show "$resource" 2>/dev/null | \
    grep '"id"' | head -1 | awk '{print $3}' | tr -d '",')

  if [ -n "$instance_id" ]; then
    result=$(aliyun ecs describe-instances --biz-region-id $REGION \
      --InstanceIds "[\"$instance_id\"]" 2>/dev/null | \
      python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d['Instances']['Instance']))" 2>/dev/null)

    if [ "$result" = "0" ]; then
      echo "  已删除: $resource ($instance_id)"
      echo "  执行: terraform state rm $resource"
    fi
  fi
done
```

---

## 检测配置漂移

```bash
# 刷新 state 并检测漂移
terraform plan -refresh=true -out=drift.tfplan 2>&1

# 查看漂移详情
terraform show drift.tfplan 2>&1

# 仅查看有变更的资源
terraform plan -refresh=true 2>&1 | grep -A5 "will be updated"
```

---

## 增量同步工作流

```bash
#!/bin/bash
# incremental-sync.sh - 增量同步脚本

set -e
REGION="${ALICLOUD_REGION:-cn-hangzhou}"
WORK_DIR="${1:-$(pwd)}"

cd "$WORK_DIR"

echo "=== 阿里云 Terraform 增量同步 ==="
echo "Region: $REGION"
echo "工作目录: $WORK_DIR"
echo ""

# Step 1: 检测配置漂移
echo "--- Step 1: 检测配置漂移 ---"
terraform plan -refresh=true -out=/tmp/drift.tfplan 2>&1
PLAN_EXIT=$?

if [ $PLAN_EXIT -eq 0 ]; then
  echo "✓ 无配置漂移"
elif [ $PLAN_EXIT -eq 2 ]; then
  echo "! 发现配置漂移，请查看上方 plan 输出"
  echo "  选项 1: 更新 HCL 使其与云上一致"
  echo "  选项 2: terraform apply 将云上资源更新为 HCL 定义的状态"
fi

# Step 2: 检测新增资源（以 ECS 为例）
echo ""
echo "--- Step 2: 检测新增资源 ---"

STATE_ECS=$(terraform state list 2>/dev/null | grep "^alicloud_instance\." | wc -l | tr -d ' ')
CLOUD_ECS=$(aliyun ecs describe-instances --biz-region-id $REGION --page-size 100 \
  --output cols=InstanceId rows=Instances.Instance 2>/dev/null | tail -n +2 | wc -l | tr -d ' ')

echo "State 中 ECS 数量: $STATE_ECS"
echo "云上 ECS 数量: $CLOUD_ECS"

if [ "$CLOUD_ECS" -gt "$STATE_ECS" ]; then
  echo "! 发现 $((CLOUD_ECS - STATE_ECS)) 个未纳管的 ECS 实例"
  echo "  运行检测脚本获取详情..."
fi

# Step 3: 检测删除资源
echo ""
echo "--- Step 3: 检测删除资源 ---"
echo "检查 state 中的资源是否仍存在..."
# （此处调用上面的存在性检查逻辑）

echo ""
echo "=== 同步完成 ==="
```

---

## 定期同步建议

### 手动定期检查

建议每周执行一次增量同步检查：

```bash
# 每周一上午 9 点检查
# 在 crontab 中添加（crontab -e）：
# 0 9 * * 1 cd /path/to/terraform && bash incremental-sync.sh >> /var/log/tf-sync.log 2>&1
```

### 与 CI/CD 集成

**GitHub Actions 示例**：

```yaml
# .github/workflows/tf-drift-check.yml
name: Terraform Drift Check

on:
  schedule:
    - cron: '0 9 * * 1'  # 每周一 9 点
  workflow_dispatch:

jobs:
  drift-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "~1.9"

      - name: Terraform Init
        run: terraform init
        env:
          ALICLOUD_ACCESS_KEY_ID: ${{ secrets.ALICLOUD_AK }}
          ALICLOUD_SECRET_ACCESS_KEY: ${{ secrets.ALICLOUD_SK }}

      - name: Check Drift
        run: terraform plan -refresh=true -detailed-exitcode
        env:
          ALICLOUD_ACCESS_KEY_ID: ${{ secrets.ALICLOUD_AK }}
          ALICLOUD_SECRET_ACCESS_KEY: ${{ secrets.ALICLOUD_SK }}
        continue-on-error: true

      - name: Notify on Drift
        if: steps.drift.outcome == 'failure'
        run: echo "发现配置漂移，请检查 plan 输出"
        # 可替换为发送钉钉/飞书/Slack 通知
```

---

## 同步策略选择

| 策略 | 适用场景 | 风险 |
|------|---------|------|
| 只检测，不自动修复 | 生产环境，需要人工审核 | 低 |
| 自动 import 新增资源 | 资源变化频繁，团队规范好 | 中 |
| 自动 apply 漂移修复 | 严格 IaC 管理，禁止手动操作 | 高 |

**推荐**：生产环境使用"只检测"策略，通过 PR 审核后再手动执行 import/apply。

---

## 常见增量同步问题

**Q: terraform plan 显示大量变更，但资源没有实际变化**

原因：provider 版本升级导致属性默认值变化

处理：
```bash
# 锁定 provider 版本
terraform providers lock
# 或在 required_providers 中指定精确版本
version = "= 1.220.0"
```

**Q: 新增资源 import 后 plan 仍有 diff**

原因：HCL 模板与实际资源配置不完全一致

处理：参考 `references/terraform-patterns.md` 中的 diff 修复模式

**Q: 大量资源需要 import，手动执行太慢**

处理：使用 `examples/import-commands.sh` 中的批量脚本


path: references/ram-policies.md
content: # RAM 权限说明

本 Skill 所需权限分为**通用权限**和**按需权限**两部分。通用权限始终需要；按需权限根据用户实际导入的资源类型选择，只导入哪些产品的资源就只需要对应产品的权限。

---

## 快捷方案

使用系统策略 `ReadOnlyAccess`（只读访问所有资源）可覆盖本 Skill 的全部 API 调用。若需精细控制，按以下列表创建自定义策略。

---

## 通用权限（始终需要）

### STS（身份验证）

| Action | 用途 |
|--------|------|
| `sts:GetCallerIdentity` | Phase 2 验证当前凭证身份 |

### ResourceCenter（资源中心，可选但推荐）

缺失时 Skill 自动降级使用原生产品 API，不影响核心导入流程。

| Action | 用途 |
|--------|------|
| `resourcecenter:ListResourceTypes` | 查询支持的资源类型 |
| `resourcecenter:SearchResources` | 搜索资源（Phase 4 资源发现） |
| `resourcecenter:ListResourceRelationships` | 查询资源关系（Phase 5 依赖图） |

### IaCService（可选）

缺失时 Skill 使用静态映射表，不影响核心导入流程。

| Action | 用途 |
|--------|------|
| `iacservice:ListResourceTypes` | 建立 Terraform 资源类型映射表 |

---

## 按需权限（根据导入的资源类型选择）

仅需为实际要导入的产品授权。例如只导入 ECS 实例，则只需 ECS 和 VPC（网络依赖）的权限。

### VPC（网络）

| Action | 用途 |
|--------|------|
| `vpc:DescribeVpcs` | 发现和获取 VPC 详情 |
| `vpc:DescribeVSwitches` | 发现和获取 VSwitch 详情 |
| `vpc:DescribeEipAddresses` | 发现和获取 EIP 详情 |
| `vpc:DescribeNatGateways` | 发现和获取 NAT 网关详情 |
| `vpc:DescribeSnatTableEntries` | 获取 SNAT 条目 |
| `vpc:DescribeRouteTableList` | 获取路由表 |
| `vpc:DescribeVpnGateways` | 发现 VPN 网关 |

### ECS（计算）

| Action | 用途 |
|--------|------|
| `ecs:DescribeInstances` | 发现和获取 ECS 实例详情 |
| `ecs:DescribeSecurityGroups` | 发现安全组 |
| `ecs:DescribeSecurityGroupAttribute` | 获取安全组规则 |
| `ecs:DescribeDisks` | 发现和获取云盘详情 |
| `ecs:DescribeImages` | 获取自定义镜像 |
| `ecs:DescribeKeyPairs` | 获取密钥对 |

### OSS（存储）

| Action | 用途 |
|--------|------|
| `oss:ListBuckets` | 发现 OSS Bucket |
| `oss:GetBucketInfo` | 获取 Bucket 详情 |
| `oss:GetBucketAcl` | 获取 Bucket ACL |
| `oss:GetBucketVersioning` | 获取版本控制配置 |
| `oss:GetBucketLifecycle` | 获取生命周期规则 |
| `oss:GetBucketCors` | 获取 CORS 配置 |
| `oss:GetBucketEncryption` | 获取加密配置 |

### RDS（关系型数据库）

| Action | 用途 |
|--------|------|
| `rds:DescribeDBInstances` | 发现 RDS 实例 |
| `rds:DescribeDBInstanceAttribute` | 获取实例详情 |
| `rds:DescribeDatabases` | 获取数据库列表 |
| `rds:DescribeAccounts` | 获取账号列表 |
| `rds:DescribeDBInstanceIPArrayList` | 获取白名单 |
| `rds:DescribeDBInstanceNetInfo` | 获取连接地址 |

### KVStore（Redis）

| Action | 用途 |
|--------|------|
| `r-kvstore:DescribeInstances` | 发现 Redis 实例 |
| `r-kvstore:DescribeInstanceAttribute` | 获取实例详情 |
| `r-kvstore:DescribeSecurityIps` | 获取白名单 |

### DDS（MongoDB）

| Action | 用途 |
|--------|------|
| `dds:DescribeDBInstances` | 发现 MongoDB 实例 |
| `dds:DescribeDBInstanceAttribute` | 获取实例详情 |
| `dds:DescribeSecurityIps` | 获取白名单 |

### SLB（传统负载均衡）

| Action | 用途 |
|--------|------|
| `slb:DescribeLoadBalancers` | 发现 SLB 实例 |
| `slb:DescribeLoadBalancerAttribute` | 获取实例详情 |
| `slb:DescribeLoadBalancerListeners` | 获取监听列表 |
| `slb:DescribeHealthStatus` | 获取后端健康状态 |

### ALB（应用型负载均衡）

| Action | 用途 |
|--------|------|
| `alb:ListLoadBalancers` | 发现 ALB 实例 |
| `alb:ListListeners` | 获取监听列表 |
| `alb:ListServerGroups` | 获取服务器组 |

### Alidns（云解析）

| Action | 用途 |
|--------|------|
| `alidns:DescribeDomains` | 发现域名 |
| `alidns:DescribeDomainRecords` | 获取解析记录 |
| `alidns:DescribeDomainGroups` | 获取域名分组 |

### RAM（访问控制）

| Action | 用途 |
|--------|------|
| `ram:ListUsers` | 发现 RAM 用户 |
| `ram:ListRoles` | 发现 RAM 角色 |
| `ram:ListPolicies` | 发现自定义策略 |
| `ram:ListPoliciesForUser` | 获取用户绑定策略 |

### KMS（密钥管理）

| Action | 用途 |
|--------|------|
| `kms:ListKeys` | 发现 KMS 密钥 |
| `kms:DescribeKey` | 获取密钥详情 |

### NAS（文件存储）

| Action | 用途 |
|--------|------|
| `nas:DescribeFileSystems` | 发现文件系统 |
| `nas:DescribeMountTargets` | 获取挂载点 |

### CS（容器服务 ACK）

| Action | 用途 |
|--------|------|
| `cs:DescribeClusters` | 发现 ACK 集群 |
| `cs:DescribeClusterNodePools` | 获取节点池 |

### FC（函数计算）

| Action | 用途 |
|--------|------|
| `fc:ListServices` | 发现 FC 服务 |
| `fc:ListFunctions` | 获取函数列表 |

### ESS（弹性伸缩）

| Action | 用途 |
|--------|------|
| `ess:DescribeScalingGroups` | 发现伸缩组 |
| `ess:DescribeScalingConfigurations` | 获取伸缩配置 |

---

## 自定义策略示例（仅导入 ECS + VPC）

```json
{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "sts:GetCallerIdentity",
        "resourcecenter:ListResourceTypes",
        "resourcecenter:SearchResources",
        "resourcecenter:ListResourceRelationships",
        "iacservice:ListResourceTypes",
        "vpc:DescribeVpcs",
        "vpc:DescribeVSwitches",
        "vpc:DescribeEipAddresses",
        "vpc:DescribeNatGateways",
        "vpc:DescribeRouteTableList",
        "ecs:DescribeInstances",
        "ecs:DescribeSecurityGroups",
        "ecs:DescribeSecurityGroupAttribute",
        "ecs:DescribeDisks"
      ],
      "Resource": "*"
    }
  ]
}
```

---

## 注意事项

- 所有权限均为**只读**（Describe/List/Get），不涉及资源创建、修改或删除
- `terraform import` 命令本身只读取云上资源状态写入本地 state，不修改云上资源
- 若使用 `ReadOnlyAccess` 系统策略，以上全部权限已包含
- 按需权限中，网络类资源（VPC）通常作为其他资源的依赖被一并导入，建议默认授权


path: references/resource-hub-api.md
content: # ResourceCenter 接口使用指南

四个接口构成完整的资源发现和关系构建链路，原生产品 API 作为降级兜底。

API 文档入口：`https://api.aliyun.com/api/ResourceCenter/2022-12-01/`

---

## 前置接口：IaCService ListResourceTypes

**在 Phase 3 初始化阶段调用一次，建立 `terraformResourceType ↔ ResourceCode` 映射表，缓存到 `.import/resource-type-mapping.json`，供全程使用。**

```bash
aliyun iacservice list-resource-types \
  --endpoint iacservice.aliyuncs.com \
  --status Available \
  --maxResults 200
```

若结果超过 200 条，使用 `nextToken` 分页获取全量数据。

**关键返回字段：**

```json
{
  "resourceTypes": [
    {
      "terraformResourceType": "alicloud_eip_address",
      "resourceType": "ALIYUN::EIP::Address",
      "product": "EIP",
      "supportTerraformer": true,
      "status": "Available"
    }
  ]
}
```

**映射规则：**

`resourceType` 字段将 `ALIYUN` 前缀替换为 `ACS`，即得到 ResourceCenter 接口所需的 ResourceCode：

```
ALIYUN::EIP::Address  →  ACS::EIP::Address
ALIYUN::VPC::VPC      →  ACS::VPC::VPC
ALIYUN::ECS::Instance →  ACS::ECS::Instance
```

**映射表结构（`.import/resource-type-mapping.json`）：**

```json
{
  "byTerraformType": {
    "alicloud_eip_address": {
      "resourceCode": "ACS::EIP::Address",
      "product": "EIP",
      "supportTerraformer": true
    },
    "alicloud_vpc": {
      "resourceCode": "ACS::VPC::VPC",
      "product": "VPC",
      "supportTerraformer": true
    }
  },
  "byResourceCode": {
    "ACS::EIP::Address": "alicloud_eip_address",
    "ACS::VPC::VPC": "alicloud_vpc"
  }
}
```

**使用方式：**
- Phase 4：已知 `terraformResourceType` → 查 `byTerraformType` → 得到 `resourceCode` → 调用 search-resources
- Phase 6：ResourceCenter 返回的 `ResourceType` → 查 `byResourceCode` → 得到 `terraformResourceType` → 生成正确的 HCL 资源类型

**降级条件：**
- 调用失败或权限不足 → 降级使用本文件末尾的静态映射表
- 映射表中找不到对应关系 → 跳过接口 A/B，直接使用原生产品 API

---

## 接口一：list-resource-types

查询 ResourceCenter 支持的资源类型，用于判断接口 A（search-resources）和接口 B（list-resource-relationships）各自的支持范围。

**在 Phase 4 开始前调用一次，结果缓存供全程使用。**

```bash
aliyun resourcecenter list-resource-types --endpoint resourcecenter.aliyuncs.com
```

**关键返回字段：**

```json
{
  "ResourceTypes": [
    {
      "ResourceType": "ACS::ECS::Instance",
      "SupportedFeatures": ["ListResources", "GetResourceRelationships"]
    }
  ]
}
```

- `SupportedFeatures` 包含 `ListResources` → 该资源类型支持接口 A（search-resources）
- `SupportedFeatures` 包含 `GetResourceRelationships` → 该资源类型支持接口 B（list-resource-relationships）

**降级条件：**
- 调用返回权限错误（`AccessDenied`）→ 全程降级，跳过接口 A 和接口 B
- 调用超时或返回空列表 → 同上

---

## 接口 A：search-resources

搜索当前账号下有权限访问的资源。**返回的是资源基础元数据，不包含资源的业务属性**（如 VPC 的 CidrBlock、ECS 的 InstanceType 等）。Phase 6 生成 HCL 时仍需调用原生产品 API 获取完整属性，search-resources 仅用于 Phase 4 资源发现阶段。

**基本用法：**
```bash
aliyun resourcecenter search-resources \
  --filter '[{"Key":"ResourceType","MatchType":"Equals","Values":["ACS::ECS::Instance"]}]' \
  --max-results 50
```

**支持的过滤条件：**

| 过滤参数 | 支持的匹配方式 |
|---------|-------------|
| `ResourceType` | Equals |
| `RegionId` | Equals |
| `ResourceId` | Equals、Prefix |
| `ResourceName` | Equals、Contains |
| `Tag` | Contains、NotContains、NotExists（JSON 格式：`{"key":"k","value":"v"}`）|
| `VpcId` | Equals |
| `VSwitchId` | Equals |
| `IpAddress` | Equals、Contains |
| `ResourceGroupId` | Equals、Exists、NotExists |

多个过滤条件之间是 AND 关系，同一条件内多个值是 OR 关系。

**按 Region + 资源类型过滤：**
```bash
aliyun resourcecenter search-resources \
  --filter '[{"Key":"ResourceType","MatchType":"Equals","Values":["ACS::ECS::Instance"]},{"Key":"RegionId","MatchType":"Equals","Values":["cn-hangzhou"]}]' \
  --max-results 50
```

**按资源 ID 查询（用于确认资源类型）：**
```bash
aliyun resourcecenter search-resources \
  --filter '[{"Key":"ResourceId","MatchType":"Equals","Values":["vpc-bp1xxx"]}]' \
  --max-results 1
```

**分页：**
```bash
aliyun resourcecenter search-resources \
  --filter '[...]' \
  --max-results 50 \
  --next-token <上一页返回的 NextToken>
```
返回结果中不存在 `NextToken` 时表示没有更多数据，MaxResults 最大值为 500。

**返回字段：**

```json
{
  "Resources": [
    {
      "AccountId": "151266687691****",
      "ResourceId": "vtb-bp1xxx",
      "ResourceType": "ACS::VPC::RouteTable",
      "ResourceName": "group1",
      "RegionId": "cn-hangzhou",
      "ZoneId": "cn-hangzhou-k",
      "ResourceGroupId": "rg-acfmzawhxxc****",
      "CreateTime": "2021-06-30T09:20:08Z",
      "ExpireTime": "2021-07-30T09:20:08Z",
      "IpAddresses": ["192.168.1.2"],
      "IpAddressAttributes": [
        {"IpAddress": "192.168.1.2", "NetworkType": "Public", "Version": "Ipv4"}
      ],
      "Tags": [{"Key": "test_key", "Value": "test_value"}],
      "Deleted": false
    }
  ],
  "NextToken": "eyJzZWFyY2hBZnRlcnMiOlsiMTAwMTU2Nzk4MTU1OSJd****"
}
```

注意：`ZoneId`、`CreateTime`、`IpAddresses` 等字段是否返回由各云服务决定，不保证所有资源类型都有。

**资源类型格式（`ACS::<Product>::<ResourceType>`）：**

| 云产品 | ResourceCenter 类型 |
|--------|-------------------|
| VPC | `ACS::VPC::VPC` |
| VSwitch | `ACS::VPC::VSwitch` |
| 路由表 | `ACS::VPC::RouteTable` |
| EIP | `ACS::VPC::EipAddress` |
完整列表通过 `iacservice list-resource-types` 动态获取，以下为静态兜底映射表：

| Terraform 资源类型 | ResourceCode |
|-------------------|-------------|
| `alicloud_vpc` | `ACS::VPC::VPC` |
| `alicloud_vswitch` | `ACS::VPC::VSwitch` |
| `alicloud_route_table` | `ACS::VPC::RouteTable` |
| `alicloud_eip_address` | `ACS::EIP::Address` |
| `alicloud_nat_gateway` | `ACS::VPC::NatGateway` |
| `alicloud_instance` | `ACS::ECS::Instance` |
| `alicloud_security_group` | `ACS::ECS::SecurityGroup` |
| `alicloud_db_instance` | `ACS::RDS::DBInstance` |
| `alicloud_kvstore_instance` | `ACS::KVStore::Instance` |
| `alicloud_mongodb_instance` | `ACS::DDS::DBInstance` |
| `alicloud_oss_bucket` | `ACS::OSS::Bucket` |
| `alicloud_slb_load_balancer` | `ACS::SLB::LoadBalancer` |
| `alicloud_cs_managed_kubernetes` | `ACS::CS::KubernetesCluster` |

**降级条件：**
- 资源类型不在 `list-resource-types` 返回的支持列表中
- 调用返回 `NoPermission` 或其他错误
- 以上情况降级到 `references/api-commands.md` 的原生产品 API

---

## 接口 B：list-resource-relationships

查询指定资源与其关联资源的关系，用于动态构建资源依赖图。**`RegionId`、`ResourceType`、`ResourceId` 三个参数均为必填。**

返回结果是一个扁平列表，每条记录表示"当前资源"与"某个相关资源"之间存在关联，**不区分方向**（不区分父子或横向关联），由调用方根据业务语义判断依赖方向。

**基本用法：**
```bash
aliyun resourcecenter list-resource-relationships \
  --region-id cn-hangzhou \
  --resource-type ACS::VPC::VPC \
  --resource-id vpc-bp1xxx
```

**按相关资源类型过滤（只查 VSwitch）：**
```bash
aliyun resourcecenter list-resource-relationships \
  --region-id cn-hangzhou \
  --resource-type ACS::VPC::VPC \
  --resource-id vpc-bp1xxx \
  --related-resource-filter '[{"Key":"RelatedResourceType","MatchType":"Equals","Value":["ACS::VPC::VSwitch"]}]'
```

**分页：**
```bash
aliyun resourcecenter list-resource-relationships \
  --region-id cn-hangzhou \
  --resource-type ACS::VPC::VPC \
  --resource-id vpc-bp1xxx \
  --max-results 50 \
  --next-token <上一页返回的 NextToken>
```
MaxResults 取值范围 1~500，默认 20。

**支持的 RelatedResourceFilter 过滤参数：**

| 参数 | 描述 | 支持的匹配方式 |
|------|------|-------------|
| `RelatedResourceRegionId` | 相关资源地域 ID | Equals |
| `RelatedResourceType` | 相关资源类型 | Equals |
| `RelatedResourceId` | 相关资源 ID | Equals |

**返回字段：**

```json
{
  "ResourceRelationships": [
    {
      "RegionId": "cn-hangzhou",
      "ResourceType": "ACS::ACK::Cluster",
      "ResourceId": "m-eb3hji****",
      "RelatedResourceRegionId": "cn-shanghai",
      "RelatedResourceType": "ACS::VPC::VPC",
      "RelatedResourceId": "vpc-uf6m5okksddm6c9lh7***"
    }
  ],
  "NextToken": "eyJzZWFyY2hBZnRlcnMiOlsiMTAwMTU2Nzk4MTU1OSJd****"
}
```

注意：相关资源可能在不同 Region（`RelatedResourceRegionId` 与请求的 `RegionId` 不同），跨 Region 关联时需注意。

**Phase 5 使用方式：**

1. 从 Phase 4 发现的资源中识别根资源（VPC、OSS Bucket、DNS 域名、RAM 用户等无父依赖的资源）
2. 对每个根资源调用接口 B，获取其所有关联资源
3. 对关联资源递归调用接口 B，直到没有新资源出现，构建完整关系图
4. 依赖方向由业务语义判断：VPC → VSwitch（VSwitch 依赖 VPC），VSwitch → ECS（ECS 依赖 VSwitch）等，参考 `references/dependency-rules.md`
5. 对关系图做拓扑排序，生成导入顺序

**Phase 6 使用方式：**

通过接口 B 查询到的关联资源，其依赖关系已确定。结合 Phase 3 提取的引用字段白名单，找到当前资源中对应的 `_id` 字段，直接替换为 terraform resource address，无需猜测。

**降级条件：**
- 资源类型不在 `list-resource-types` 返回的支持列表中
- 调用返回 `NoPermission` 或其他错误
- 以上情况降级到 `references/dependency-rules.md` 的静态规则


path: references/resource-types.md
content: # 特殊 import ID 格式速查

仅收录 import ID 格式复杂、容易出错的资源。其余资源的 import 格式直接查 provider 文档：
https://registry.terraform.io/providers/aliyun/alicloud/latest/docs

---

## 复合 ID 格式（用 `:` 分隔）

| 资源 | import ID 格式 | 说明 |
|------|---------------|------|
| `alicloud_route_entry` | `<route-table-id>:<cidr>:<nexthop-type>:<nexthop-id>` | 4 段 |
| `alicloud_security_group_rule` | `<sg-id>:<direction>:<ip-protocol>:<port-range>:<priority>:<cidr-ip>:<policy>` | 7 段，顺序不能错 |
| `alicloud_eip_association` | `<eip-id>:<instance-id>` | |
| `alicloud_snat_entry` | `<snat-table-id>:<snat-entry-id>` | snat-table-id 从 NAT 网关属性获取 |
| `alicloud_forward_entry` | `<forward-table-id>:<forward-entry-id>` | forward-table-id 从 NAT 网关属性获取 |
| `alicloud_disk_attachment` | `<disk-id>:<instance-id>` | |
| `alicloud_nas_mount_target` | `<fs-id>:<mount-target-domain>` | domain 是完整域名，非 ID |
| `alicloud_db_database` | `<db-instance-id>:<db-name>` | |
| `alicloud_db_account` | `<db-instance-id>:<account-name>` | |
| `alicloud_db_connection` | `<db-instance-id>:<connection-prefix>` | |
| `alicloud_polardb_database` | `<cluster-id>:<db-name>` | |
| `alicloud_ram_policy` | `<policy-name>:<policy-type>` | policy-type: `Custom` 或 `System` |
| `alicloud_ram_user_policy_attachment` | `<user-name>:<policy-name>:<policy-type>` | |
| `alicloud_slb_listener` | `<slb-id>:<protocol>_<port>` | 注意是下划线，如 `lb-xxx:http_80` |
| `alicloud_cs_kubernetes_node_pool` | `<cluster-id>:<nodepool-id>` | |
| `alicloud_fc_function` | `<service-name>:<function-name>` | |
| `alicloud_kms_alias` | `alias/<alias-name>` | 需带 `alias/` 前缀 |

---

## 非 ID 字段作为 import 标识

| 资源 | import 标识 | 说明 |
|------|------------|------|
| `alicloud_oss_bucket` | bucket 名称 | 不含 `oss://` 前缀 |
| `alicloud_oss_bucket_acl` | bucket 名称 | 同上 |
| `alicloud_oss_bucket_policy` | bucket 名称 | 同上 |
| `alicloud_oss_bucket_cors` | bucket 名称 | 同上 |
| `alicloud_dns_domain` | 域名字符串 | 如 `example.com` |
| `alicloud_ram_user` | 用户名 | 非 UID |
| `alicloud_ram_role` | 角色名 | 非 ARN |
| `alicloud_key_pair` | 密钥对名称 | 非 ID |
| `alicloud_fc_service` | 服务名称 | 非 ID |

---

## 需要从父资源属性获取的 ID

| 资源 | 如何获取 import 所需 ID |
|------|------------------------|
| `alicloud_snat_entry` | `snat_table_id` 从 `alicloud_nat_gateway.<name>.snat_table_ids` 获取（注意是逗号分隔的字符串，取第一个） |
| `alicloud_forward_entry` | `forward_table_id` 从 `alicloud_nat_gateway.<name>.forward_table_ids` 获取 |

```bash
# 获取 NAT 网关的 snat_table_id
aliyun vpc describe-nat-gateways --biz-region-id cn-hangzhou --nat-gateway-id ngw-xxx \
  | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['NatGateways']['NatGateway'][0]['SnatTableIds']['SnatTableId'][0])"
```

---

## 密码/密钥字段处理

import 后以下字段无法从 API 读取，需在 HCL 中处理：

```hcl
# 方式 1：用变量
resource "alicloud_db_instance" "xxx" {
  # ...
}
variable "rds_password" { sensitive = true }

# 方式 2：ignore_changes（推荐，避免 plan diff）
lifecycle {
  ignore_changes = [password, account_password]
}
```

涉及资源：RDS（`password`）、Redis（`password`）、MongoDB（`account_password`）、ECS（`password`）


path: references/state-management.md
content: # Terraform State 文件操作参考

---

## 常用 state 命令

```bash
# 列出 state 中所有资源
terraform state list

# 按类型过滤
terraform state list | grep alicloud_instance
terraform state list | grep alicloud_vpc

# 查看单个资源的详细属性
terraform state show alicloud_instance.ecs_web_01

# 查看完整 state（JSON 格式）
terraform show -json

# 从 state 移除资源（不删除云上资源）
terraform state rm alicloud_instance.ecs_web_01

# 重命名 state 中的资源
terraform state mv alicloud_instance.old_name alicloud_instance.new_name

# 手动拉取远程 state
terraform state pull > backup.tfstate

# 手动推送 state（危险操作，谨慎使用）
terraform state push backup.tfstate

# 强制解锁 state（state 被锁定时）
terraform force-unlock <lock-id>
```

---

## State 文件 JSON 结构

```json
{
  "version": 4,
  "terraform_version": "1.9.0",
  "serial": 42,
  "lineage": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "alicloud_vpc",
      "name": "vpc_prod_main",
      "provider": "provider[\"registry.terraform.io/aliyun/alicloud\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "id": "vpc-bp1xxxxxxxxxxxxxxx",
            "vpc_name": "prod-main",
            "cidr_block": "10.0.0.0/8",
            "status": "Available",
            "tags": {
              "Env": "production"
            }
          },
          "sensitive_attributes": [],
          "dependencies": []
        }
      ]
    }
  ]
}
```

关键字段：
- `resources[].type`：Terraform 资源类型
- `resources[].name`：资源名称（对应 HCL 中的标识符）
- `resources[].instances[].attributes`：资源的所有属性值
- `resources[].instances[].dependencies`：依赖的其他资源地址列表

---

## 从 State 提取依赖关系

```bash
# 方法 1：使用 terraform graph（需要 graphviz）
terraform graph | dot -Tsvg > graph.svg

# 方法 2：从 JSON state 提取（Python）
terraform show -json | python3 -c "
import json, sys
state = json.load(sys.stdin)
resources = state.get('values', {}).get('root_module', {}).get('resources', [])
print(f'总资源数: {len(resources)}')
print()
for r in resources:
    addr = r['address']
    deps = r.get('depends_on', [])
    if deps:
        for d in deps:
            print(f'{d} --> {addr}')
    else:
        print(f'[root] --> {addr}')
"

# 方法 3：列出所有资源及其 ID
terraform show -json | python3 -c "
import json, sys
state = json.load(sys.stdin)
resources = state.get('values', {}).get('root_module', {}).get('resources', [])
for r in resources:
    rid = r.get('values', {}).get('id', 'N/A')
    print(f'{r[\"address\"]}: {rid}')
"
```

---

## 检查资源是否已在 State 中

```bash
# 检查特定资源是否存在
terraform state list | grep "alicloud_instance.ecs_web_01"
# 返回非空则已存在，返回空则不存在

# 批量检查（用于幂等性 import）
check_in_state() {
  local resource="$1"
  terraform state list | grep -q "^${resource}$"
  return $?
}

if check_in_state "alicloud_vpc.vpc_prod_main"; then
  echo "已在 state 中，跳过 import"
else
  terraform import alicloud_vpc.vpc_prod_main vpc-bp1xxx
fi
```

---

## State 备份与恢复

```bash
# 导入前备份 state
cp terraform.tfstate terraform.tfstate.backup.$(date +%Y%m%d_%H%M%S)

# 或使用 terraform state pull
terraform state pull > backup_$(date +%Y%m%d_%H%M%S).tfstate

# 恢复 state（谨慎操作）
cp terraform.tfstate.backup terraform.tfstate
# 或
terraform state push backup.tfstate
```

---

## 远程 State（OSS Backend）

```hcl
# backend.tf
terraform {
  backend "oss" {
    bucket   = "my-terraform-state"
    prefix   = "alicloud-import"
    key      = "terraform.tfstate"
    region   = "cn-hangzhou"
    endpoint = "oss-cn-hangzhou.aliyuncs.com"
  }
}
```

迁移到远程 state：
```bash
# 1. 添加 backend 配置到 backend.tf
# 2. 执行迁移
terraform init -migrate-state
# 3. 确认迁移
```

---

## 常见 State 问题处理

**问题 1：import 后 state 与 HCL 不一致**
```bash
# 查看 state 中的实际属性值
terraform state show alicloud_instance.ecs_web_01
# 根据输出更新 HCL，使两者一致
```

**问题 2：资源被手动删除，state 中仍存在**
```bash
# 从 state 移除（不会删除云上资源，因为已不存在）
terraform state rm alicloud_instance.ecs_web_01
```

**问题 3：资源 ID 变更（如重建了资源）**
```bash
# 先移除旧 state
terraform state rm alicloud_instance.ecs_web_01
# 再 import 新 ID
terraform import alicloud_instance.ecs_web_01 i-bp1new_id
```

**问题 4：State 锁定（多人操作时）**
```bash
# 查看锁信息
terraform state pull | python3 -c "import json,sys; s=json.load(sys.stdin); print(s.get('lock_info','no lock'))"
# 强制解锁（确认没有其他操作在进行）
terraform force-unlock <lock-id>
```


path: references/terraform-patterns.md
content: # Terraform HCL 代码模板库

各资源类型的 HCL 模板，以及常见 plan diff 修复模式。

---

## Provider 配置

```hcl
terraform {
  required_providers {
    alicloud = {
      source  = "aliyun/alicloud"
      version = "~> 1.220.0"
    }
  }
}

provider "alicloud" {
  access_key = var.access_key
  secret_key = var.secret_key
  region     = var.region
}

variable "access_key" { sensitive = true }
variable "secret_key" { sensitive = true }
variable "region"     { default = "cn-hangzhou" }
```

---

## 网络资源模板

### VPC

```hcl
resource "alicloud_vpc" "vpc_<name>" {
  vpc_name    = "<name>"
  cidr_block  = "10.0.0.0/8"
  description = ""
  tags = {
    Env = "production"
  }
}
```

### VSwitch

```hcl
resource "alicloud_vswitch" "vsw_<name>" {
  vswitch_name = "<name>"
  vpc_id       = alicloud_vpc.vpc_<vpc_name>.id
  cidr_block   = "10.0.1.0/24"
  zone_id      = "cn-hangzhou-h"
  description  = ""
  tags = {}
}
```

### 安全组

```hcl
resource "alicloud_security_group" "sg_<name>" {
  security_group_name = "<name>"  # 注意：provider 1.239.0+ 已废弃 name 字段，使用 security_group_name
  vpc_id              = alicloud_vpc.vpc_<vpc_name>.id
  description         = ""
  tags = {}
}

# 入方向规则
resource "alicloud_security_group_rule" "sgr_<name>_ingress_80" {
  type              = "ingress"
  ip_protocol       = "tcp"
  port_range        = "80/80"
  security_group_id = alicloud_security_group.sg_<name>.id
  cidr_ip           = "0.0.0.0/0"
  policy            = "accept"
  priority          = 1
}

# 出方向规则（通常允许所有出流量）
resource "alicloud_security_group_rule" "sgr_<name>_egress_all" {
  type              = "egress"
  ip_protocol       = "all"
  port_range        = "-1/-1"
  security_group_id = alicloud_security_group.sg_<name>.id
  cidr_ip           = "0.0.0.0/0"
  policy            = "accept"
  priority          = 1
}
```

### EIP

```hcl
resource "alicloud_eip_address" "eip_<name>" {
  address_name         = "<name>"
  payment_type         = "PayAsYouGo"
  internet_charge_type = "PayByTraffic"
  bandwidth            = "100"
  isp                  = "BGP"
  tags = {}
}

resource "alicloud_eip_association" "eip_assoc_<name>" {
  allocation_id = alicloud_eip_address.eip_<name>.id
  instance_id   = alicloud_instance.ecs_<name>.id
  instance_type = "EcsInstance"
}
```

### NAT 网关

```hcl
resource "alicloud_nat_gateway" "nat_<name>" {
  vpc_id           = alicloud_vpc.vpc_<vpc_name>.id
  vswitch_id       = alicloud_vswitch.vsw_<vsw_name>.id
  nat_gateway_name = "<name>"
  nat_type         = "Enhanced"
  payment_type     = "PayAsYouGo"
  tags = {}
}

resource "alicloud_snat_entry" "snat_<name>" {
  snat_table_id     = alicloud_nat_gateway.nat_<name>.snat_table_ids
  source_vswitch_id = alicloud_vswitch.vsw_<vsw_name>.id
  snat_ip           = alicloud_eip_address.eip_<name>.ip_address
}
```

---

## 计算资源模板

### ECS 实例

```hcl
resource "alicloud_instance" "ecs_<name>" {
  instance_name        = "<name>"
  instance_type        = "ecs.c6.large"
  image_id             = "aliyun_3_x64_20G_alibase_20240528.vhd"
  system_disk_category = "cloud_essd"
  system_disk_size     = 40

  vswitch_id         = alicloud_vswitch.vsw_<vsw_name>.id
  security_groups    = [alicloud_security_group.sg_<name>.id]
  availability_zone  = "cn-hangzhou-h"

  internet_max_bandwidth_out = 0
  internet_charge_type       = "PayByTraffic"

  key_name     = "<key-pair-name>"
  [REDACTED_SECRET]  # 或用 key_name，不要硬编码密码

  description  = ""
  host_name    = "<hostname>"

  tags = {
    Env = "production"
  }

  lifecycle {
    ignore_changes = [image_id, password]
  }
}

variable "ecs_password" {
  sensitive = true
  default   = ""
}
```

### 云盘

```hcl
resource "alicloud_disk" "disk_<name>" {
  disk_name         = "<name>"
  availability_zone = "cn-hangzhou-h"
  category          = "cloud_essd"
  size              = 100
  performance_level = "PL1"
  description       = ""
  tags = {}
}

resource "alicloud_disk_attachment" "disk_attach_<name>" {
  disk_id     = alicloud_disk.disk_<name>.id
  instance_id = alicloud_instance.ecs_<name>.id
}
```

---

## 存储资源模板

### OSS Bucket

```hcl
resource "alicloud_oss_bucket" "bucket_<name>" {
  bucket = "<bucket-name>"

  acl = "private"  # private / public-read / public-read-write

  versioning {
    status = "Enabled"  # Enabled / Suspended
  }

  lifecycle_rule {
    id      = "rule-1"
    enabled = true
    prefix  = "logs/"

    expiration {
      days = 30
    }
  }

  cors_rule {
    allowed_headers = ["*"]
    allowed_methods = ["GET", "POST"]
    allowed_origins = ["https://example.com"]
    max_age_seconds = 3000
  }

  server_side_encryption_rule {
    sse_algorithm = "AES256"
  }

  tags = {
    Env = "production"
  }
}
```

---

## 数据库资源模板

### RDS MySQL

```hcl
resource "alicloud_db_instance" "rds_<name>" {
  engine           = "MySQL"
  engine_version   = "8.0"
  instance_type    = "rds.mysql.s2.large"
  instance_storage = 20
  instance_name    = "<name>"
  vswitch_id       = alicloud_vswitch.vsw_<vsw_name>.id
  security_ips     = ["10.0.0.0/8"]
  payment_type     = "Postpaid"

  tags = {}

  lifecycle {
    ignore_changes = [instance_storage]
  }
}

resource "alicloud_db_database" "db_<name>" {
  instance_id   = alicloud_db_instance.rds_<name>.id
  name          = "<db-name>"
  character_set = "utf8mb4"
  description   = ""
}

resource "alicloud_db_account" "dba_<name>" {
  db_instance_id   = alicloud_db_instance.rds_<name>.id
  account_name     = "<username>"
  account_password = var.rds_password
  account_type     = "Normal"  # Normal / Super

  lifecycle {
    ignore_changes = [account_password]
  }
}

variable "rds_password" { sensitive = true }
```

### Redis（KVStore）

```hcl
resource "alicloud_kvstore_instance" "redis_<name>" {
  db_instance_class = "redis.master.small.default"
  instance_name     = "<name>"
  vswitch_id        = alicloud_vswitch.vsw_<vsw_name>.id
  engine_version    = "7.0"
  instance_type     = "Redis"
  payment_type      = "PostPaid"
  security_ips      = ["10.0.0.0/8"]

  tags = {}

  lifecycle {
    ignore_changes = [password]
  }
}
```

### MongoDB（DDS）

```hcl
resource "alicloud_mongodb_instance" "mongo_<name>" {
  engine_version      = "6.0"
  db_instance_class   = "dds.mongo.mid"
  db_instance_storage = 10
  name                = "<name>"
  vswitch_id          = alicloud_vswitch.vsw_<vsw_name>.id
  security_ip_list    = ["10.0.0.0/8"]
  payment_type        = "PostPaid"

  tags = {}

  lifecycle {
    ignore_changes = [account_password]
  }
}
```

---

## 负载均衡模板

### SLB

```hcl
resource "alicloud_slb_load_balancer" "slb_<name>" {
  load_balancer_name = "<name>"
  load_balancer_spec = "slb.s2.small"
  vswitch_id         = alicloud_vswitch.vsw_<vsw_name>.id
  payment_type       = "PayAsYouGo"
  address_type       = "intranet"  # intranet / internet

  tags = {}
}

resource "alicloud_slb_listener" "slb_listener_<name>_80" {
  load_balancer_id          = alicloud_slb_load_balancer.slb_<name>.id
  backend_port              = 80
  frontend_port             = 80
  protocol                  = "http"
  bandwidth                 = -1
  sticky_session            = "off"
  health_check              = "on"
  health_check_uri          = "/health"
  health_check_connect_port = 80
}

resource "alicloud_slb_attachment" "slb_attach_<name>" {
  load_balancer_id = alicloud_slb_load_balancer.slb_<name>.id
  instance_ids     = [alicloud_instance.ecs_<name>.id]
  weight           = 100
}
```

---

## DNS 模板

```hcl
resource "alicloud_dns_domain" "domain_<name>" {
  domain_name = "example.com"
}

resource "alicloud_dns_record" "record_www_<name>" {
  name        = "example.com"
  host_record = "www"
  type        = "A"
  value       = "1.2.3.4"
  ttl         = 600
  priority    = 0  # 仅 MX 记录需要
}
```

---

## 常见 plan diff 修复模式

### 1. 标签格式不匹配

**现象**：`tags` 字段显示有变更，但值看起来一样

**原因**：阿里云 API 返回的 tag key/value 大小写或格式与 HCL 不一致

**修复**：
```hcl
# 从 terraform show 查看实际 state 中的 tags 格式，严格匹配
tags = {
  "Env"  = "production"   # 注意大小写
  "Team" = "infra"
}
```

### 2. 密码字段无法读取

**现象**：`password` 字段显示 `(known after apply)` 或有 diff

**修复**：
```hcl
lifecycle {
  ignore_changes = [password, account_password]
}
```

### 3. 系统盘大小漂移

**现象**：`system_disk_size` 显示有变更

**修复**：
```hcl
lifecycle {
  ignore_changes = [system_disk_size]
}
```

### 4. 镜像 ID 变更

**现象**：`image_id` 显示有变更（阿里云更新了基础镜像）

**修复**：
```hcl
lifecycle {
  ignore_changes = [image_id]
}
```

### 5. ECS `dry_run` 字段 ✅ 已验证

**现象**：ECS import 后 plan 显示 `+ dry_run = false`

**原因**：`dry_run` 是 provider 内部计算字段，不对应云上实际属性

**修复**：
```hcl
lifecycle {
  ignore_changes = [image_id, password, user_data, dry_run]
}
```

### 5. 安全组规则顺序

**现象**：安全组规则显示有变更，但规则内容相同

**原因**：Terraform 对规则顺序敏感，API 返回顺序可能不同

**修复**：确保 HCL 中规则顺序与 `terraform state show` 输出一致

### 6. RDS 存储大小

**现象**：`instance_storage` 显示有变更

**原因**：RDS 存储只能扩容不能缩容，实际值可能已被手动扩容

**修复**：
```hcl
lifecycle {
  ignore_changes = [instance_storage]
}
```

### 7. ECS 带宽设置

**现象**：`internet_max_bandwidth_out` 显示有变更

**修复**：从 `terraform state show` 读取实际值并更新 HCL

### 8. 安全 IP 列表格式

**现象**：`security_ips` 显示有变更

**原因**：API 返回的 IP 列表顺序或格式（CIDR vs IP）与 HCL 不一致

**修复**：
```hcl
security_ips = ["10.0.0.0/8", "172.16.0.0/12"]  # 严格匹配 state 中的格式
```

### 9. OSS Bucket 加密配置

**现象**：`server_side_encryption_rule` 显示有变更

**修复**：若不需要管理加密配置，移除该块；或严格匹配 state 中的值

### 10. NAT 网关 SNAT 表 ID

**现象**：`snat_table_ids` 是计算字段，无法在 HCL 中直接引用

**修复**：
```hcl
# 使用 split 处理多个 SNAT 表 ID
resource "alicloud_snat_entry" "snat_xxx" {
  snat_table_id = split(",", alicloud_nat_gateway.nat_xxx.snat_table_ids)[0]
  ...
}
```

### 11. ECS 实例 userData

**现象**：`user_data` 显示有变更

**修复**：
```hcl
lifecycle {
  ignore_changes = [user_data]
}
```

### 12. SLB 监听健康检查默认值

**现象**：健康检查相关字段显示有变更

**修复**：从 `terraform state show` 读取所有健康检查字段的实际值，在 HCL 中显式设置

### 13. VSwitch 可用区 ID 格式

**现象**：`zone_id` 显示有变更

**原因**：API 返回的可用区 ID 格式可能是 `cn-hangzhou-h` 或 `cn-hangzhou-h`（带后缀）

**修复**：严格使用 `terraform state show` 中的 zone_id 值

### 14. Redis 实例规格

**现象**：`db_instance_class` 显示有变更

**原因**：API 返回的规格名称可能与 Terraform provider 期望的格式不同

**修复**：参考 alicloud provider 文档中的规格名称格式

### 15. 计算字段（read-only）

**现象**：某些字段在 plan 中显示变更，但这些字段是只读的

**修复**：
```hcl
lifecycle {
  ignore_changes = [
    # 列出所有只读/计算字段
    create_time,
    expired_time,
    status,
  ]
}
```


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (HIGH)