设计 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"
}
}
它周围有特定的图案吗?据我所知,
- 如果有多个枚举值(
bar
、bar_2
)并且只能指定其中一个,则使用块:
resource "aws_foo" "temp" {
bar {
id = "dxcon-ffabc123"
}
// bar_2 {
// id = "abcde"
//}
}
- 使用块对多个相关属性进行分组:
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
}
}
- 当计划向块代表的对象添加更多属性时使用块:
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
。它们本质上是等价的,因为它们允许将键值对作为值传递,但存在一些差异。这是要使用的二元决策树的一些内容:
- 理想的 Terraform 值类型是
map
还是 object
(即键应该遵循命名模式,还是键可以命名为几乎任何东西)?
地图:属性
对象:块
- 如果更改了值,是否会强制执行删除和创建操作,或者是否可以改为更新?
DC:通常是块
U: 通常属性
- 提供者中是否有其他资源可以替代当前资源的使用(不同的 API 使用),例如在您上面的示例中,是否会有另一个专门用于分配连接的资源?
是:通常阻塞
否:通常属性
- 是值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?以便做出明智的决定。
上下文:我正在开发 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"
}
}
它周围有特定的图案吗?据我所知,
- 如果有多个枚举值(
bar
、bar_2
)并且只能指定其中一个,则使用块:
resource "aws_foo" "temp" {
bar {
id = "dxcon-ffabc123"
}
// bar_2 {
// id = "abcde"
//}
}
- 使用块对多个相关属性进行分组:
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
}
}
- 当计划向块代表的对象添加更多属性时使用块:
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
。它们本质上是等价的,因为它们允许将键值对作为值传递,但存在一些差异。这是要使用的二元决策树的一些内容:
- 理想的 Terraform 值类型是
map
还是object
(即键应该遵循命名模式,还是键可以命名为几乎任何东西)?
地图:属性
对象:块
- 如果更改了值,是否会强制执行删除和创建操作,或者是否可以改为更新?
DC:通常是块
U: 通常属性
- 提供者中是否有其他资源可以替代当前资源的使用(不同的 API 使用),例如在您上面的示例中,是否会有另一个专门用于分配连接的资源?
是:通常阻塞
否:通常属性
- 是值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?以便做出明智的决定。