» Attribute Types

Attributes are the fields in a resource, data source, or provider. They hold the values that end up in state. Every attribute has an attribute type, which describes the constraints on the data the attribute can hold. When you access an attribute from the configuration, state, or plan, you are accessing attribute values, which are the actual data that was found in the configuration, state, or plan.

You can either use the built-in attribute type and value implementations or implement your own.

» Null and Unknown Values

There are two values every attribute in Terraform can hold, regardless of their type: null and unknown.

» Null

Null represents the absence of a Terraform value. It is usually encountered with optional attributes that the practitioner neglected to specify a value for, but can show up on any non-required attribute. Required attributes can never be null.

» Unknown

Unknown represents a Terraform value that is not yet known. Terraform uses a graph of providers, resources, and data sources to do things in the right order, and when a provider, resource, or data source relies on a value from another provider, resource, or data source that has not been resolved yet, it represents that state by using the unknown value. For example:

resource "example_foo" "bar" {
  hello = "world"
  demo = true
}

resource "example_baz" "quux" {
  foo_id = example_foo.bar.id
}

In the example above, example_baz.quux is relying on the id attribute of example_foo.bar. The id attribute of example_foo.bar isn't known until after the apply. The plan would list it as (known after apply). During the plan phase, example_baz.quux would get an unknown value as the value for foo_id.

Because they can result from interpolations in the practitioner's config, you have no control over what attributes may contain an unknown value. However, by the time a resource is expected to be created, read, updated, or deleted, only its computed attributes can be unknown. The rest are guaranteed to have known values (or be null).

Provider configuration values can be unknown, and providers should handle that situation, even if that means just returning an error.

» Built-In Types and Values

A collection of attribute type and attribute value implementations is available in the types package.

» StringType and String

Strings are a UTF-8 encoded collection of bytes.

hello = "world"

They are used by specifying the types.StringType constant in your tfsdk.Attribute's Type property, and are represented by a types.String struct in config, state, and plan. The types.String struct has the following properties:

  • Value contains the string's value as a Go string type.
  • Null is set to true when the string's value is null.
  • Unknown is set to true when the string's value is unknown.

» NumberType and Number

Numbers are numeric values, both whole values like 12 or fractional values like 3.14.

hello = 123

They are used by specifying the types.NumberType constant in your tfsdk.Attribute's Type property, and are represented by a types.Number struct in config, state, and plan. The types.Number struct has the following properties:

  • Value contains the number's value as a Go *big.Float type.
  • Null is set to true when the number's value is null.
  • Unknown is set to true when the number's value is unknown.

» BoolType and Bool

Bools are boolean values that can either be true or false.

hello = true

They are used by specifying the types.BoolType constant in your tfsdk.Attribute's Type property, and are represented by a types.Bool struct in config, state, and plan. The types.Bool struct has the following properties:

  • Value contains the boolean's value as a Go bool type.
  • Null is set to true when the boolean's value is null.
  • Unknown is set to true when the boolean's value is unknown.

» ListType and List

Lists are ordered collections of other types. Their elements, the values inside the list, must all be of the same type.

hello = ["red", "blue", "green"]

They are used by specifying a types.ListType value in your tfsdk.Attribute's Type property. You must specify an ElemType property for your list, indicating what type the elements should be. Lists are represented by a types.List struct in config, state, and plan. The types.List struct has the following properties:

  • ElemType will always contain the same type as the ElemType property of the types.ListType that created the types.List.
  • Elem contains a list of values, one for each element in the list. The values will all be of the value type produced by the ElemType for the list.
  • Null is set to true when the entire list's value is null. Individual elements may still be null even if the list's Null property is false.
  • Unknown is set to true when the entire list's value is unknown. Individual elements may still be unknown even if the list's Unknown property is false.

Elements of a types.List with a non-null, non-unknown value can be accessed without using type assertions by using the types.List's ElementsAs method, which uses the same conversion rules as the Get methods described in Access State, Config, and Plan.

» MapType and Map

Maps are unordered collections of other types with unique string indexes. Their elements, the values inside the map, must all be of the same type. The keys used to index the elements must be strings, but there are (theoretically) no limitations on what keys are acceptable or how many there can be.

hello = {
  pi = 3.14
  random = 4
  "meaning of life" = 42
}

