587 lines
21 KiB
Markdown
587 lines
21 KiB
Markdown
---
|
|
layout: "docs"
|
|
page_title: "Internals: Machine-Readable UI"
|
|
sidebar_current: "docs-internals-machine-readable-ui"
|
|
description: |-
|
|
Terraform provides a machine-readable streaming JSON UI output for plan, apply, and refresh operations.
|
|
---
|
|
|
|
# Machine-Readable UI
|
|
|
|
-> **Note:** This format is available in Terraform 0.15.3 and later.
|
|
|
|
By default, many Terraform 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.
|
|
|
|
-> **Note:** The first message output has type `version`, and includes a `ui` key, which currently has major version zero to indicate that the format is experimental and subject to change. A future version will assign a non-zero major version and make stronger promises about compatibility. We do not anticipate any significant breaking changes to the format before its first major version, however.
|
|
|
|
## Sample JSON Output
|
|
|
|
Below is sample output from running `terraform apply -json`:
|
|
|
|
```javascript
|
|
{"@level":"info","@message":"Terraform 0.15.4","@module":"terraform.ui","@timestamp":"2021-05-25T13:32:41.275359-04:00","terraform":"0.15.4","type":"version","ui":"0.1.0"}
|
|
{"@level":"info","@message":"random_pet.animal: Plan to create","@module":"terraform.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":"terraform.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":"terraform.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":"terraform.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":"terraform.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":"terraform.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 "terraform.ui" when rendering UI output
|
|
- `@timestamp`: an RFC3339 timestamp of when the message was output
|
|
- `type`: defines which kind of message this is and determines how to interpret other keys which may be present
|
|
|
|
Clients 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 Terraform is running with concurrency above 1). The [`resource` object value](#resource-object) can be used to link multiple messages about a single resource.
|
|
|
|
## Message Types
|
|
|
|
The following message types are supported:
|
|
|
|
### Generic Messages
|
|
|
|
- `version`: information about the Terraform version and the version of the schema used for the following messages
|
|
- `log`: unstructured human-readable log lines
|
|
- `diagnostic`: diagnostic warning or error messages; [see the `terraform validate` docs for more details on the format](/docs/cli/commands/validate.html#json)
|
|
|
|
### Operation Results
|
|
|
|
- `resource_drift`: describes a detected change to a single resource made outside of Terraform
|
|
- `planned_change`: describes a planned change to a single resource
|
|
- `change_summary`: summary of all planned or applied changes
|
|
- `outputs`: list of all root module outputs
|
|
|
|
### Resource Progress
|
|
|
|
- `apply_start`, `apply_progress`, `apply_complete`, `apply_errored`: sequence of messages indicating progress of a single resource through apply
|
|
- `provision_start`, `provision_progress`, `provision_complete`, `provision_errored`: sequence of messages indicating progress of a single provisioner step
|
|
- `refresh_start`, `refresh_complete`: sequence of messages indicating progress of a single resource through refresh
|
|
|
|
## Version Message
|
|
|
|
A machine-readable UI command output will always begin with a `version` message. The following message-specific keys are defined:
|
|
|
|
- `terraform`: the Terraform version which emitted this message
|
|
- `ui`: the machine-readable UI schema version defining the meaning of the following messages
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "Terraform 0.15.4",
|
|
"@module": "terraform.ui",
|
|
"@timestamp": "2021-05-25T13:32:41.275359-04:00",
|
|
"terraform": "0.15.4",
|
|
"type": "version",
|
|
"ui": "0.1.0"
|
|
}
|
|
```
|
|
|
|
## Resource Drift
|
|
|
|
If drift is detected during planning, Terraform will emit a `resource_drift` message for each resource which has changed outside of Terraform. 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](#resource-object) below for details
|
|
- `action`: 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](./json-format.html).
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "random_pet.animal: Drift detected (update)",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Planned Change
|
|
|
|
At the end of a plan or before an apply, Terraform 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](#resource-object) below for details
|
|
- `previous_resource`: object describing the previous address of the resource, if this change includes a configuration-driven move
|
|
- `action`: the action planned to be taken for the resource. Values: `noop`, `create`, `read`, `update`, `replace`, `delete`, `move`.
|
|
- `reason`: an optional reason for the change, currently only used when the action is `replace`. Values:
|
|
- `tainted`: resource was marked as tainted
|
|
- `requested`: user requested that the resource be replaced, for example via the `-replace` plan flag
|
|
- `cannot_update`: changes to configuration force the resource to be deleted and created rather than updated
|
|
|
|
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](./json-format.html).
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "random_pet.animal: Plan to create",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Change Summary
|
|
|
|
Terraform 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-place
|
|
- `remove`: count of resources to be destroyed (including as part of replacement)
|
|
- `operation`: one of `plan`, `apply`, or `destroy`
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "Apply complete! Resources: 1 added, 0 changed, 0 destroyed.",
|
|
"@module": "terraform.ui",
|
|
"@timestamp": "2021-05-25T13:32:41.869168-04:00",
|
|
"changes": {
|
|
"add": 1,
|
|
"change": 0,
|
|
"remove": 0,
|
|
"operation": "apply"
|
|
},
|
|
"type": "change_summary"
|
|
}
|
|
```
|
|
|
|
## Outputs
|
|
|
|
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`, `delete`
|
|
- `value`: for applied outputs, the value of the output, encoded in JSON
|
|
- `type`: for applied outputs, the detected HCL type of the output value
|
|
- `sensitive`: boolean value, `true` if the output is sensitive and should be hidden from UI by default
|
|
|
|
Note that `sensitive` outputs still include the `value` field, and integrating software should respect the sensitivity value as appropriate for the given use case.
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "Outputs: 1",
|
|
"@module": "terraform.ui",
|
|
"@timestamp": "2021-05-25T13:32:41.869280-04:00",
|
|
"outputs": {
|
|
"pets": {
|
|
"sensitive": false,
|
|
"type": "string",
|
|
"value": "smart-lizard"
|
|
}
|
|
},
|
|
"type": "outputs"
|
|
}
|
|
```
|
|
|
|
## Operation Messages
|
|
|
|
Performing Terraform 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 resource
|
|
- `apply_progress`: periodically, showing elapsed time output
|
|
- `apply_complete`: on successful operation completion
|
|
- `apply_errored`: when an error is encountered during the operation
|
|
- `provision_start`: when starting a provisioner step
|
|
- `provision_progress`: on provisioner output
|
|
- `provision_complete`: on successful provisioning
|
|
- `provision_errored`: when an error is enountered during provisioning
|
|
- `refresh_start`: when reading a resource during refresh
|
|
- `refresh_complete`: on successful refresh
|
|
|
|
Each of these messages has a `hook` object, which has different fields for each type. All hooks have a [`resource` object](#resource-object) which identifies which resource is the subject of the operation.
|
|
|
|
## Apply Start
|
|
|
|
The `apply_start` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `action`: the action to be taken for the resource. Values: `noop`, `create`, `read`, `update`, `replace`, `delete`
|
|
- `id_key` and `id_value`: a key/value pair used to identify this instance of the resource, omitted when unknown
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "random_pet.animal: Creating...",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Apply Progress
|
|
|
|
The `apply_progress` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `action`: the action being taken for the resource. Values: `noop`, `create`, `read`, `update`, `replace`, `delete`
|
|
- `elapsed_seconds`: time elapsed since the apply operation started, expressed as an integer number of seconds
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "null_resource.none[4]: Still creating... [30s elapsed]",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Apply Complete
|
|
|
|
The `apply_complete` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `action`: the action taken for the resource. Values: `noop`, `create`, `read`, `update`, `replace`, `delete`
|
|
- `id_key` and `id_value`: a key/value pair used to identify this instance of the resource, omitted when unknown
|
|
- `elapsed_seconds`: time elapsed since the apply operation started, expressed as an integer number of seconds
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "random_pet.animal: Creation complete after 0s [id=smart-lizard]",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Apply Errored
|
|
|
|
The `apply_complete` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `action`: the action taken for the resource. Values: `noop`, `create`, `read`, `update`, `replace`, `delete`
|
|
- `elapsed_seconds`: time elapsed since the apply operation started, expressed as an integer number of seconds
|
|
|
|
The exact detail of the error will be rendered as a separate `diagnostic` message.
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "null_resource.none[0]: Creation errored after 10s",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Provision Start
|
|
|
|
The `provision_start` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `provisioner`: the type of provisioner
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "null_resource.none[0]: Provisioning with 'local-exec'...",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Provision Progress
|
|
|
|
The `provision_progress` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `provisioner`: the type of provisioner
|
|
- `output`: the output log from the provisioner
|
|
|
|
One `provision_progress` message is output for each log line received from the provisioner.
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "null_resource.none[0]: (local-exec): Executing: [\"/bin/sh\" \"-c\" \"sleep 10 && exit 1\"]",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Provision Complete
|
|
|
|
The `provision_complete` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `provisioner`: the type of provisioner
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "null_resource.none[0]: (local-exec) Provisioning complete",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Provision Errored
|
|
|
|
The `provision_errored` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `provisioner`: the type of provisioner
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "null_resource.none[0]: (local-exec) Provisioning errored",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Refresh Start
|
|
|
|
The `refresh_start` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `id_key` and `id_value`: a key/value pair used to identify this instance of the resource
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "null_resource.none[0]: Refreshing state... [id=1971614370559474622]",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Refresh Complete
|
|
|
|
The `refresh_complete` message `hook` object has the following keys:
|
|
|
|
- `resource`: a [`resource` object](#resource-object) identifying the resource
|
|
- `id_key` and `id_value`: a key/value pair used to identify this instance of the resource
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"@level": "info",
|
|
"@message": "null_resource.none[0]: Refresh complete [id=1971614370559474622]",
|
|
"@module": "terraform.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"
|
|
}
|
|
```
|
|
|
|
## Resource Object
|
|
|
|
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 string
|
|
- `module`: the address of the module containing the resource, in the form `module.foo.module.bar`, or an empty string for a root module resource
|
|
- `resource`: the module-relative address, which is identical to `addr` for root module resources
|
|
- `resource_type`: the type of resource being addressed
|
|
- `resource_name`: the name label for the resource
|
|
- `resource_key`: the address key (`count` or `for_each` value), or `null` if the neither are used
|
|
- `implied_provider`: the provider type implied by the resource type; this may not reflect the resource's provider if provider aliases are used
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"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"
|
|
}
|
|
```
|