Terraform 0.11 and earlier had two formats for communicating planned changes: printing human-readable output to the terminal, or printing a binary plan file intended to be read only by Terraform.
This binary plan file is designed so that in principle we could document it as stable in the long run, but in practice it is required to encode very precise details about a Terraform plan, which makes it hard to make any compatibility promises in the face of changes to Terraform internals. We will continue to consider it an opaque format for the foreseeable future.
To meet the need for machine consumption of plan data by code outside of Terraform CLI, Terraform 0.12 introduces a third format: a JSON summary of the plan designed for consumption by external software. The structure of this format -- described in detail in subsequent sections -- is intended to represent sufficient details for external tools to analyze the implications of the plan but to do so at an end-user-oriented level of abstraction that is less likely to see significant breaking changes in future versions of Terraform.
**NOTE**
The JSON format is experimental and subject to change. A "format_version" key is included in the output. The minor version portion will be incremented for compatible additions to the output, and the major version portion will be incremented for any format changes which require mandatory changes for correct processing.
The full structure of this file will be discussed by example in parts in the following sub-sections, followed by any additional constraints or considerations that cannot be reflected directly in the examples. We will use a pseudo-JSON notation with `<references>` to indicate how these different portions of the output are combined without excessive repetition.
The JSON representation of a Terraform Planfile or Statefile is generated by running [`terraform show -json $filename`](/docs/commands/show.html) (if `$filename` is omitted, terraform will output the json representation of the current state).
The "values" object, described here, is used in the "state" and "plan" sections to describe current state (which is always complete) and planned state (which will omit values not known until apply) values respectively.
// "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 config-specific
// structures described in later sections.
"values": {
"id": "i-abc123",
"instance_type": "t2.micro",
// etc, etc
}
]
"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 Terraform 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": [ ... ],
}
]
}
}
```
The translation of attribute and output values is the same intuitive mapping from HCL types to JSON types used by Terraform's jsonencode function. This mapping does lose some information: lists, sets, and tuples all lower to JSON arrays while maps and objects both lower to JSON objects. Unknown values and null values are both treated as absent or null. We assume that consumers of this simplified structure do not need to distinguish these cases; in the rare situation where exact representations are required, the Terraform's native storage file formats can be parsed directly (in a later release, once we make compatibility promises for them).
Only the "current" object for each resource instance is described. "Deposed" objects are not reflected in this structure at all, and so callers that wish to work with these must again use the native storage formats, or (in the case of the plan) refer to the plan-specific "changes" structure that includes changes to deposed objects.
The intent of this structure is to give a caller access to a similar level of detail as is available to expressions within the configuration itself. This common representation is not suitable for all use-cases because it loses information compared to the data structures it is built from. For more complex needs, the more elaborate plan and configuration data structures, described in later sections, should be used.
## State Representation
Because the state does not currently have any significant additional metadata not covered by the common values representation in the prior section, the `<state-representation>` is straightforward:
Because the configuration models are produced at a stage prior to expression evaluation, it is not possible to produce a common values representation for configuration. Instead, we describe the physical structure of the configuration, giving access to constant values where possible and allowing callers to analyze any references to other objects that are present, in the `<configuration-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 the common 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
// Terraform considers "module.foo" to be an atomic reference, not an
// attribute access.
]
}
```
### Expressions Representation
In some cases, it is the entire content of a block (possibly after certain special arguments have already been handled and removed) that must be represented. For that, we have an `<expressions-representation>` structure:
// <expressions-representation> or an array object of these, depending on the
// block nesting mode chosen in the schema.
// - "single" nesting is a direct <expressions-representation>
// - "list" and "set" produce arrays
// - "map" produces an object
"root_block_device": <expression-representation>,
"ebs_block_device": [
<expression-representation>
],
"potential_future_map_block": {
"foo": <expression-representation>
}
}
```
For now we expect callers to just hard-code assumptions about the schemas of particular resource types in order to process these expression representations. In a later release we will add new inspection commands to return machine-readable descriptions of the schemas themselves, allowing for more generic handling in programs such as visualization tools.
A plan consists of a prior state, the configuration that is being applied to that state, and the set of changes Terraform plans to make to achieve that. For ease of consumption by callers, our representation of plans will additionally include a partial representation of the values in the final state using the common value representation, allowing callers to easily analyze the planned outcome using similar code as for analyzing the prior state.