跳至主要内容

JSON 配置语法

大多数 OpenTofu 配置都是使用 原生 OpenTofu 语言语法 编写的,这种语法设计得相对易于人类阅读和更新。

OpenTofu 还支持一种与 JSON 兼容的替代语法。当以编程方式生成配置的一部分时,此语法很有用,因为可以使用现有的 JSON 库来准备生成的配置文件。

JSON 语法是根据原生语法定义的。原生语法中可以表达的所有内容也可以在 JSON 语法中表达,但由于 JSON 语法的限制,某些结构在 JSON 中表示起来更为复杂。

OpenTofu 期待以 .tf.tofu 后缀命名的文件使用原生语法,以 .tf.json.tofu.json 后缀命名的文件使用 JSON 语法。

低级 JSON 语法,就像原生语法一样,是根据称为 HCL 的规范定义的。要使用 OpenTofu,无需了解 HCL 语法或其 JSON 映射的所有细节,因此本页总结了原生语法和 JSON 语法之间最重要的区别。如果您有兴趣,可以在 其规范 中找到 HCL 的 JSON 语法的完整定义。

扩展优先级

当同一目录中同时存在具有相同基本名称的 .tf.json.tofu.json 文件时,OpenTofu 将优先使用 .tofu.json 文件并忽略 .tf.json 文件。例如

  • 如果 foo.tf.jsonfoo.tofu.json 都存在于同一目录中,OpenTofu 将只加载 foo.tofu.json 并忽略 foo.tf.json

这确保了当两种文件都可用时,.tofu.json 文件始终优先于 .tf.json 文件。对于希望其模块同时支持 OpenTofu 和 Terraform 的模块作者来说,这种情况可能很有用。

JSON 文件结构

任何基于 JSON 的 OpenTofu 配置的根都是一个 JSON 对象。此对象的属性对应于 OpenTofu 语言的顶级块类型。例如

代码块
{
"variable": {
"example": {
"default": "hello"
}
}
}

每个顶级对象属性必须与预期顶级块类型之一的名称匹配。需要标签的块类型(如上所示的 variable)由每个标签级别的一个嵌套对象值表示。resource 块需要两个标签,因此需要两个级别的嵌套

代码块
{
"resource": {
"aws_instance": {
"example": {
"instance_type": "t2.micro",
"ami": "ami-abc123"
}
}
}
}

在表示标签的任何嵌套对象之后,最终还有一个嵌套对象表示块本身的主体。在上面的示例中,指定了 variable "example"default 参数以及 resource "aws_instance" "example"instance_typeami 参数。

综合来看,以上两个配置文件等同于原生语法中的以下块

代码块
variable "example" {
default = "hello"
}

resource "aws_instance" "example" {
instance_type = "t2.micro"
ami = "ami-abc123"
}

在每个顶级块类型中,映射到 JSON 的规则略有不同(请参阅下面的 特定于块类型的异常),但在大多数情况下,以下一般规则适用

  • 表示块主体的 JSON 对象包含与参数名称或嵌套块类型名称相对应的属性。

  • 如果属性对应于在原生语法中接受 任意表达式 的参数,则属性值将映射到表达式,如下面的 表达式映射 中所述。对于不接受任意表达式的参数,属性值的解释取决于参数,如稍后在本页中给出的 特定于块类型的异常 中所述。

  • 如果属性名称对应于预期的嵌套块类型名称,则其值将按照下面的 嵌套块映射 中所述进行解释,除非在本页后面给出的 特定于块类型的异常 中另有说明。

表达式映射

由于 JSON 语法无法表示所有 OpenTofu 语言 表达式语法,因此解释为表达式的 JSON 值将按如下方式映射

JSONOpenTofu 语言解释
布尔值文字 bool 值。
数字文字 number 值。
字符串解析为 字符串模板,然后按如下所述进行评估。
对象每个属性值都根据此表进行映射,生成具有适当属性类型的 object(...) 值。
数组每个元素都根据此表进行映射,生成具有适当元素类型的 tuple(...) 值。
空值文字 null

当在预期任意表达式的某个位置遇到 JSON 字符串时,其值首先将被解析为 [字符串模板][],然后对其进行评估以生成最终结果。

如果给定的模板由单个插值序列组成,则直接获取其表达式的结果,而不先将其转换为字符串。这允许在 JSON 语法中使用非字符串表达式

代码块
{
"output": {
"example": {
"value": "${aws_instance.example}"
}
}
}

上面声明的 output "example" 将表示给定 aws_instance 资源块的对象值作为其值,而不是字符串值。如果模板中出现任何文字或控制序列,则此特殊行为不适用;在这些其他情况下,始终会生成字符串值。

嵌套块映射

当 JSON 对象属性以嵌套块类型命名时,此属性的值表示该类型的一个或多个块。属性的值必须是 JSON 对象或 JSON 数组。

最简单的情况是仅表示给定类型的一个块,当该类型不需要标签时,例如在 resource 块中使用的 lifecycle 嵌套块

代码块
{
"resource": {
"aws_instance": {
"example": {
"lifecycle": {
"create_before_destroy": true
}
}
}
}
}

以上等同于以下原生语法配置

代码块
resource "aws_instance" "example" {
lifecycle {
create_before_destroy = true
}
}

当嵌套块类型需要一个或多个标签,或者当可以给出相同类型的多个块时,映射就会变得稍微复杂一些。例如,resource 块中使用的 provisioner 嵌套块类型需要一个标签来指定要使用的供应器,并且供应器块的顺序对于确定操作顺序至关重要。

以下本机语法示例显示了一个包含多个不同类型供应器的 resource

