设计 Terraform 资源时应该使用 block 还是 attirbute?

Shall I use block or attirbute when designing a Terraform resource?

上下文:我正在开发 Terraform 提供程序。

我可以看到一些提供商(如 AWS)在引用 ID 时使用 attribute(例如 connection_id):

resource "aws_dx_connection_confirmation" "confirmation" {
  connection_id = "dxcon-ffabc123"
}

而其他人使用 :

resource "aws_dx_connection_confirmation" "confirmation" {
  connection {
     id = "dxcon-ffabc123"
  }
}

它周围有特定的图案吗?据我所知,

  1. 如果有多个枚举值(barbar_2)并且只能指定其中一个,则使用
resource "aws_foo" "temp" {
  bar {
     id = "dxcon-ffabc123"
  }
  // bar_2 {
  //   id = "abcde"
  //}
}
  1. 使用对多个相关属性进行分组:
resource "aws_devicefarm_test_grid_project" "example" {
  name = "example"

  vpc_config {
    vpc_id             = aws_vpc.example.id
    subnet_ids         = aws_subnet.example.*.id
    security_group_ids = aws_security_group.example.*.id
  }
}
  1. 当计划向块代表的对象添加更多属性时使用:
resource "aws_dx_connection_confirmation" "confirmation" {
  connection {
     id = "dxcon-ffabc123"
     // TODO: later on, `name` will be added as a second input option that could be used to identify connection instead of `id`
  }
}

我找到了 Attributes as Blocks 文档,但它有点令人困惑。

一般来说,这里的直接比较是在参数(属性)与 Terraform map 类型值之间进行(区别于 Golang map,它也可以用于指定 Terraform block 值)和 Terraform block。它们本质上是等价的,因为它们允许将键值对作为值传递,但存在一些差异。这是要使用的二元决策树的一些内容:

  1. 理想的 Terraform 值类型是 map 还是 object(即键应该遵循命名模式,还是键可以命名为几乎任何东西)?

地图:属性
对象:块

  1. 如果更改了值,是否会强制执行删除和创建操作,或者是否可以改为更新?

DC:通常是块
U: 通常属性

  1. 提供者中是否有其他资源可以替代当前资源的使用(不同的 API 使用),例如在您上面的示例中,是否会有另一个专门用于分配连接的资源?

是:通常阻塞
否:通常属性

  1. 是值multi-level(多级键值对),还是单级?

单一级别:属性导致更好的代码,因为更简单和更清晰
multi-level: 块导致更好的代码,因为嵌套块

可能还有其他我不记得的决定性因素,但希望这些因素能指引正确的方向。

如果您正在开发一个完全未开发的提供程序,因此您不需要与任何现有用法保持兼容,我建议考虑使用围绕类型系统设计的 Terraform Plugin Framework (instead of "SDKv2") 和现代 Terraform 的行为,而旧的 SDK 是为更旧的 Terraform 版本设计的,这些版本具有更严格的配置语言。

特别是,新框架鼓励只使用 attribute-style 语法,允许您声明某些 attributes as having nested attributes,然后支持块允许的大多数相同内部结构,但使用为名称赋值的语法,而不是嵌套块语法。

嵌套块的初衷是表示声明一个单独的对象恰好“属于”包含对象的意义,而不是声明该 top-level 对象的参数。这种区别在实践中是模糊的,因为底层 APIs 通常将这些嵌套对象表示为 JSON 数组或 top-level 对象内的映射,因此将它们显示为单独对象的额外抽象最终伤害而不是帮助,因为它掩盖了底层数据结构的性质。特别是如果 API 中概念的物理表示是包含对象内的嵌套数据结构,我认为在 Terraform 中使用可比较的数据结构最有帮助。

现有提供程序中嵌套块类型的许多使用要么是对向后兼容性的让步,要么是这些提供程序仍在针对 SDKv2 编写的限制,因此无法声明结构化属性类型——这样的概念确实Terraform v0.11 及更早版本中不存在,这正是 SDKv2 的设计目标。

插件框架仍然支持声明块,并且在某些情况下嵌套项确实一个单独的对象,恰好在概念上包含在块所在的另一个对象中syntax 仍然是一个合理的选择。但是,我的建议是在大多数情况下默认使用属性语法。


在我写这篇文章的时候,插件框架还比较新,它的设计还没有完全确定。因此,在考虑是否使用时,我建议咨询Which SDK Should I Use?以便做出明智的决定。