website/docs/internals/machine-readable-ui.mdx
By default, many OpenTofu commands display UI output as unstructured text, intended to be read by a user via a terminal emulator. This text stream is not a stable interface for integrations. Some commands support a -json flag, which enables a structured JSON output mode with a defined interface.
For long-running commands such as plan, apply, and refresh the -json flag outputs a stream of JSON UI messages, one per line. These can be processed one message at a time, with integrating software filtering, combining, or modifying the output as desired.
The first message output has type version, and includes a ui key, which has
value "1.0". The semantics of this version are:
"1.1", for backward-compatible
changes or additions. Ignore any object properties with unrecognized names to
remain forward-compatible with future minor versions."2.0", for changes that are not
backward-compatible. Reject any input which reports an unsupported major
version.We will introduce new major versions only within the bounds of the OpenTofu 1.0 Compatibility Promises.
Below is sample output from running tofu apply -json:
{"@level":"info","@message":"OpenTofu 1.6.0","@module":"tofu.ui","@timestamp":"2021-05-25T13:32:41.275359-04:00","tofu":"0.15.4","type":"version","ui":"0.1.0"}
{"@level":"info","@message":"random_pet.animal: Plan to create","@module":"tofu.ui","@timestamp":"2021-05-25T13:32:41.705503-04:00","change":{"resource":{"addr":"random_pet.animal","module":"","resource":"random_pet.animal","implied_provider":"random","resource_type":"random_pet","resource_name":"animal","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"tofu.ui","@timestamp":"2021-05-25T13:32:41.705638-04:00","changes":{"add":1,"change":0,"remove":0,"operation":"plan"},"type":"change_summary"}
{"@level":"info","@message":"random_pet.animal: Creating...","@module":"tofu.ui","@timestamp":"2021-05-25T13:32:41.825308-04:00","hook":{"resource":{"addr":"random_pet.animal","module":"","resource":"random_pet.animal","implied_provider":"random","resource_type":"random_pet","resource_name":"animal","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"random_pet.animal: Creation complete after 0s [id=smart-lizard]","@module":"tofu.ui","@timestamp":"2021-05-25T13:32:41.826179-04:00","hook":{"resource":{"addr":"random_pet.animal","module":"","resource":"random_pet.animal","implied_provider":"random","resource_type":"random_pet","resource_name":"animal","resource_key":null},"action":"create","id_key":"id","id_value":"smart-lizard","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 0 destroyed.","@module":"tofu.ui","@timestamp":"2021-05-25T13:32:41.869168-04:00","changes":{"add":1,"change":0,"remove":0,"operation":"apply"},"type":"change_summary"}
{"@level":"info","@message":"Outputs: 1","@module":"tofu.ui","@timestamp":"2021-05-25T13:32:41.869280-04:00","outputs":{"pets":{"sensitive":false,"type":"string","value":"smart-lizard"}},"type":"outputs"}
Each line consists of a JSON object with several keys common to all messages. These are:
@level: this is normally "info", but can be "error" or "warn" when showing diagnostics@message: a human-readable summary of the contents of this message@module: always "tofu.ui" when rendering UI output@timestamp: an RFC3339 timestamp of when the message was outputtype: defines which kind of message this is and determines how to interpret other keys which may be presentClients presenting the logs as a user interface should handle unexpected message types by presenting at least the @message field to the user.
Messages will be emitted as events occur to trigger them. This means that messages related to several resources may be interleaved (if OpenTofu is running with concurrency above 1). The resource object value can be used to link multiple messages about a single resource.
The following message types are supported:
version: information about the OpenTofu version and the version of the schema used for the following messageslog: unstructured human-readable log linesdiagnostic: diagnostic warning or error messages; see the tofu validate docs for more details on the formatresource_drift: describes a detected change to a single resource made outside of OpenTofuplanned_change: describes a planned change to a single resourcechange_summary: summary of all planned or applied changesoutputs: list of all root module outputsapply_start, apply_progress, apply_complete, apply_errored: sequence of messages indicating progress of a single resource through applyprovision_start, provision_progress, provision_complete, provision_errored: sequence of messages indicating progress of a single provisioner steprefresh_start, refresh_complete: sequence of messages indicating progress of a single resource through refreshA machine-readable UI command output will always begin with a version message. The following message-specific keys are defined:
tofu: the OpenTofu version which emitted this messageui: the machine-readable UI schema version defining the meaning of the following messages{
"@level": "info",
"@message": "OpenTofu 0.15.4",
"@module": "tofu.ui",
"@timestamp": "2021-05-25T13:32:41.275359-04:00",
"tofu": "0.15.4",
"type": "version",
"ui": "0.1.0"
}
If drift is detected during planning, OpenTofu will emit a resource_drift message for each resource which has changed outside of OpenTofu. This message has an embedded change object with the following keys:
resource: object describing the address of the resource to be changed; see resource object below for detailsaction: the action planned to be taken for the resource. Values: update, delete.This message does not include details about the exact changes which caused the change to be planned. That information is available in the JSON plan output.
{
"@level": "info",
"@message": "random_pet.animal: Drift detected (update)",
"@module": "tofu.ui",
"@timestamp": "2021-05-25T13:32:41.705503-04:00",
"change": {
"resource": {
"addr": "random_pet.animal",
"module": "",
"resource": "random_pet.animal",
"implied_provider": "random",
"resource_type": "random_pet",
"resource_name": "animal",
"resource_key": null
},
"action": "update"
},
"type": "resource_drift"
}
At the end of a plan or before an apply, OpenTofu will emit a planned_change message for each resource which has changes to apply. This message has an embedded change object with the following keys:
resource: object describing the address of the resource to be changed; see resource object below for detailsprevious_resource: object describing the previous address of the resource, if this change includes a configuration-driven moveaction: the action planned to be taken for the resource. Values: noop, create, read, update, replace, delete, move.reason: an optional reason for the change, only used when the action is replace or delete. Values:
tainted: resource was marked as taintedrequested: user requested that the resource be replaced, for example via the -replace plan flagcannot_update: changes to configuration force the resource to be deleted and created rather than updateddelete_because_no_resource_config: no matching resource in configurationdelete_because_wrong_repetition: resource instance key has no corresponding count or for_each in configurationdelete_because_count_index: resource instance key is outside the range of the count argumentdelete_because_each_key: resource instance key is not included in the for_each argumentdelete_because_no_module: enclosing module instance is not in configurationThis message does not include details about the exact changes which caused the change to be planned. That information is available in the JSON plan output.
{
"@level": "info",
"@message": "random_pet.animal: Plan to create",
"@module": "tofu.ui",
"@timestamp": "2021-05-25T13:32:41.705503-04:00",
"change": {
"resource": {
"addr": "random_pet.animal",
"module": "",
"resource": "random_pet.animal",
"implied_provider": "random",
"resource_type": "random_pet",
"resource_name": "animal",
"resource_key": null
},
"action": "create"
},
"type": "planned_change"
}
OpenTofu outputs a change summary when a plan or apply operation completes. Both message types include a changes object, which has the following keys:
add: count of resources to be created (including as part of replacement)change: count of resources to be changed in-placeremove: count of resources to be destroyed (including as part of replacement)operation: one of plan, apply, or destroy{
"@level": "info",
"@message": "Apply complete! Resources: 1 added, 0 changed, 0 destroyed.",
"@module": "tofu.ui",
"@timestamp": "2021-05-25T13:32:41.869168-04:00",
"changes": {
"add": 1,
"change": 0,
"remove": 0,
"operation": "apply"
},
"type": "change_summary"
}
After a successful plan or apply, a message with type outputs contains the values of all root module output values. This message contains an outputs object, the keys of which are the output names. The outputs values are objects with the following keys:
action: for planned outputs, the action which will be taken for the output. Values: noop, create, update, deletevalue: for applied outputs, the value of the output, encoded in JSONtype: for applied outputs, the detected HCL type of the output valuesensitive: boolean value, true if the output is sensitive and should be hidden from UI by defaultNote that sensitive outputs still include the value field, and integrating software should respect the sensitivity value as appropriate for the given use case.
{
"@level": "info",
"@message": "Outputs: 1",
"@module": "tofu.ui",
"@timestamp": "2021-05-25T13:32:41.869280-04:00",
"outputs": {
"pets": {
"sensitive": false,
"type": "string",
"value": "smart-lizard"
}
},
"type": "outputs"
}
Performing OpenTofu operations to a resource will often result in several messages being emitted. The message types include:
apply_start: when starting to apply changes for a resourceapply_progress: periodically, showing elapsed time outputapply_complete: on successful operation completionapply_errored: when an error is encountered during the operationprovision_start: when starting a provisioner stepprovision_progress: on provisioner outputprovision_complete: on successful provisioningprovision_errored: when an error is encountered during provisioningrefresh_start: when reading a resource during refreshrefresh_complete: on successful refreshEach of these messages has a hook object, which has different fields for each type. All hooks have a resource object which identifies which resource is the subject of the operation.
The apply_start message hook object has the following keys:
resource: a resource object identifying the resourceaction: the action to be taken for the resource. Values: noop, create, read, update, replace, deleteid_key and id_value: a key/value pair used to identify this instance of the resource, omitted when unknown{
"@level": "info",
"@message": "random_pet.animal: Creating...",
"@module": "tofu.ui",
"@timestamp": "2021-05-25T13:32:41.825308-04:00",
"hook": {
"resource": {
"addr": "random_pet.animal",
"module": "",
"resource": "random_pet.animal",
"implied_provider": "random",
"resource_type": "random_pet",
"resource_name": "animal",
"resource_key": null
},
"action": "create"
},
"type": "apply_start"
}
The apply_progress message hook object has the following keys:
resource: a resource object identifying the resourceaction: the action being taken for the resource. Values: noop, create, read, update, replace, deleteelapsed_seconds: time elapsed since the apply operation started, expressed as an integer number of seconds{
"@level": "info",
"@message": "null_resource.none[4]: Still creating... [30s elapsed]",
"@module": "tofu.ui",
"@timestamp": "2021-03-17T09:34:26.222465-04:00",
"hook": {
"resource": {
"addr": "null_resource.none[4]",
"module": "",
"resource": "null_resource.none[4]",
"implied_provider": "null",
"resource_type": "null_resource",
"resource_name": "none",
"resource_key": 4
},
"action": "create",
"elapsed_seconds": 30
},
"type": "apply_progress"
}
The apply_complete message hook object has the following keys:
resource: a resource object identifying the resourceaction: the action taken for the resource. Values: noop, create, read, update, replace, deleteid_key and id_value: a key/value pair used to identify this instance of the resource, omitted when unknownelapsed_seconds: time elapsed since the apply operation started, expressed as an integer number of seconds{
"@level": "info",
"@message": "random_pet.animal: Creation complete after 0s [id=smart-lizard]",
"@module": "tofu.ui",
"@timestamp": "2021-05-25T13:32:41.826179-04:00",
"hook": {
"resource": {
"addr": "random_pet.animal",
"module": "",
"resource": "random_pet.animal",
"implied_provider": "random",
"resource_type": "random_pet",
"resource_name": "animal",
"resource_key": null
},
"action": "create",
"id_key": "id",
"id_value": "smart-lizard",
"elapsed_seconds": 0
},
"type": "apply_complete"
}
The apply_complete message hook object has the following keys:
resource: a resource object identifying the resourceaction: the action taken for the resource. Values: noop, create, read, update, replace, deleteelapsed_seconds: time elapsed since the apply operation started, expressed as an integer number of secondsThe exact detail of the error will be rendered as a separate diagnostic message.
{
"@level": "info",
"@message": "null_resource.none[0]: Creation errored after 10s",
"@module": "tofu.ui",
"@timestamp": "2021-03-26T16:38:54.013910-04:00",
"hook": {
"resource": {
"addr": "null_resource.none[0]",
"module": "",
"resource": "null_resource.none[0]",
"implied_provider": "null",
"resource_type": "null_resource",
"resource_name": "none",
"resource_key": 0
},
"action": "create",
"elapsed_seconds": 10
},
"type": "apply_errored"
}
The provision_start message hook object has the following keys:
resource: a resource object identifying the resourceprovisioner: the type of provisioner{
"@level": "info",
"@message": "null_resource.none[0]: Provisioning with 'local-exec'...",
"@module": "tofu.ui",
"@timestamp": "2021-03-26T16:38:43.997431-04:00",
"hook": {
"resource": {
"addr": "null_resource.none[0]",
"module": "",
"resource": "null_resource.none[0]",
"implied_provider": "null",
"resource_type": "null_resource",
"resource_name": "none",
"resource_key": 0
},
"provisioner": "local-exec"
},
"type": "provision_start"
}
The provision_progress message hook object has the following keys:
resource: a resource object identifying the resourceprovisioner: the type of provisioneroutput: the output log from the provisionerOne provision_progress message is output for each log line received from the provisioner.
{
"@level": "info",
"@message": "null_resource.none[0]: (local-exec): Executing: [\"/bin/sh\" \"-c\" \"sleep 10 && exit 1\"]",
"@module": "tofu.ui",
"@timestamp": "2021-03-26T16:38:43.997869-04:00",
"hook": {
"resource": {
"addr": "null_resource.none[0]",
"module": "",
"resource": "null_resource.none[0]",
"implied_provider": "null",
"resource_type": "null_resource",
"resource_name": "none",
"resource_key": 0
},
"provisioner": "local-exec",
"output": "Executing: [\"/bin/sh\" \"-c\" \"sleep 10 && exit 1\"]"
},
"type": "provision_progress"
}
The provision_complete message hook object has the following keys:
resource: a resource object identifying the resourceprovisioner: the type of provisioner{
"@level": "info",
"@message": "null_resource.none[0]: (local-exec) Provisioning complete",
"@module": "tofu.ui",
"@timestamp": "2021-03-17T09:34:06.239043-04:00",
"hook": {
"resource": {
"addr": "null_resource.none[0]",
"module": "",
"resource": "null_resource.none[0]",
"implied_provider": "null",
"resource_type": "null_resource",
"resource_name": "none",
"resource_key": 0
},
"provisioner": "local-exec"
},
"type": "provision_complete"
}
The provision_errored message hook object has the following keys:
resource: a resource object identifying the resourceprovisioner: the type of provisioner{
"@level": "info",
"@message": "null_resource.none[0]: (local-exec) Provisioning errored",
"@module": "tofu.ui",
"@timestamp": "2021-03-26T16:38:54.013572-04:00",
"hook": {
"resource": {
"addr": "null_resource.none[0]",
"module": "",
"resource": "null_resource.none[0]",
"implied_provider": "null",
"resource_type": "null_resource",
"resource_name": "none",
"resource_key": 0
},
"provisioner": "local-exec"
},
"type": "provision_errored"
}
The refresh_start message hook object has the following keys:
resource: a resource object identifying the resourceid_key and id_value: a key/value pair used to identify this instance of the resource{
"@level": "info",
"@message": "null_resource.none[0]: Refreshing state... [id=1971614370559474622]",
"@module": "tofu.ui",
"@timestamp": "2021-03-26T14:18:06.508915-04:00",
"hook": {
"resource": {
"addr": "null_resource.none[0]",
"module": "",
"resource": "null_resource.none[0]",
"implied_provider": "null",
"resource_type": "null_resource",
"resource_name": "none",
"resource_key": 0
},
"id_key": "id",
"id_value": "1971614370559474622"
},
"type": "refresh_start"
}
The refresh_complete message hook object has the following keys:
resource: a resource object identifying the resourceid_key and id_value: a key/value pair used to identify this instance of the resource{
"@level": "info",
"@message": "null_resource.none[0]: Refresh complete [id=1971614370559474622]",
"@module": "tofu.ui",
"@timestamp": "2021-03-26T14:18:06.509371-04:00",
"hook": {
"resource": {
"addr": "null_resource.none[0]",
"module": "",
"resource": "null_resource.none[0]",
"implied_provider": "null",
"resource_type": "null_resource",
"resource_name": "none",
"resource_key": 0
},
"id_key": "id",
"id_value": "1971614370559474622"
},
"type": "refresh_complete"
}
The resource object is a decomposed structure representing a resource address in configuration, which is used to identify which resource a given message is associated with. The object has the following keys:
addr: the full unique address of the resource as a stringmodule: the address of the module containing the resource, in the form module.foo.module.bar, or an empty string for a root module resourceresource: the module-relative address, which is identical to addr for root module resourcesresource_type: the type of resource being addressedresource_name: the name label for the resourceresource_key: the address key (count or for_each value), or null if the neither are usedimplied_provider: the provider type implied by the resource type; this may not reflect the resource's provider if provider aliases are used{
"addr": "module.pets.random_pet.pet[\"friend\"]",
"module": "module.pets",
"resource": "random_pet.pet[\"friend\"]",
"implied_provider": "random",
"resource_type": "random_pet",
"resource_name": "pet",
"resource_key": "friend"
}
Running OpenTofu tests will result in several messages being emitted. The message types include:
test_abstract: Summary of test files and tests foundtest_file: Summary of test file executiontest_run: Summary of test executiontest_summary: Summary of overall test file execution status and statisticsThe test_abstract message test_abstract object contains dynamic keys composed of the test file name:
main.tftest.hcl: list of tests found within the test file{
"@level": "info",
"@message": "Found 1 file and 1 run block",
"@module": "tofu.ui",
"@timestamp": "2024-04-20T17:24:48.418126+10:00",
"test_abstract": {
"main.tftest.hcl": [
"test"
]
},
"type": "test_abstract"
}
The test_file message test_file object has the following keys:
path: the relative path of the test filestatus: the overall test execution status{
"@level": "info",
"@message": "main.tftest.hcl... pass",
"@module": "tofu.ui",
"@testfile": "main.tftest.hcl",
"@timestamp": "2024-04-20T17:24:48.588473+10:00",
"test_file": {
"path": "main.tftest.hcl",
"status": "pass"
},
"type": "test_file"
}
The test_run message test_run object has the following keys:
path: the relative path of the test filerun: name of test that was executedstatus: the overall test execution status{
"@level": "info",
"@message": " \"test\"... pass",
"@module": "tofu.ui",
"@testfile": "main.tftest.hcl",
"@testrun": "test",
"@timestamp": "2024-04-20T17:24:48.588519+10:00",
"test_run": {
"path": "main.tftest.hcl",
"run": "test",
"status": "pass"
},
"type": "test_run"
}
The test_summary message test_summary object has the following keys:
status: the overall status of all tests executedpassed: the total number of tests that passedfailed: the total number of tests that failederrored: the total number of tests that erroredskipped: the total number of tests that skipped{
"@level": "info",
"@message": "Success! 1 passed, 0 failed.",
"@module": "tofu.ui",
"@timestamp": "2024-04-20T17:24:48.716977+10:00",
"test_summary": {
"status": "pass",
"passed": 1,
"failed": 0,
"errored": 0,
"skipped": 0
},
"type": "test_summary"
}