github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/docs/plugin-protocol/object-wire-format.md (about) 1 # Wire Format for Terraform Objects and Associated Values 2 3 The provider wire protocol (as of major version 5) includes a protobuf message 4 type `DynamicValue` which Terraform uses to represent values from the Terraform 5 Language type system, which result from evaluating the content of `resource`, 6 `data`, and `provider` blocks, based on a schema defined by the corresponding 7 provider. 8 9 Because the structure of these values is determined at runtime, `DynamicValue` 10 uses one of two possible dynamic serialization formats for the values 11 themselves: MessagePack or JSON. Terraform most commonly uses MessagePack, 12 because it offers a compact binary representation of a value. However, a server 13 implementation of the provider protocol should fall back to JSON if the 14 MessagePack field is not populated, in order to support both formats. 15 16 The remainder of this document describes how Terraform translates from its own 17 type system into the type system of the two supported serialization formats. 18 A server implementation of the Terraform provider protocol can use this 19 information to decode `DynamicValue` values from incoming messages into 20 whatever representation is convenient for the provider implementation. 21 22 A server implementation must also be able to _produce_ `DynamicValue` messages 23 as part of various response messages. When doing so, servers should always 24 use MessagePack encoding, because Terraform does not consistently support 25 JSON responses across all request types and all Terraform versions. 26 27 Both the MessagePack and JSON serializations are driven by information the 28 provider previously returned in a `Schema` message. Terraform will encode each 29 value depending on the type constraint given for it in the corresponding schema, 30 using the closest possible MessagePack or JSON type to the Terraform language 31 type. Therefore a server implementation can decode a serialized value using a 32 standard MessagePack or JSON library and assume it will conform to the 33 serialization rules described below. 34 35 ## MessagePack Serialization Rules 36 37 The MessagePack types referenced in this section are those defined in 38 [The MessagePack type system specification](https://github.com/msgpack/msgpack/blob/master/spec.md#type-system). 39 40 Note that MessagePack defines several possible serialization formats for each 41 type, and Terraform may choose any of the formats of a specified type. 42 The exact serialization chosen for a given value may vary between Terraform 43 versions, but the types given here are contractual. 44 45 Conversely, server implementations that are _producing_ MessagePack-encoded 46 values are free to use any of the valid serialization formats for a particular 47 type. However, we recommend choosing the most compact format that can represent 48 the value without a loss of range. 49 50 ### `Schema.Block` Mapping Rules for MessagePack 51 52 To represent the content of a block as MessagePack, Terraform constructs a 53 MessagePack map that contains one key-value pair per attribute and one 54 key-value pair per distinct nested block described in the `Schema.Block` message. 55 56 The key-value pairs representing attributes have values based on 57 [the `Schema.Attribute` mapping rules](#Schema.Attribute-mapping-rules-for-messagepack). 58 The key-value pairs representing nested block types have values based on 59 [the `Schema.NestedBlock` mapping rules](#Schema.NestedBlock-mapping-rules-for-messagepack). 60 61 ### `Schema.Attribute` Mapping Rules for MessagePack 62 63 The MessagePack serialization of an attribute value depends on the value of the 64 `type` field of the corresponding `Schema.Attribute` message. The `type` field is 65 a compact JSON serialization of a 66 [Terraform type constraint](https://www.terraform.io/docs/configuration/types.html), 67 which consists either of a single 68 string value (for primitive types) or a two-element array giving a type kind 69 and a type argument. 70 71 The following table describes the type-specific mapping rules. Along with those 72 type-specific rules there are two special rules that override the mappings 73 in the table below, regardless of type: 74 75 * A null value is represented as a MessagePack nil value. 76 * An unknown value (that is, a placeholder for a value that will be decided 77 only during the apply operation) is represented as a 78 [MessagePack extension](https://github.com/msgpack/msgpack/blob/master/spec.md#extension-types) 79 value, described in more detail below. 80 81 | `type` Pattern | MessagePack Representation | 82 |---|---| 83 | `"string"` | A MessagePack string containing the Unicode characters from the string value serialized as normalized UTF-8. | 84 | `"number"` | Either MessagePack integer, MessagePack float, or MessagePack string representing the number. If a number is represented as a string then the string contains a decimal representation of the number which may have a larger mantissa than can be represented by a 64-bit float. | 85 | `"bool"` | A MessagePack boolean value corresponding to the value. | 86 | `["list",T]` | A MessagePack array with the same number of elements as the list value, each of which is represented by the result of applying these same mapping rules to the nested type `T`. | 87 | `["set",T]` | Identical in representation to `["list",T]`, but the order of elements is undefined because Terraform sets are unordered. | 88 | `["map",T]` | A MessagePack map with one key-value pair per element of the map value, where the element key is serialized as the map key (always a MessagePack string) and the element value is represented by a value constructed by applying these same mapping rules to the nested type `T`. | 89 | `["object",ATTRS]` | A MessagePack map with one key-value pair per attribute defined in the `ATTRS` object. The attribute name is serialized as the map key (always a MessagePack string) and the attribute value is represented by a value constructed by applying these same mapping rules to each attribute's own type. | 90 | `["tuple",TYPES]` | A MessagePack array with one element per element described by the `TYPES` array. The element values are constructed by applying these same mapping rules to the corresponding element of `TYPES`. | 91 | `"dynamic"` | A MessagePack array with exactly two elements. The first element is a MessagePack binary value containing a JSON-serialized type constraint in the same format described in this table. The second element is the result of applying these same mapping rules to the value with the type given in the first element. This special type constraint represents values whose types will be decided only at runtime. | 92 93 Unknown values have two possible representations, both using 94 [MessagePack extension](https://github.com/msgpack/msgpack/blob/master/spec.md#extension-types) 95 values. 96 97 The older encoding is for unrefined unknown values and uses an extension 98 code of zero, with the extension value payload completely ignored. 99 100 Newer Terraform versions can produce "refined" unknown values which carry some 101 additional information that constrains the possible range of the final value/ 102 Refined unknown values have extension code 12 and then the extension object's 103 payload is a MessagePack-encoded map using integer keys to represent different 104 kinds of refinement: 105 106 * `1` represents "nullness", and the value of that key will be a boolean 107 value that is true if the value is definitely null or false if it is 108 definitely not null. If this key isn't present at all then the value may or 109 may not be null. It's not actually useful to encode that an unknown value 110 is null; use a known null value instead in that case, because there is only 111 one null value of each type. 112 * `2` represents string prefix, and the value is a string that the final 113 value is known to begin with. This is valid only for unknown values of string 114 type. 115 * `3` and `4` represent the lower and upper bounds respectively of a number 116 value, and the value of both is a two-element msgpack array whose 117 first element is a valid encoding of a number (as in the table above) 118 and whose second element is a boolean value that is true for an inclusive 119 bound and false for an exclusive bound. This is valid only for unknown values 120 of number type. 121 * `5` and `6` represent the lower and upper bounds respectively of the length 122 of a collection value. The value of both is an integer representing an 123 inclusive bound. This is valid only for unknown values of the three kinds of 124 collection types: list, set, and map. 125 126 Unknown value refinements are an optional way to reduce the range of possible 127 values for situations where that makes it possible to produce a known result 128 for unknown inputs or where it allows detecting an error during the planning 129 phase that would otherwise be detected only during the apply phase. It's always 130 safe to ignore refinements and just treat an unknown value as wholly unknown, 131 but considering refinements may allow a more precise answer. A provider that 132 produces refined values in its planned new state (from `PlanResourceChange`) 133 _must_ honor those refinements in the final state (from `ApplyResourceChange`). 134 135 Unmarshalling code should ignore refinement map keys that they don't know about, 136 because future versions of the protocol might define additional refinements. 137 138 When encoding an unknown value without any refinements, always use the older 139 format with extension code zero instead of using extension code 12 with an 140 empty refinement map. Any refined unknown value _must_ have at least one 141 refinement map entry. This rule ensures backward compatibility with older 142 implementations that predate the value refinements concept. 143 144 A server implementation of the protocol should treat _any_ MessagePack extension 145 code as representing an unknown value, but should ignore the payload of that 146 extension value entirely unless the extension code is 12 to indicate that 147 the body represents refinements. Future versions of this protocol may define 148 specific formats for other extension codes, but they will always represent 149 unknown values. 150 151 ### `Schema.NestedBlock` Mapping Rules for MessagePack 152 153 The MessagePack serialization of a collection of blocks of a particular type 154 depends on the `nesting` field of the corresponding `Schema.NestedBlock` message. 155 The `nesting` field is a value from the `Schema.NestingBlock.NestingMode` 156 enumeration. 157 158 All `nesting` values cause the individual blocks of a type to be represented 159 by applying 160 [the `Schema.Block` mapping rules](#Schema.Block-mapping-rules-for-messagepack) 161 to the block's contents based on the `block` field, producing what we'll call 162 a _block value_ in the table below. 163 164 The `nesting` value then in turn defines how Terraform will collect all of the 165 individual block values together to produce a single property value representing 166 the nested block type. For all `nesting` values other than `MAP`, blocks may 167 not have any labels. For the `nesting` value `MAP`, blocks must have exactly 168 one label, which is a string we'll call a _block label_ in the table below. 169 170 | `nesting` Value | MessagePack Representation | 171 |---|---| 172 | `SINGLE` | The block value of the single block of this type, or nil if there is no block of that type. | 173 | `LIST` | A MessagePack array of all of the block values, preserving the order of definition of the blocks in the configuration. | 174 | `SET` | A MessagePack array of all of the block values in no particular order. | 175 | `MAP` | A MessagePack map with one key-value pair per block value, where the key is the block label and the value is the block value. | 176 | `GROUP` | The same as with `SINGLE`, except that if there is no block of that type Terraform will synthesize a block value by pretending that all of the declared attributes are null and that there are zero blocks of each declared block type. | 177 178 For the `LIST` and `SET` nesting modes, Terraform guarantees that the 179 MessagePack array will have a number of elements between the `min_items` and 180 `max_items` values given in the schema, _unless_ any of the block values contain 181 nested unknown values. When unknown values are present, Terraform considers 182 the value to be potentially incomplete and so Terraform defers validation of 183 the number of blocks. For example, if the configuration includes a `dynamic` 184 block whose `for_each` argument is unknown then the final number of blocks is 185 not predictable until the apply phase. 186 187 ## JSON Serialization Rules 188 189 The JSON serialization is a secondary representation for `DynamicValue`, with 190 MessagePack preferred due to its ability to represent unknown values via an 191 extension. 192 193 The JSON encoding described in this section is also used for the `json` field 194 of the `RawValue` message that forms part of an `UpgradeResourceState` request. 195 However, in that case the data is serialized per the schema of the provider 196 version that created it, which won't necessarily match the schema of the 197 _current_ version of that provider. 198 199 ### `Schema.Block` Mapping Rules for JSON 200 201 To represent the content of a block as JSON, Terraform constructs a 202 JSON object that contains one property per attribute and one property per 203 distinct nested block described in the `Schema.Block` message. 204 205 The properties representing attributes have property values based on 206 [the `Schema.Attribute` mapping rules](#Schema.Attribute-mapping-rules-for-json). 207 The properties representing nested block types have property values based on 208 [the `Schema.NestedBlock` mapping rules](#Schema.NestedBlock-mapping-rules-for-json). 209 210 ### `Schema.Attribute` Mapping Rules for JSON 211 212 The JSON serialization of an attribute value depends on the value of the `type` 213 field of the corresponding `Schema.Attribute` message. The `type` field is 214 a compact JSON serialization of a 215 [Terraform type constraint](https://www.terraform.io/docs/configuration/types.html), 216 which consists either of a single 217 string value (for primitive types) or a two-element array giving a type kind 218 and a type argument. 219 220 The following table describes the type-specific mapping rules. Along with those 221 type-specific rules there is one special rule that overrides the rules in the 222 table regardless of type: 223 224 * A null value is always represented as JSON `null`. 225 226 | `type` Pattern | JSON Representation | 227 |---|---| 228 | `"string"` | A JSON string containing the Unicode characters from the string value. | 229 | `"number"` | A JSON number representing the number value. Terraform numbers are arbitrary-precision floating point, so the value may have a larger mantissa than can be represented by a 64-bit float. | 230 | `"bool"` | Either JSON `true` or JSON `false`, depending on the boolean value. | 231 | `["list",T]` | A JSON array with the same number of elements as the list value, each of which is represented by the result of applying these same mapping rules to the nested type `T`. | 232 | `["set",T]` | Identical in representation to `["list",T]`, but the order of elements is undefined because Terraform sets are unordered. | 233 | `["map",T]` | A JSON object with one property per element of the map value, where the element key is serialized as the property name string and the element value is represented by a property value constructed by applying these same mapping rules to the nested type `T`. | 234 | `["object",ATTRS]` | A JSON object with one property per attribute defined in the `ATTRS` object. The attribute name is serialized as the property name string and the attribute value is represented by a property value constructed by applying these same mapping rules to each attribute's own type. | 235 | `["tuple",TYPES]` | A JSON array with one element per element described by the `TYPES` array. The element values are constructed by applying these same mapping rules to the corresponding element of `TYPES`. | 236 | `"dynamic"` | A JSON object with two properties: `"type"` specifying one of the `type` patterns described in this table in-band, giving the exact runtime type of the value, and `"value"` specifying the result of applying these same mapping rules to the table for the specified runtime type. This special type constraint represents values whose types will be decided only at runtime. | 237 238 ### `Schema.NestedBlock` Mapping Rules for JSON 239 240 The JSON serialization of a collection of blocks of a particular type depends 241 on the `nesting` field of the corresponding `Schema.NestedBlock` message. 242 The `nesting` field is a value from the `Schema.NestingBlock.NestingMode` 243 enumeration. 244 245 All `nesting` values cause the individual blocks of a type to be represented 246 by applying 247 [the `Schema.Block` mapping rules](#Schema.Block-mapping-rules-for-json) 248 to the block's contents based on the `block` field, producing what we'll call 249 a _block value_ in the table below. 250 251 The `nesting` value then in turn defines how Terraform will collect all of the 252 individual block values together to produce a single property value representing 253 the nested block type. For all `nesting` values other than `MAP`, blocks may 254 not have any labels. For the `nesting` value `MAP`, blocks must have exactly 255 one label, which is a string we'll call a _block label_ in the table below. 256 257 | `nesting` Value | JSON Representation | 258 |---|---| 259 | `SINGLE` | The block value of the single block of this type, or `null` if there is no block of that type. | 260 | `LIST` | A JSON array of all of the block values, preserving the order of definition of the blocks in the configuration. | 261 | `SET` | A JSON array of all of the block values in no particular order. | 262 | `MAP` | A JSON object with one property per block value, where the property name is the block label and the value is the block value. | 263 | `GROUP` | The same as with `SINGLE`, except that if there is no block of that type Terraform will synthesize a block value by pretending that all of the declared attributes are null and that there are zero blocks of each declared block type. | 264 265 For the `LIST` and `SET` nesting modes, Terraform guarantees that the JSON 266 array will have a number of elements between the `min_items` and `max_items` 267 values given in the schema.