代码块
resource "aws_instance" "example" {
# (resource configuration omitted for brevity)

provisioner "local-exec" {
command = "echo 'Hello World' >example.txt"
}
provisioner "file" {
source = "example.txt"
destination = "/tmp/example.txt"
}
provisioner "remote-exec" {
inline = [
"sudo install-something -f /tmp/example.txt",
]
}
}

为了保留这些块的顺序,必须使用 JSON 数组作为表示此块类型的属性的直接值,如上述内容的 JSON 等效项所示

代码块
{
"resource": {
"aws_instance": {
"example": {
"provisioner": [
{
"local-exec": {
"command": "echo 'Hello World' >example.txt"
}
},
{
"file": {
"source": "example.txt",
"destination": "/tmp/example.txt"
}
},
{
"remote-exec": {
"inline": ["sudo install-something -f /tmp/example.txt"]
}
}
]
}
}
}
}

provisioner 数组的每个元素都是一个对象,其中包含一个属性,其名称表示每个 provisioner 块的标签。对于期望多个标签的块类型,可以在每个附加级别使用这种交替的数组和对象嵌套模式。

如果嵌套块类型需要标签但顺序并不重要,则可以省略数组并仅提供一个对象,其属性名称对应于唯一的块标签。在简单情况下,这允许作为上述内容的简写,但交替的数组和对象方法是最通用的。我们建议在系统地将本机语法转换为 JSON 时使用最通用的形式,以确保配置的含义被完全保留。

注释属性

尽管我们不建议手动编辑 JSON 语法配置文件——这种格式主要用于程序生成和使用——但在表示块体的 JSON 对象内部允许使用有限形式的注释,使用特殊的属性名称

代码块
{
"resource": {
"aws_instance": {
"example": {
"//": "This instance runs the scheduled tasks for backup",

"instance_type": "t2.micro",
"ami": "ami-abc123"
}
}
}
}

在任何表示块体的对象中,名为 "//" 的属性都被 OpenTofu 完全忽略。此例外适用于被解释为表达式的对象,在这些对象中,这将被解释为名为 "//" 的对象类型属性。

此特殊属性名称也可用于基于 JSON 的配置文件的根目录。这对于记录创建文件的程序很有用。

代码块
{
"//": "This file is generated by generate-outputs.py. DO NOT HAND-EDIT!",

"output": {
"example": {
"value": "${aws_instance.example}"
}
}
}

特定于块类型的异常

OpenTofu 以特殊方式处理特定块类型中的某些参数,因此它们到 JSON 语法的映射不遵循上述一般规则。以下子部分描述了适用于每个顶级块类型的特殊映射规则。

resourcedata

resourcedata 块类型的一些元参数直接引用对象或文字关键字。在 JSON 中表示时,引用或关键字作为 JSON 字符串给出,没有其他周围的空格或符号。

例如,provider 元参数采用对提供程序配置的 <PROVIDER>.<ALIAS> 引用,它在本机语法中未加引号显示,但必须在 JSON 语法中以字符串形式呈现

代码块
{
"resource": {
"aws_instance": {
"example": {
"provider": "aws.foo"
}
}
}
}

此特殊处理适用于以下元参数

  • provider:单个字符串,如上所示
  • depends_on:一个包含对命名实体的引用的字符串数组,例如 ["aws_instance.example"]
  • lifecycle 块中的 ignore_changes:如果设置为 all,则必须提供单个字符串 "all"。否则,必须使用包含属性引用的 JSON 字符串数组,例如 ["ami"]

特殊处理也适用于任何 connection 块的 type 参数,无论是在 resource 块内还是嵌套在 provisioner 块内:给定的字符串按字面解释,而不是作为字符串模板进行解析和评估。

variable

variable 块中的所有参数都具有非标准的 JSON 映射

  • type:包含类型表达式的字符串,例如 "string""list(string)"
  • default:可以转换为给定类型的文字 JSON 值。此值中的字符串按字面理解,解释为字符串模板。
  • description:文字 JSON 字符串,解释为模板。
代码块
{
"variable": {
"example": {
"type": "string",
"default": "hello"
}
}
}

output

descriptionsensitive 参数被解释为文字 JSON 值。description 字符串不被解释为字符串模板。

value 参数被解释为表达式

代码块
{
"output": {
"example": {
"value": "${aws_instance.example}"
}
}
}

locals

表示 locals 块类型的 JSON 对象属性的值必须是一个 JSON 对象,其属性名称是要声明的本地值名称

代码块
{
"locals": {
"greeting": "Hello, ${var.name}"
}
}

这些嵌套属性的每个值都被解释为表达式

module

sourceversion 元参数必须作为文字字符串给出。这些值不被解释为字符串模板。

providers 元参数必须作为 JSON 对象给出,其属性是公开到子模块的紧凑提供程序地址,其值是当前模块要使用的提供程序地址,两者都作为文字字符串给出

代码块
{
"module": {
"example": {
"source": "hashicorp/consul/azurerm",
"version": "= 1.0.0",
"providers": {
"aws": "aws.usw1"
}
}
}
}

provider

aliasversion 元参数必须作为文字字符串给出。这些值不被解释为字符串模板。

代码块
{
"provider": {
"aws": [
{
"region": "us-east-1"
},
{
"alias": "usw1",
"region": "us-west-1"
}
]
}
}

terraform

由于 terraform 块中的任何设置都不接受命名对象引用或函数调用,因此所有设置值都按字面理解。字符串值不被解释为字符串模板。

由于每个 terraform 块只允许一个 backend 块,因此可以使用紧凑块映射来表示它,其中嵌套对象包含一个属性,其名称表示后端类型。

代码块
{
"terraform": {
"required_version": ">= 0.12.0",
"backend": {
"s3": {
"region": "us-west-2",
"bucket": "acme-tofu-states"
}
}
}
}