They are used by specifying a types.MapType value in your tfsdk.Attribute's Type property. You must specify an ElemType property for your map, indicating what type the elements should be. Maps are represented by a types.Map struct in config, state, and plan. The types.Map struct has the following properties:

  • ElemType will always contain the same type as the ElemType property of the types.MapType that created the types.Map.
  • Elem contains a map of values, one for each element in the map. The keys will be the keys defined in the config, state, or plan, and the values will all be of the value type produced by the ElemType for the map.
  • Null is set to true when the entire map's value is null. Individual elements may still be null even if the map's Null property is false.
  • Unknown is set to true when the entire map's value is unknown. Individual elements may still be unknown even if the map's Unknown property is false.

Elements of a types.Map with a non-null, non-unknown value can be accessed without using type assertions by using the types.Map's ElementsAs method, which uses the same conversion rules as the Get methods described in Access State, Config, and Plan.

» ObjectType and Object

Objects are unordered collections of other types with unique, pre-specified attributes. The attributes have names represented by strings, and each attribute can specify its own type, similar to a Go struct type. The attributes and their types are considered part of the object's type; two objects are not the same type unless they have the same attributes, and those attributes have the same types.

hello = {
  pi = 3.14
  demo = true
  color = "red"
}

They are used by specifying a types.ObjectType value in your tfsdk.Attribute's Type property. You must specify an AttrTypes property for your object, indicating a map of the attribute names and the types of those attributes. Objects are represented by a types.Object struct in config, state, and plan. The types.Object struct has the following properties:

  • AttrTypes will always contain the same attribute names and associated types as the AttrTypes property of the types.ObjectType that created the types.Object.
  • Attrs contains a map of attribute names to values. Each attribute is guaranteed to always be present in the map. The values will always be of the value type for that attribute in the AttrTypes of the object.
  • Null is set to true when the entire object's value is null. Individual attributes may still be null even if the object's Null property is false.
  • Unknown is set to true when the entire object's value is unknown. Individual attributes may still be unknown even if the object's Unknown property is false.

A non-null, non-unknown types.Object value can be converted to a Go struct without using type assertions by using the types.Object's As method, which uses the same conversion rules as the Get methods described in Access State, Config, and Plan.

» Create Provider-Defined Types and Values

You may want to build your own attribute value and type implementations to allow your provider to combine validation, description, and plan customization behaviors into a reusable bundle. This helps avoid duplication or reimplementation and ensures consistency.

» attr.Type Interface

Use the attr.Type interface to implement an attribute type. It tells Terraform about its constraints and tells the framework how to create new attribute values from the information Terraform supplies. attr.Type has the following methods.

Method Description
TerraformType Returns the tftypes.Type value that describes its type constraints. This is how Terraform will know what type of values it can accept.
ValueFromTerraform Returns an attribute value from the tftypes.Value that Terraform supplies, or to return an error if it cannot. This error should not be used for validation purposes, and is expected to indicate programmer error, not practitioner error.
Equal Returns true if the attribute type is considered equal to the passed attribute type.

» AttributePathStepper Interface

All attribute types must implement the tftypes.AttributePathStepper interface, so the framework can access element or attribute types using attribute paths.

» Type-Specific Interfaces

Case Interface Description
Elements of the same type TypeWithElementType Attribute types that contain elements of the same type, like maps and lists, are required to implement attr.TypeWithElementType, which adds WithElementType and ElementType methods to the attr.Type interface. WithElementType must return a copy of the attribute type, but with its element type set to the passed type. ElementType must return the attribute type's element type.
Elements of different types TypeWithElementTypes Attribute types that contain elements of differing types, like tuples, are required to implement the attr.TypeWithElementTypes, which adds WithElementTypes and ElementTypes methods to the attr.Type interface. WithElementTypes must return a copy of the attribute type, but with its element types set to the passed element types. ElementTypes must return the attribute type's element types.
Contain attributes TypeWithAttributeTypes Attribute types that contain attributes, like objects, are required to implement the attr.TypeWithAttributeTypes interface, which adds WithAttributeTypes and AttributeTypes methods to the attr.Type interface. WithAttributeTypes must return a copy of the attribute type, but with its attribute types set to the passed attribute types. AttributeTypes must return the attribute type's attribute types.

» attr.Value Interface

Use the attr.Value interface to implement an attribute value. It tells the framework how to express that attribute value in a way that Terraform will understand. attr.Value has the following methods.

Method Description
ToTerraformValue Returns a Go type that is valid input for tftypes.NewValue for the tftypes.Type specified by the attr.Type that creates the attr.Value.
Equal Returns true if the passed attribute value should be considered to the attribute value the method is being called on. The passed attribute value is not guaranteed to be of the same Go type.