Terraform
Object Type
Object types store a mapping of explicit attribute names to value types. Objects must declare all attribute values, even when null or unknown, unless the entire object is null or unknown.
By default, objects from schema (configuration, plan, and state) data are represented in the framework by types.ObjectType
and its associated value storage type of types.Object
. These types fully support Terraform's type system concepts that cannot be represented in Go built-in types, such as a struct. Framework types can be extended by provider code or shared libraries to provide specific use case functionality.
Schema Definitions
Tip
Use nested attribute types instead of object attribute types where possible. Object attributes have limited utility as they can only define type information.
Use one of the following attribute types to directly add a single structure of a nested attributes to a schema or nested attribute type:
If a wrapping collection is needed on the structure of nested attributes, any of the other nested attribute and nested block types can be used.
Use one of the following attribute types to directly add an object value directly to a schema or nested attribute type:
Schema Type | Attribute Type |
---|---|
Data Source | schema.ObjectAttribute |
Provider | schema.ObjectAttribute |
Resource | schema.ObjectAttribute |
Ephemeral Resource | schema.ObjectAttribute |
If the object value should be the element type of another collection attribute type, set the ElementType
field to types.ObjectType{AttrTypes: /* ... */}
or the appropriate custom type.
If the object value should be a value type of an object attribute type, set the AttributeTypes
map value to types.ObjectType{AttrTypes: /* ... */}
or the appropriate custom type.
Accessing Values
Tip
Review the associated attribute documentation to understand how schema-based data gets mapped into accessible values, such as a types.Object
in this case.
Access types.Object
information via the following methods:
(types.Object).IsNull() bool
: Returnstrue
if the object is null.(types.Object).IsUnknown() bool
: Returnstrue
if the object is unknown.(types.Object).Attributes() map[string]attr.Value
: Returns the knownmap[string]attr.Value
value, ornil
if null or unknown.(types.Object).As(context.Context, any, ObjectAsOptions) diag.Diagnostics
: Converts the known values into the given Go type, if possible. It is recommended to use a struct of framework types to account for attributes which may be unknown.
In this example, an object with a string attribute is checked for being null or unknown value first, before accessing its known value attributes as a Go struct type:
// Example data model definitions
// type ExampleModel struct {
// ExampleAttribute types.Object `tfsdk:"example_attribute"`
// }
//
// type ExampleAttributeModel struct {
// StringAttribute types.String `tfsdk:"string_attribute"`
// }
//
// This would be filled in, such as calling: req.Plan.Get(ctx, &data)
var data ExampleModel
// optional logic for handling null value
if data.ExampleAttribute.IsNull() {
// ...
}
// optional logic for handling unknown value
if data.ExampleAttribute.IsUnknown() {
// ...
}
var exampleAttribute ExampleAttributeModel
diags := data.ExampleAttribute.As(ctx, &exampleAttribute, basetypes.ObjectAsOptions{})
// Object data now is accessible, such as: exampleAttribute.StringAttribute.StringValue()
Setting Values
Call one of the following to create a types.Object
value:
types.ObjectNull(map[string]attr.Type) types.Object
: A null object value with the given element type.types.ObjectUnknown(map[string]attr.Type) types.Object
: An unknown object value with the given element type.types.ObjectValue(map[string]attr.Type, map[string]attr.Value) (types.Object, diag.Diagnostics)
: A known value with the given attribute type mapping and attribute values mapping.types.ObjectValueFrom(context.Context, map[string]attr.Type, any) (types.Object, diag.Diagnostics)
: A known value with the given attribute type mapping and values. This can convert the source data from standard Go types into framework types as noted in the documentation for each type, such as giving astruct
for atypes.Object
.types.ObjectValueMust(map[string]attr.Type, map[string]attr.Value) types.Object
: A known value with the given attribute type mapping and attribute value mapping. Any diagnostics are converted to a runtime panic. This is recommended only for testing or exhaustively tested logic.
In this example, a known object value is created from framework types:
elementTypes := map[string]attr.Type{
"attr1": types.StringType,
"attr2": types.Int64Type,
}
elements := map[string]attr.Value{
"attr1": types.StringValue("value"),
"attr2": types.Int64Value(123),
}
objectValue, diags := types.ObjectValue(elementTypes, elements)
Otherwise, for certain framework functionality that does not require types
implementations directly, such as:
(tfsdk.State).SetAttribute()
types.ListValueFrom()
types.MapValueFrom()
types.ObjectValueFrom()
types.SetValueFrom()
Automatic conversion with tfsdk
struct tags
Objects can be automatically converted to and from any Go struct type that follows these constraints to prevent accidental data loss:
- Every struct field must have a
tfsdk
struct tag and every attribute in the object must have a corresponding struct tag. Thetfsdk
struct tag must name an attribute in the object that it is being mapped or be set to-
to explicitly declare it does not map to an attribute in the object. Duplicatetfsdk
struct tags are not allowed. - Every struct type must be an acceptable conversion type according to the type documentation, such as
*string
being acceptable for a string type. However, it is recommended to use framework types to simplify data modeling (one model type for accessing and setting data) and prevent errors when encountering unknown values from Terraform.
In this example, a struct is directly used to set an object attribute value:
type ExampleAttributeModel struct {
Int64Attribute types.Int64 `tfsdk:"int64_attribute`
StringAttribute types.String `tfsdk:"string_attribute"`
}
value := ExampleAttributeModel{
Int64Attribute: types.Int64Value(123),
StringAttribute: types.StringValue("example"),
}
diags := resp.State.SetAttribute(ctx, path.Root("example_attribute"), value)
In this example, a types.Object
is created from a struct:
type ExampleAttributeModel struct {
Int64Attribute types.Int64 `tfsdk:"int64_attribute`
StringAttribute types.String `tfsdk:"string_attribute"`
}
func (m ExampleAttributeModel) AttributeTypes() map[string]attr.Type {
return map[string]attr.Type{
"int64_attribute": types.Int64Type,
"string_attribute": types.StringType,
}
}
value := ExampleAttributeModel{
Int64Attribute: types.Int64Value(123),
StringAttribute: types.StringValue("example"),
}
objectValue, diags := types.ObjectValueFrom(ctx, value.AttributeTypes(), value)
Struct Embedding
Go struct types that utilize struct embedding to promote fields with tfsdk
tags are supported when converting to and from object types.
In this example, a types.Object
is created from a struct that embeds another struct type:
type ExampleAttributeModel struct {
Attr1 types.String `tfsdk:"attr_1"`
Attr2 types.Bool `tfsdk:"attr_2"`
CommonModel // This embedded struct type promotes the Attr3 field
}
type CommonModel struct {
Attr3 types.Int64 `tfsdk:"attr_3"`
}
func (m ExampleAttributeModel) AttributeTypes() map[string]attr.Type {
return map[string]attr.Type{
"attr_1": types.StringType,
"attr_2": types.BoolType,
"attr_3": types.Int64Type,
}
}
value := ExampleAttributeModel{
Attr1: types.StringValue("example"),
Attr2: types.BoolValue(true),
}
// This field is promoted from CommonModel
value.Attr3 = types.Int64Value(123)
objectValue, diags := types.ObjectValueFrom(ctx, value.AttributeTypes(), value)
Restrictions
In addition to the constraints listed above for object conversions using tfsdk
tagged fields, embedded struct types have these additional restrictions:
- Promoted fields cannot have duplicate
tfsdk
struct tags that conflict with any fields of structs they are embedded within.
type thingResourceModel struct {
Attr1 types.String `tfsdk:"attr_1"`
Attr2 types.Bool `tfsdk:"attr_2"`
CommonModel
}
type CommonModel struct {
// This field has a duplicate tfsdk tag, conflicting with (thingResourceModel).Attr1
// and will raise an error diagnostic during conversion.
ConflictingAttr types.Int64 `tfsdk:"attr_1"`
}
- Struct types embedded by pointers are not supported.
type thingResourceModel struct {
Attr1 types.String `tfsdk:"attr_1"`
Attr2 types.Bool `tfsdk:"attr_2"`
// This type of struct embedding is not supported and will raise
// an error diagnostic during conversion.
//
// Remove the pointer to embed the struct by value.
*CommonModel
}
type CommonModel struct {
Attr3 types.Int64 `tfsdk:"attr_3"`
Attr4 types.List `tfsdk:"attr_4"`
}
Extending
The framework supports extending its base type implementations with custom types. These can adjust expected provider code usage depending on their implementation.