- OpenTofu 内部机制
- JSON 输出格式
JSON 输出格式
当 OpenTofu 计划进行更改时,它会将人类可读的摘要打印到终端。它还可以(当使用 -out=<PATH>
运行时)写入更详细的二进制计划文件,该文件稍后可用于应用这些更改。
由于计划文件的格式不适合与外部工具一起使用(并且可能永远不会),因此 OpenTofu 可以输出计划文件更改的机器可读 JSON 表示形式。它还可以将状态文件转换为相同的格式,以简化数据加载并提供更好的长期兼容性。
使用 tofu show -json <FILE>
生成计划或状态文件的 JSON 表示形式。有关更多详细信息,请参阅 tofu show
文档。
输出包括一个 format_version
密钥,其值为 "1.0"
。此版本的语义为
- 我们将增加次要版本,例如
"1.1"
,以进行向后兼容的更改或添加。忽略任何具有无法识别的名称的对象属性以保持与未来次要版本的向前兼容性。 - 我们将增加主要版本,例如
"2.0"
,以进行不向后兼容的更改。拒绝报告不支持的主要版本的任何输入。
我们将在 OpenTofu 1.0 兼容性承诺 的范围内引入新的主要版本。
格式摘要
以下部分通过示例描述 JSON 输出格式,使用伪 JSON 表示法。
重要元素用注释描述,注释以 //
为前缀。
为了避免过度重复,我们将完整格式拆分为几个离散的子对象,并在单独的标题下进行描述。用尖括号括起来(如 <values-representation>
)的引用是占位符,在实际输出中,将替换为指定子对象的实例。
JSON 输出格式包含以下对象和子对象
- 状态表示 — 由
tofu show -json <STATE FILE>
返回的完整顶级对象。 - 计划表示 — 由
tofu show -json <PLAN FILE>
返回的完整顶级对象。 - 值表示 — 计划和状态输出的子对象,描述当前状态或计划状态。
- 配置表示 — 计划输出的子对象,描述已解析的 OpenTofu 配置。
- 更改表示 — 计划输出的子对象,描述对对象的更改。
- 检查表示 — 计划和状态表示的属性,描述配置中任何检查(例如先决条件和后置条件)的当前状态。
状态表示
状态没有任何重要的元数据未包含在公共 值表示 中,因此 <state-representation>
使用以下格式
{
// "values" is a values representation object derived from the values in the
// state. Because the state is always fully known, this is always complete.
"values": <values-representation>
// The key here is left unchanged in OpenTofu for compatibility reasons.
"terraform_version": "version.string"
}
计划表示
计划由先前状态、正在应用于该状态的配置以及 OpenTofu 计划为实现该状态而进行的一系列更改组成。
为了便于调用者使用,计划表示包含最终状态中值的局部表示(使用 值表示),允许调用者使用与分析先前状态类似的代码轻松分析计划结果。
{
"format_version": "1.0",
// "prior_state" is a representation of the state that the configuration is
// being applied to, using the state representation described above.
"prior_state": <state-representation>,
// "configuration" is a representation of the configuration being applied to the
// prior state, using the configuration representation described above.
"configuration": <configuration-representation>,
// "planned_values" is a description of what is known so far of the outcome in
// the standard value representation, with any as-yet-unknown values omitted.
"planned_values": <values-representation>,
// "proposed_unknown" is a representation of the attributes, including any
// potentially-unknown attributes. Each value is replaced with "true" or
// "false" depending on whether it is known in the proposed plan.
"proposed_unknown": <values-representation>,
// "variables" is a representation of all the variables provided for the given
// plan. This is structured as a map similar to the output map so we can add
// additional fields in later.
"variables": {
"varname": {
"value": "varvalue"
},
},
// "resource_changes" is a description of the individual change actions that
// OpenTofu plans to use to move from the prior state to a new state
// matching the configuration.
"resource_changes": [
// Each element of this array describes the action to take
// for one instance object. All resources in the
// configuration are included in this list.
{
// "address" is the full absolute address of the resource instance this
// change applies to, in the same format as addresses in a value
// representation.
"address": "module.child.aws_instance.foo[0]",
// "previous_address" is the full absolute address of this resource
// instance as it was known after the previous OpenTofu run.
// Included only if the address has changed, e.g. by handling
// a "moved" block in the configuration.
"previous_address": "module.instances.aws_instance.foo[0]",
// "module_address", if set, is the module portion of the above address.
// Omitted if the instance is in the root module.
"module_address": "module.child",
// "mode", "type", "name", and "index" have the same meaning as in a
// value representation.
"mode": "managed",
"type": "aws_instance",
"name": "foo",
"index": 0,
// "deposed", if set, indicates that this action applies to a "deposed"
// object of the given instance rather than to its "current" object.
// Omitted for changes to the current object. "address" and "deposed"
// together form a unique key across all change objects in a particular
// plan. The value is an opaque key representing the specific deposed
// object.
"deposed": "deadbeef",
// "change" describes the change that will be made to the indicated
// object. The <change-representation> is detailed in a section below.
"change": <change-representation>,
// "action_reason" is some optional extra context about why the
// actions given inside "change" were selected. This is the JSON
// equivalent of annotations shown in the normal plan output like
// "is tainted, so it must be replaced" as opposed to just "must be
// replaced".
//
// These reason codes are display hints only and the set of possible
// hints may change over time. Users of this must be prepared to
// encounter unrecognized reasons and treat them as unspecified reasons.
//
// The current set of possible values is:
// - "replace_because_tainted": the object in question is marked as
// "tainted" in the prior state, so OpenTofu planned to replace it.
// - "replace_because_cannot_update": the provider indicated that one
// of the requested changes isn't possible without replacing the
// existing object with a new object.
// - "replace_by_request": the user explicitly called for this object
// to be replaced as an option when creating the plan, which therefore
// overrode what would have been a "no-op" or "update" action otherwise.
// - "delete_because_no_resource_config": OpenTofu found no resource
// configuration corresponding to this instance.
// - "delete_because_no_module": The resource instance belongs to a
// module instance that's no longer declared, perhaps due to changing
// the "count" or "for_each" argument on one of the containing modules.
// - "delete_because_wrong_repetition": The instance key portion of the
// resource address isn't of a suitable type for the corresponding
// resource's configured repetition mode (count, for_each, or neither).
// - "delete_because_count_index": The corresponding resource uses count,
// but the instance key is out of range for the currently-configured
// count value.
// - "delete_because_each_key": The corresponding resource uses for_each,
// but the instance key doesn't match any of the keys in the
// currently-configured for_each value.
// - "read_because_config_unknown": For a data resource, OpenTofu cannot
// read the data during the plan phase because of values in the
// configuration that won't be known until the apply phase.
// - "read_because_dependency_pending": For a data resource, OpenTofu
// cannot read the data during the plan phase because the data
// resource depends on at least one managed resource that also has
// a pending change in the same plan.
//
// If there is no special reason to note, OpenTofu will omit this
// property altogether.
action_reason: "replace_because_tainted"
}
],
// "resource_drift" is a description of the changes OpenTofu detected
// when it compared the most recent state to the prior saved state.
"resource_drift": [
{
// "resource_drift" uses the same object structure as
// "resource_changes".
}
],
// "relevant_attributes" lists the sources of all values contributing to
// changes in the plan. You can use "relevant_attributes" to filter
// "resource_drift" and determine which external changes may have affected the
// plan result.
"relevant_attributes": [
{
"resource": "aws_instance.foo",
"attribute": "attr",
}
]
// "output_changes" describes the planned changes to the output values of the
// root module.
"output_changes": {
// Keys are the defined output value names.
"foo": {
// "change" describes the change that will be made to the indicated output
// value, using the same representation as for resource changes except
// that the only valid actions values are:
// ["create"]
// ["update"]
// ["delete"]
// OpenTofu is not yet fully able to
// track changes to output values, so the actions indicated may not be
// fully accurate, but the "after" value will always be correct.
"change": <change-representation>,
}
},
// "checks" describes the partial results for any checkable objects, such as
// resources with postconditions, with as much information as OpenTofu can
// recognize at plan time. Some objects will have status "unknown" to
// indicate that their status will only be determined after applying the plan.
"checks" <checks-representation>,
// "errored" indicates whether planning failed. An errored plan cannot be applied,
// but the actions planned before failure may help to understand the error.
"errored": false
}
此整体计划结构(完全展开)将由 tofu show -json <planfile>
命令打印。
值表示
值表示在状态和计划输出中都使用,以描述当前状态(始终完整)和计划状态(省略在应用之前未知的值)。
以下示例说明了 <values-representation>
的结构
{
// "outputs" describes the outputs from the root module. Outputs from
// descendent modules are not available because they are not retained in all
// of the underlying structures we will build this values representation from.
"outputs": {
"private_ip": {
"value": "192.168.3.2",
"type": "string",
"sensitive": false
}
},
// "root_module" describes the resources and child modules in the root module.
"root_module": {
"resources": [
{
// "address" is the absolute resource address, which callers must consider
// opaque but may do full string comparisons with other address strings or
// pass this verbatim to other OpenTofu commands that are documented to
// accept absolute resource addresses. The module-local portions of this
// address are extracted in other properties below.
"address": "aws_instance.example[1]",
// "mode" can be "managed", for resources, or "data", for data resources
"mode": "managed",
"type": "aws_instance",
"name": "example",
// If the count or for_each meta-arguments are set for this resource, the
// additional key "index" is present to give the instance index key. This
// is omitted for the single instance of a resource that isn't using count
// or for_each.
"index": 1,
// "provider_name" is the name of the provider that is responsible for
// this resource. This is only the provider name, not a provider
// configuration address, and so no module path nor alias will be
// indicated here. This is included to allow the property "type" to be
// interpreted unambiguously in the unusual situation where a provider
// offers a resource type whose name does not start with its own name,
// such as the "googlebeta" provider offering "google_compute_instance".
"provider_name": "aws",
// "schema_version" indicates which version of the resource type schema
// the "values" property conforms to.
"schema_version": 2,
// "values" is the JSON representation of the attribute values of the
// resource, whose structure depends on the resource type schema. Any
// unknown values are omitted or set to null, making them
// indistinguishable from absent values; callers which need to distinguish
// unknown from unset must use the plan-specific or configuration-specific
// structures described in later sections.
"values": {
"id": "i-abc123",
"instance_type": "t2.micro",
// etc, etc
},
// "sensitive_values" is the JSON representation of the sensitivity of
// the resource's attribute values. Only attributes which are sensitive
// are included in this structure.
"sensitive_values": {
"id": true,
}
}
]
"child_modules": [
// Each entry in "child_modules" has the same structure as the root_module
// object, with the additional "address" property shown below.
{
// "address" is the absolute module address, which callers must treat as
// opaque but may do full string comparisons with other module address
// strings and may pass verbatim to other OpenTofu commands that are
// documented as accepting absolute module addresses.
"address": "module.child",
// "resources" is the same as in "root_module" above
"resources": [
{
"address": "module.child.aws_instance.foo",
// etc, etc
}
],
// Each module object can optionally have its own
// nested "child_modules", recursively describing the
// full module tree.
"child_modules": [ ... ],
}
]
}
}
属性和输出值的转换与 OpenTofu 的 jsonencode
函数使用的从 HCL 类型到 JSON 类型的相同直观映射相同。此映射确实会丢失一些信息:列表、集合和元组都降低到 JSON 数组,而映射和对象都降低到 JSON 对象。未知值和空值都视为不存在或为空。
输出值包括一个 "type"
字段,它是 值类型的序列化。对于原始类型,这是一个字符串值,例如 "number"
或 "bool"
。复杂类型表示为嵌套的 JSON 数组,例如 ["map","string"]
或 ["object",{"a":"number"}]
。这可用于使用正确的类型重建输出值。
仅描述每个资源实例的“当前”对象。“废除”的对象在此结构中根本没有反映出来;在计划表示中,您可以参考更改表示以获取更多详细信息。
此结构的目的是让调用者能够访问与配置本身内的表达式可访问的相同级别的详细信息。此通用表示不适用于所有用例,因为它与构建它的数据结构相比会丢失信息。对于更复杂的需求,请使用更详细的更改和配置表示。
配置表示
配置是 OpenTofu 中最复杂的结构,因为它包含未求值的表达式节点和其他复杂性。
由于配置模型是在表达式求值之前生成的,因此无法生成配置的值表示。相反,我们描述了配置的物理结构,在可能的情况下访问常量值,并允许调用者分析存在的对其他对象的任何引用。
{
// "provider_configs" describes all of the provider configurations throughout
// the configuration tree, flattened into a single map for convenience since
// provider configurations are the one concept in OpenTofu that can span
// across module boundaries.
"provider_config": {
// Keys in the provider_configs map are to be considered opaque by callers,
// and used just for lookups using the "provider_config_key" property in each
// resource object.
"opaque_provider_ref_aws": {
// "name" is the name of the provider without any alias
"name": "aws",
// "full_name" is the fully-qualified provider name
"full_name": "registry.opentofu.org/hashicorp/aws",
// "alias" is the alias set for a non-default configuration, or unset for
// a default configuration.
"alias": "foo",
// "module_address" is included only for provider configurations that are
// declared in a descendent module, and gives the opaque address for the
// module that contains the provider configuration.
"module_address": "module.child",
// "expressions" describes the provider-specific content of the
// configuration block, as a block expressions representation (see section
// below).
"expressions": <block-expressions-representation>
}
},
// "root_module" describes the root module in the configuration, and serves
// as the root of a tree of similar objects describing descendent modules.
"root_module": {
// "outputs" describes the output value configurations in the module.
"outputs": {
// Property names here are the output value names
"example": {
"expression": <expression-representation>,
"sensitive": false
}
},
// "resources" describes the "resource" and "data" blocks in the module
// configuration.
"resources": [
{
// "address" is the opaque absolute address for the resource itself.
"address": "aws_instance.example",
// "mode", "type", and "name" have the same meaning as for the resource
// portion of a value representation.
"mode": "managed",
"type": "aws_instance",
"name": "example",
// "provider_config_key" is the key into "provider_configs" (shown
// above) for the provider configuration that this resource is
// associated with. If the provider configuration was passed into
// this module from the parent module, the key will point to the
// original provider config block.
"provider_config_key": "opaque_provider_ref_aws",
// "provisioners" is an optional field which describes any provisioners.
// Connection info will not be included here.
"provisioners": [
{
"type": "local-exec",
// "expressions" describes the provisioner configuration
"expressions": <block-expressions-representation>
},
],
// "expressions" describes the resource-type-specific content of the
// configuration block.
"expressions": <block-expressions-representation>,
// "schema_version" is the schema version number indicated by the
// provider for the type-specific arguments described in "expressions".
"schema_version": 2,
// "count_expression" and "for_each_expression" describe the expressions
// given for the corresponding meta-arguments in the resource
// configuration block. These are omitted if the corresponding argument
// isn't set.
"count_expression": <expression-representation>,
"for_each_expression": <expression-representation>
},
],
// "module_calls" describes the "module" blocks in the module. During
// evaluation, a module call with count or for_each may expand to multiple
// module instances, but in configuration only the block itself is
// represented.
"module_calls": {
// Key is the module call name chosen in the configuration.
"child": {
// "resolved_source" is the resolved source address of the module, after
// any normalization and expansion. This could be either a
// go-getter-style source address or a local path starting with "./" or
// "../". If the user gave a registry source address then this is the
// final location of the module as returned by the registry, after
// following any redirect indirection.
"resolved_source": "./child"
// "expressions" describes the expressions for the arguments within the
// block that correspond to input variables in the child module.
"expressions": <block-expressions-representation>,
// "count_expression" and "for_each_expression" describe the expressions
// given for the corresponding meta-arguments in the module
// configuration block. These are omitted if the corresponding argument
// isn't set.
"count_expression": <expression-representation>,
"for_each_expression": <expression-representation>,
// "module" is a representation of the configuration of the child module
// itself, using the same structure as the "root_module" object,
// recursively describing the full module tree.
"module": <module-configuration-representation>
}
}
}
}
表达式表示
配置中的每个未求值表达式都用一个<expression-representation>
对象表示,其结构如下所示
{
// "constant_value" is set only if the expression contains no references to
// other objects, in which case it gives the resulting constant value. This is
// mapped as for the individual values in a value representation.
"constant_value": "hello",
// Alternatively, "references" will be set to a list of references in the
// expression. Multi-step references will be unwrapped and duplicated for each
// significant traversal step, allowing callers to more easily recognize the
// objects they care about without attempting to parse the expressions.
// Callers should only use string equality checks here, since the syntax may
// be extended in future releases.
"references": [
"data.template_file.foo[1].vars[\"baz\"]",
"data.template_file.foo[1].vars", // implied by previous
"data.template_file.foo[1]", // implied by previous
"data.template_file.foo", // implied by previous
"module.foo.bar",
"module.foo", // implied by the previous
"var.example[0]",
"var.example", // implied by the previous
// Partial references like "data" and "module" are not included, because
// OpenTofu considers "module.foo" to be an atomic reference, not an
// attribute access.
]
}
dynamic
块中的表达式不包含在配置表示中。
块表达式表示
在某些情况下,必须表示整个块的内容(可能在某些特殊参数已被处理和移除后)。为此,我们有一个<block-expressions-representation>
结构。
{
// Attribute arguments are mapped directly with the attribute name as key and
// an <expression-representation> as value.
"ami": <expression-representation>,
"instance_type": <expression-representation>,
// Nested block arguments are mapped as either a single nested
// <block-expressions-representation> or an array object of these, depending on the
// block nesting mode chosen in the schema.
// - "single" nesting is a direct <block-expressions-representation>
// - "list" and "set" produce arrays
// - "map" produces an object
"root_block_device": <expression-representation>,
"ebs_block_device": [
<expression-representation>
]
}
目前,我们期望调用者仅对特定资源类型的模式进行硬编码假设,以便处理这些表达式表示。在以后的版本中,我们将添加新的检查命令以返回模式本身的机器可读描述,从而允许在可视化工具等程序中进行更通用的处理。
更改表示
一个<change-representation>
描述了对指示对象的更改。
{
// "actions" are the actions that will be taken on the object selected by the
// properties below.
// Valid actions values are:
// ["no-op"]
// ["create"]
// ["read"]
// ["update"]
// ["delete", "create"]
// ["create", "delete"]
// ["delete"]
// ["forget"]
// The two "replace" actions are represented in this way to allow callers to
// e.g. just scan the list for "delete" to recognize all three situations
// where the object will be deleted, allowing for any new deletion
// combinations that might be added in future.
"actions": ["update"],
// Before and After are representations of the object value both before and
// after the action. For ["delete"] and ["forget"] actions, the "after"
// value is unset. For ["create"] the "before" is unset. For ["no-op"], the
// before and after values are identical. The "after" value will be
// incomplete if there are values within it that won't be known until after
// apply.
"before": <value-representation>,
"after": <value-representation>,
// "after_unknown" is an object value with similar structure to "after", but
// with all unknown leaf values replaced with "true", and all known leaf
// values omitted. This can be combined with "after" to reconstruct a full
// value after the action, including values which will only be known after
// apply.
"after_unknown": {
"id": true
},
// "before_sensitive" and "after_sensitive" are object values with similar
// structure to "before" and "after", but with all sensitive leaf values
// replaced with true, and all non-sensitive leaf values omitted. These
// objects should be combined with "before" and "after" to prevent accidental
// display of sensitive values in user interfaces.
"before_sensitive": {},
"after_sensitive": {
"triggers": {
"boop": true
}
},
// "replace_paths" is an array of arrays representing a set of paths into the
// object value which resulted in the action being "replace". This will be
// omitted if the action is not replace, or if no paths caused the
// replacement (for example, if the resource was tainted). Each path
// consists of one or more steps, each of which will be a number or a
// string.
"replace_paths": [["triggers"]]
}
检查表示
检查的 JSON 表示是实验性的,一些细节可能会根据反馈在未来的 OpenTofu 版本中更改,即使是在 OpenTofu CLI 的次要版本中。
一个<checks-representation>
描述了配置中可检查对象的当前状态。例如,具有一个或多个先决条件或后置条件的资源是可检查对象的示例,其检查状态表示这些条件的结果。
[
{
// "address" describes the address of the checkable object whose status
// this object is describing.
"address": {
// "kind" specifies what kind of checkable object this is. Different
// kinds of object will have different additional properties inside the
// address object, but all kinds include both "kind" and "to_display".
// The two valid kinds are "resource" and "output_value".
"kind": "resource",
// "to_display" contains an opaque string representation of the address
// of the object that is suitable for display in a UI. For consumers that
// have special handling depending on the value of "kind", this property
// is a good fallback to use when the application doesn't recognize the
// "kind" value.
"to_display": "aws_instance.example",
// "mode" is included for kind "resource" only, and specifies the resource
// mode which can either be "managed" (for "resource" blocks) or "data"
// (for "data" blocks).
"mode": "managed",
// "type" is included for kind "resource" only, and specifies the resource
// type.
"type": "aws_instance",
// "name" is the local name of the object. For a resource this is the
// second label in the resource block header, and for an output value
// this is the single label in the output block header.
"name": "example",
// "module" is included if the object belongs to a module other than
// the root module, and provides an opaque string representation of the
// module this object belongs to. This example is of a root module
// resource and so "module" is not included.
}
// "status" is the aggregate status of all of the instances of the object
// being described by this object.
// The possible values are "pass", "fail", "error", and "unknown".
"status": "fail",
// "instances" describes the current status of each of the instances of
// the object being described. An object can have multiple instances if
// it is either a resource which has "count" or "for_each" set, or if
// it's contained within a module that has "count" or "for_each" set.
//
// If "instances" is empty or omitted, that can either mean that the object
// has no instances at all (e.g. count = 0) or that an error blocked
// evaluation of the repetition argument. You can distinguish these cases
// using the "status" property, which will be "pass" or "error" for a
// zero-instance object and "unknown" for situations where an error blocked
// evalation.
"instances": [
{
// "address" is an object similar to the property of the same name in
// the containing object. Merge the instance-level address into the
// object-level address, overwriting any conflicting property names,
// to create a full description of the instance's address.
"address": {
// "to_display" overrides the property of the same name in the main
// object's address, to include any module instance or resource
// instance keys that uniquely identify this instance.
"to_display": "aws_instance.example[0]",
// "instance_key" is included for resources only and specifies the
// resource-level instance key, which can either be a number or a
// string. Omitted for single-instance resources.
"instance_key": 0,
// "module" is included if the object belongs to a module other than
// the root module, and provides an opaque string representation of the
// module instance this object belongs to.
},
// "status" describes the result of running the configured checks
// against this particular instance of the object, with the same
// possible values as the "status" in the parent object.
//
// "fail" means that the condition evaluated successfully but returned
// false, while "error" means that the condition expression itself
// was invalid.
"status": "fail",
// "problems" might be included for statuses "fail" or "error", in
// which case it describes the individual conditions that failed for
// this instance, if any.
// When a condition expression is invalid, OpenTofu returns that as
// a normal error message rather than as a problem in this list.
"problems": [
{
// "message" is the string that resulted from evaluating the
// error_message argument of the failing condition.
"message": "Server does not have a public IPv6 address."
}
]
},
]
}
]
"checks" 模型同时包含静态可检查对象和这些对象的实例,以确保即使错误阻止了配置的完整评估,可检查对象的集合也将保持一致。配置中的任何具有关联检查的对象(例如具有先决条件或后置条件的资源)都将始终包含为可检查对象,即使运行时错误阻止 OpenTofu 评估其“count”或“for_each”参数,从而确定该对象的哪些实例动态存在。
在 UI 中汇总检查时,我们建议优先仅列出各个实例,通常忽略顶级对象。但是,在任何对象没有实例的情况下,UI 应该显示顶级对象作为占位符,以便用户可以看到 OpenTofu 识别了检查的存在,即使它无法在最近的运行中对其进行评估。