- OpenTofu 语言
- 语法
- JSON 配置语法
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.json
和foo.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_type
和 ami
参数。
综合来看,以上两个配置文件等同于原生语法中的以下块
variable "example" {
default = "hello"
}
resource "aws_instance" "example" {
instance_type = "t2.micro"
ami = "ami-abc123"
}
在每个顶级块类型中,映射到 JSON 的规则略有不同(请参阅下面的 特定于块类型的异常),但在大多数情况下,以下一般规则适用
-
表示块主体的 JSON 对象包含与参数名称或嵌套块类型名称相对应的属性。
-
如果属性对应于在原生语法中接受 任意表达式 的参数,则属性值将映射到表达式,如下面的 表达式映射 中所述。对于不接受任意表达式的参数,属性值的解释取决于参数,如稍后在本页中给出的 特定于块类型的异常 中所述。
-
如果属性名称对应于预期的嵌套块类型名称,则其值将按照下面的 嵌套块映射 中所述进行解释,除非在本页后面给出的 特定于块类型的异常 中另有说明。
表达式映射
由于 JSON 语法无法表示所有 OpenTofu 语言 表达式语法,因此解释为表达式的 JSON 值将按如下方式映射
JSON | OpenTofu 语言解释 |
---|---|
布尔值 | 文字 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 语法的映射不遵循上述一般规则。以下子部分描述了适用于每个顶级块类型的特殊映射规则。
resource
和 data
块
resource
和 data
块类型的一些元参数直接引用对象或文字关键字。在 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
块
description
和 sensitive
参数被解释为文字 JSON 值。description
字符串不被解释为字符串模板。
value
参数被解释为表达式。
{
"output": {
"example": {
"value": "${aws_instance.example}"
}
}
}
locals
块
表示 locals 块类型的 JSON 对象属性的值必须是一个 JSON 对象,其属性名称是要声明的本地值名称
{
"locals": {
"greeting": "Hello, ${var.name}"
}
}
这些嵌套属性的每个值都被解释为表达式。
module
块
source
和 version
元参数必须作为文字字符串给出。这些值不被解释为字符串模板。
providers
元参数必须作为 JSON 对象给出,其属性是公开到子模块的紧凑提供程序地址,其值是当前模块要使用的提供程序地址,两者都作为文字字符串给出
{
"module": {
"example": {
"source": "hashicorp/consul/azurerm",
"version": "= 1.0.0",
"providers": {
"aws": "aws.usw1"
}
}
}
}
provider
块
alias
和 version
元参数必须作为文字字符串给出。这些值不被解释为字符串模板。
{
"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"
}
}
}
}