一个资源需要由列表/映射驱动的可变数量的相同嵌套块(例如,入口规则)。
使用一个 `dynamic` 块,其 `for_each` 迭代集合;通过块迭代器(默认名称 = 块标签)在 `content {}` 内部引用每个元素。
原因: 动态块生成重复的嵌套块,无需复制粘贴;迭代器使每个生成的块都与其源元素绑定。
最后审核:2026年5月
TF-PRO 考试涉及的架构模式快速参考。从头到尾阅读,或跳转到任意章节。
一个资源需要由列表/映射驱动的可变数量的相同嵌套块(例如,入口规则)。
使用一个 `dynamic` 块,其 `for_each` 迭代集合;通过块迭代器(默认名称 = 块标签)在 `content {}` 内部引用每个元素。
原因: 动态块生成重复的嵌套块,无需复制粘贴;迭代器使每个生成的块都与其源元素绑定。
为对象映射中的每个条目创建一个资源,以稳定的键控,因此重新排序永远不会强制替换。
设置 `for_each = var.objects`(一个映射)。使用 `each.key` 作为稳定键,`each.value.<attr>` 用于字段。避免在此处使用 `count` — 索引偏移会导致频繁变动。
原因: 映射键在状态中是稳定的身份;列表索引是位置性的,当元素添加/删除时会发生偏移。
为多个实例选择使用 `count` 还是 `for_each`。
当实例具有不同的身份(集合/映射)时,使用 `for_each`;仅当需要 N 个相同、顺序不敏感的副本时,使用 `count`。对于任何可能增长/缩小的东西,优先使用 `for_each`。
原因: `for_each` 通过键寻址(resource["key"]);`count` 通过索引寻址(resource[0]),在插入/删除时会重新洗牌。
一个输入变量是一个对象,其中某些属性是可选的并且需要默认值。
将其类型定义为 `object({ name = string, size = optional(number, 10) })`。当调用者省略该属性时,`optional(type, default)` 提供默认值。
原因: 带默认值的 `optional()` 保持调用者简洁,同时保证下游的具体值——无需在所有地方处理空值。
在应用资源之前验证其假设,或在应用之后保证结果。
使用 `lifecycle { precondition { ... } }` 在创建/更新前断言输入,使用 `postcondition` 在之后断言输出。两者都接受 `condition` + `error_message`。
原因: 自定义条件会快速失败并显示清晰的消息,而不是产生损坏的应用或令人困惑的下游错误。
每当另一个资源或属性发生变化时,一个资源必须被重新创建。
添加 `lifecycle { replace_triggered_by = [aws_x.y.id] }`。当引用的值发生变化时,Terraform 会强制替换此资源。
原因: 以声明方式表达替换依赖关系,避免在每次相关更改时手动 `-replace`。
替换资源会导致停机,因为旧资源在新资源存在之前就被销毁了。
设置 `lifecycle { create_before_destroy = true }`,这样 Terraform 会先置备替换资源,然后再销毁旧资源。确保名称唯一/没有硬冲突。
原因: 零停机替换;但在两者短暂共存时,请注意名称冲突和配额限制。
尽早拒绝无效的输入值(例如,不是 dev/stage/prod 的环境)。
在变量中添加一个 `validation { condition = contains(["dev","stage","prod"], var.env), error_message = "..." }` 块。
原因: 在计划时捕获不良输入并显示可读消息,而不是在提供者调用深处失败。
引用一个可能不存在的值而不会使计划崩溃。
使用 `try(local.maybe.value, "default")` 在错误时回退,或者使用 `can(expr)` 获取表达式是否成功的布尔值。
原因: 优雅地处理可选/可变形状的数据;避免在键缺失时出现“错误:不支持的属性”。
将列表转换为映射,或过滤/塑造集合以用于资源参数。
使用 `for` 表达式:`{ for u in var.users : u.name => u.role if u.active }`(映射)或 `[for x in list : upper(x)]`(列表)。
原因: `for` 表达式是重塑数据的惯用方式;`if` 子句用于过滤,`k => v` 形式用于构建映射。
变量或输出包含不应在计划/应用输出中打印的秘密。
将变量标记为 `sensitive = true`(输出也一样)。Terraform 会在 CLI 输出中将其编辑掉,尽管它仍然存储在状态中。
原因: 防止日志/CI 输出中意外泄露;状态本身仍必须受到保护(加密后端,访问控制)。
在一个配置中管理两个区域/账户中的资源。
声明别名提供者(`provider "aws" { alias = "west" region = "us-west-2" }`),并在资源上设置 `provider = aws.west` 或传递给模块。
原因: 别名允许一个配置针对多个提供者实例;模块通过 `providers` 参数显式接收它们。
一个隐藏的依赖(未通过引用表达)导致排序问题。
添加 `depends_on = [aws_iam_role_policy.x]` 以强制排序。谨慎使用 — 优先通过属性引用进行隐式依赖。
原因: 显式 `depends_on` 处理图无法推断的依赖关系,但过度使用会创建保守、较慢的计划。
使用结构化变量从模板渲染配置文件/用户数据。
使用 `templatefile("${path.module}/tpl.tftpl", { items = local.items })`;模板使用 `%{ for }` / `${}` 插值。
原因: `templatefile` 在计划时保持渲染纯净(与已弃用的模板提供者不同),并支持循环/条件。
构建所有(子网、规则)组合的扁平列表,以供单个 `for_each` 使用。
使用 `setproduct(var.subnets, var.rules)` 获取笛卡尔积,或使用 `flatten([for ...])` 将嵌套列表折叠成一个。
原因: 这些函数将嵌套数据转换为 `for_each` 所需的扁平、唯一可键控的集合。
注册表模块更改,并在下次初始化时意外更改基础设施。
使用 `version = "~> 4.2"` 进行固定(仅限注册表模块)。对于 Git 源,固定 `?ref=v4.2.0` 标签。有意运行 `terraform init -upgrade` 以移动固定版本。
原因: 未固定的模块会浮动到最新版本;固定版本使升级有目的性且可审查。
根模块需要子模块深处产生的值。
在子模块中将其作为 `output` 暴露,然后引用 `module.child.output_name`。未输出的值对调用者不可访问。
原因: 模块是封装的;输出是数据向上跨越模块边界的唯一方式。
从映射中为每个团队/环境实例化相同的模块一次。
在模块块上设置 `for_each`:`module "env" { for_each = var.envs; source = "./env"; name = each.key }`。引用 `module.env["prod"]`。
原因: 模块上的 `for_each` 无需复制粘贴块即可扩展模式;键提供稳定的地址。
子模块必须在非默认(别名)提供者中创建资源。
显式传递提供者:`module "x" { providers = { aws = aws.west } }`。子模块在 `required_providers` 中使用 `configuration_aliases` 声明提供者。
原因: 模块不会隐式继承别名提供者;`providers` 映射将父别名连接到子模块。
重命名资源或将其移入模块通常会导致其被销毁并重新创建。
添加一个 `moved { from = aws_instance.old; to = module.compute.aws_instance.new }` 块。Terraform 会更新状态地址而不会销毁资源。
原因: `moved` 块使重构在代码中安全且可审查,取代了手动 `terraform state mv`。
一个庞大的模块变得难以管理,并且混合了网络、计算和数据方面的关注点。
分解为专注的子模块,并在根模块中组合它们,将一个模块的输出作为下一个模块的输入。保持模块单一用途。
原因: 组合提高了重用性和可测试性;紧密作用域的模块独立版本化和演进。
消费者向共享模块传递无效的输入组合。
在模块内部添加 `validation` 块和 `precondition` 以强制执行契约,并使用 `description` 文档化输入。
原因: 模块拥有其契约;内部验证保护所有调用者,而不仅仅是一个根配置。
深度嵌套的模块使数据流和提供者传递难以跟踪。
保持嵌套层级较浅(1-2 层)。在每个级别显式传递提供者和关键输入;避免依赖深层隐式继承。
原因: 浅层树更容易理解;深层嵌套会放大提供者传递和输出管道的复杂性。
将可重用模块发布到私有注册表,并在不破坏调用者的情况下对其进行演进。
使用语义版本(`v1.2.0`)标记发布;破坏性的输入/输出更改会提升主版本号。调用者使用 `~>` 约束进行固定。
原因: 语义版本控制允许消费者安全地采用修复/功能,并有意选择破坏性更改。
为给定成熟度级别选择模块的来源。
存储库内的模块使用本地路径(`./modules/x`),共享但未发布的模块使用 Git(`git::...?ref=tag`),版本化/已发布的模块使用注册表(`namespace/name/provider`)。
原因: 源类型匹配共享范围;只有注册表源支持 `version` 参数和约束解析。
模块输出包含由根模块使用的秘密。
将模块输出标记为 `sensitive = true`。在非敏感上下文中消费它将报错,直到您也将其视为敏感。
原因: 敏感性在模块边界上传播,防止在根输出中意外泄露。
现有的基于 `count` 的资源集需要转换为 `for_each` 而不销毁实例。
添加 `moved` 块,将每个 `resource[0]` 索引映射到新的 `resource["key"]` 地址,然后切换到 `for_each`。
原因: `moved` 块将状态从位置寻址重新键控到身份寻址,避免销毁/重新创建。
您在配置中重命名了一个资源;现在计划想要销毁旧资源并创建一个新资源。
优先在配置中使用 `moved` 块。对于临时/CLI 修复,使用 `terraform state mv aws_x.old aws_x.new` 重新指向现有对象。
原因: 两者都更新状态地址,以便 Terraform 将现有对象视为重命名的资源 — 不会销毁。
将现有手动创建的资源纳入 Terraform 管理。
添加一个 `import { to = aws_x.y; id = "i-123" }` 块,并运行 `terraform plan -generate-config-out=gen.tf` 以生成配置骨架,然后进行细化和应用。
原因: 配置驱动的导入是可审查的,并生成初始配置,与旧的命令式 `terraform import` 不同。
停止使用 Terraform 管理资源,但使其在云中继续运行。
运行 `terraform state rm aws_x.y`。Terraform 会忘记该对象;它不会被销毁。同时删除其配置以避免重新创建计划。
原因: `state rm` 在不删除的情况下分离资源 — 当将资源移交给另一个工具/团队时很有用。
将状态从本地后端迁移到 S3(或 HCP Terraform)。
添加/替换 `backend`/`cloud` 块,运行 `terraform init` — Terraform 会检测到更改并提示将现有状态迁移到新后端。
原因: `init` 协调复制;回答“是”会安全地迁移状态,而不是从空状态开始。
两名工程师同时对同一个远程状态运行 apply。
使用支持锁定的后端(S3+DynamoDB, HCP Terraform 等)。Terraform 每次操作都会获取一个锁;第二次运行会等待或报错。
原因: 锁定可防止并发写入导致状态损坏。切勿随意禁用它。
崩溃的 apply 留下了一个陈旧的锁,现在每次运行都被阻塞。
确认没有操作正在实际运行,然后 `terraform force-unlock <LOCK_ID>`。使用错误消息中的 ID。
原因: `force-unlock` 清除孤立锁;在实际操作运行时执行此操作有状态损坏的风险。
检测配置/状态与实际基础设施之间的漂移,但不提议更改。
运行 `terraform plan -refresh-only`(或 `apply -refresh-only` 以更新状态)。它报告差异而不计划资源更改。
原因: 将漂移检测与更改计划分离 — 在决定协调之前,您会看到云中发生了什么变化。
一个资源行为异常,您想在不编辑配置的情况下重新创建它。
运行 `terraform apply -replace="aws_instance.web"`。这是已弃用的 `terraform taint` 的现代替代方案。
原因: `-replace` 强制在下次 apply 时销毁并重新创建某个资源,在 CLI 上声明性地完成。
您倾向于经常使用 `-target` 来加速 apply。
仅在从错误中恢复或进行精细修复时使用 `-target`。避免将其作为正常工作流程 — 它会产生部分 apply 并可能跳过依赖项。
原因: 常规的目标定位会隐藏依赖问题并导致不完整状态;HashiCorp 将其记录为一种例外工具。
提供者移动了命名空间(例如,从 hashicorp/aws 移动到分叉),并且状态引用了旧地址。
运行 `terraform state replace-provider registry.terraform.io/hashicorp/aws registry.example.com/org/aws`。
原因: 重写状态中的提供者引用,以便 init/plan 解析新源而无需重新创建资源。
一个配置需要另一个配置/工作区产生的输出。
使用 `terraform_remote_state` 数据源(或 HCP Terraform 运行输出)以只读方式读取另一个状态的输出。
原因: 在状态边界之间共享值而无需复制资源;只有导出的输出是可读的。
后端设置(桶,键)因环境而异,不应硬编码。
将它们从 `backend` 块中移除,并在初始化时传递:`terraform init -backend-config=prod.hcl`(或 `-backend-config="key=..."`)。
原因: 部分配置使一个配置在不同环境之间可重用,同时在初始化时提供特定于环境的后端值。
您需要从一个配置中为 dev/stage/prod 提供单独的状态。
使用 CLI 工作区(`terraform workspace new prod`)进行轻量级隔离,或使用单独的根配置/HCP 工作区进行更强的隔离。
原因: 每个工作区都有自己的状态;引用 `terraform.workspace` 来改变命名/大小。对于强隔离,优先选择不同的后端/工作区。
避免在 HCP Terraform 工作区变量中存储长期有效的云密钥。
配置动态提供者凭据:HCP Terraform 使用 OIDC/工作负载身份在每次运行中从 AWS/Azure/GCP/Vault 获取短期凭据。
原因: 消除了静态秘密;凭据是即时生成的并会过期,从而缩小了爆炸半径。
许多工作区都需要相同的变量(提供者配置、标签)。
定义一个变量集并将其应用于项目或选定的工作区。工作区级变量会覆盖变量集中的值。
原因: 变量集避免了共享配置的重复;优先级(工作区 > 变量集)允许工作区在需要时进行覆盖。
在每次运行中强制执行防护措施(例如,不允许公共 S3,要求标签)。
附加一个 Sentinel 或 OPA 策略集。设置强制级别:建议(警告)、软强制(经许可可覆盖)或硬强制(阻止)。
原因: 策略即代码集中控制运行;强制级别在严格性与操作灵活性之间取得平衡。
将外部检查(成本估算、安全扫描)集成到运行管道中。
在某个阶段(计划前、计划后、应用前)配置一个运行任务。HCP Terraform 会调用外部服务,并根据其结果控制运行。
原因: 运行任务通过第三方检查扩展了管道,无需自定义 CI 管道。
选择如何触发工作区的运行。
VCS 驱动(提交/PR 触发计划)、CLI 驱动(针对远程运行 `terraform plan/apply`)或 API 驱动(上传配置)。根据团队工作流程选择。
原因: VCS 驱动适用于 GitOps;CLI 驱动适用于本地迭代;API 驱动适用于自定义管道。它们在每个工作区中是互斥的。
授予团队对 staging 工作区的写入权限,但对 production 工作区仅授予只读权限。
在组织/项目/工作区级别划分权限:为每个项目或工作区分配团队访问权限(读取/计划/写入/管理员);使用项目分组进行大规模管理。
原因: 细粒度、作用域化的权限强制执行最小权限原则;项目级授权减少了每个工作区的管理。
网络工作区的 apply 应该自动在依赖的应用工作区中排队运行。
配置运行触发器:下游工作区订阅上游工作区;成功应用后会排队下游运行。
原因: 运行触发器链式连接依赖工作区,以便共享基础设施更改按顺序传播。
让非 Terraform 用户通过表单配置标准化基础设施。
在私有注册表中发布一个无代码模块;用户通过 UI 实例化它,只需提供输入 — 无需编写 HCL。
原因: 无代码模块普及了自助服务配置,同时保持底层配置受控和版本化。
在组织内共享经过审查的模块和提供者。
发布到 HCP Terraform 私有注册表;消费者使用版本约束引用 `app.terraform.io/org/name/provider`。
原因: 私有注册表集中了内部模块的发现、版本控制和治理。
按团队/应用程序组织数十个工作区,以实现权限和变量集管理。
将工作区分组到项目中;在项目级别应用团队权限和变量集。
原因: 项目扩展了治理规模 — 您可以按项目而不是按工作区管理访问权限和共享配置。
持续检测生产环境何时偏离配置状态。
在工作区上启用健康评估(漂移检测/持续验证);HCP Terraform 会定期刷新并报告漂移和失败的断言。
原因: 自动化评估可在 apply 之间发现漂移和失败的后置条件,从而在它们导致事件之前进行预警。
HCP Terraform 必须访问没有公共入口的私有网络中的基础设施。
在私有网络中部署 HCP Terraform 代理,并将工作区分配给代理池;运行通过代理执行。
原因: 代理允许 HCP Terraform 在私有/隔离环境中运行,而无需将其公开暴露。
一次错误的 apply 破坏了状态,您需要恢复。
HCP Terraform 维护版本化的状态;从工作区 UI/API 回滚到先前的状态版本并重新计划。
原因: 内置的状态版本控制提供了恢复点,而无需您自行管理后端快照。