Terraform
Framework Code Generator
Note
Code Generation is currently in tech preview.The Framework Code Generator is a CLI tool that generates Terraform Plugin Framework code from a provider code specification. This section contains usage documentation and examples for using the generator.
Overview
The Framework Code Generator has two commands: generate
and scaffold
.
The primary command is generate
, which receives a Provider Code Specification as input and creates Terraform Plugin Framework Go code as output. These generated Go files will have a suffix of _gen.go
and are marked with a "DO NOT EDIT" comment at the top of each file.
The scaffold
command creates Terraform Plugin Framework Go code from pre-defined templates. These Go files are not marked as generated code and are intended to be edited by the provider developer after running the command.
Usage
Installation
The CLI tool can be installed with the Go toolchain, via go install
:
go install github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework@latest
Generate command
Note
Currently, the Framework Code Generator creates Plugin Framework schemas and helper functions for nested attributes.The generate
command uses a specification as input and generates Terraform Provider code as output.
For example:
tfplugingen-framework generate all \
--input specification.json \
--output internal/provider
More detailed examples of input/output can be found in the Examples.
Subcommands
The specification defines the schema for the provider, data sources, and resources.
A subcommand is required to inform the generate
command whether to generate code for all data sources, resources and the provider, or whether to specifically generate code for only data sources, or resources or the provider.
The subcommands that are available are all
, data-sources
, provider
and resources
.
Flags
The available flags for the generate <all|data-sources|provider|resources>
command:
Flag | Required? | Default | Description |
---|---|---|---|
input | No | Uses stdin as input | String path of a file containing the specification |
output | No | ./output | String path of the destination directory for the generated code |
package | No | A package will be generated per data source, provider, and resource | String that represents a single Go package name to be used for all generated code |
If the input
flag is not supplied then the generate
command requires that the specification is provided through stdin
, for example by piping into the generate
command (e.g., cat provider_code_spec.json | tfplugingen-framework generate all ...
).
Scaffold command
The scaffold
command generates starter code for a data source, provider, or resource.
For example:
tfplugingen-framework scaffold data-source \
--name example \
--force \
--output-dir internal/provider
Subcommands
A subcommand is required to inform the scaffold
command whether to generate starter code for a data source, provider, or resource.
The subcommands that are available are data-source
, provider
and resource
.
Flags
The available flags for the scaffold <data-source|provider|resource>
command:
Flag | Required? | Default | Description |
---|---|---|---|
name | Yes | String name of data source, provider, or resource in snake_case; without the provider type prefix. | |
force | No | If present, forces recreation overwriting preexisting file. | |
output-dir | No | Current working directory | String path to destination directory for the scaffolded code file. |
output-file | No | Uses name flag with suffix _<data_source/provider/resource>.go | String file name and extension for the scaffolded code file. |
package | No | provider | String representing the Go package name to be used for the scaffolded code file. |
Example
tfplugingen-framework scaffold data-source \
--name example \
--force \
--output-dir ./output/scaffold/data_source
Executing this command generates the following scaffold code in ./output/scaffold/data_source/example_data_source.go
:
package provider
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)
var _ datasource.DataSource = (*exampleDataSource)(nil)
func NewExampleDataSource() datasource.DataSource {
return &exampleDataSource{}
}
type exampleDataSource struct{}
type exampleDataSourceModel struct {
Id types.String `tfsdk:"id"`
}
func (d *exampleDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_example"
}
func (d *exampleDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
},
}
}
func (d *exampleDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data exampleDataSourceModel
// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Read API call logic
// Example data value setting
data.Id = types.StringValue("example-id")
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Examples
The following sections detail how Provider Code Specification map to Plugin Framework code for the generate
command.
Primitive Attributes
The primitive attributes BoolAttribute
, Float64Attribute
, Int64Attribute
, NumberAttribute
and StringAttribute
are defined using the bool
, float64
, int64
, number
and string
types, respectively in the specification.
For example:
{
"datasources": [
{
"name": "example",
"schema": {
"attributes": [
{
"name": "bool_attribute",
"bool": {
"computed_optional_required": "computed"
}
}
]
}
}
],
"provider": {
"name": "provider"
}
}
Using this specification with the following generate command:
tfplugingen-framework generate all --input <path_to_specification>
Creates the following Go code in output/datasource_example/example_data_source_gen.go
:
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package datasource_example
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func ExampleDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"bool_attribute": schema.BoolAttribute{
Computed: true,
},
},
}
}
type ExampleModel struct {
BoolAttribute types.Bool `tfsdk:"bool_attribute"`
}
Collection Attributes
The collection attributes ListAttribute
, MapAttribute
, and SetAttribute
are defined using the list
, map
, and set
types, respectively in the specification.
For example:
{
"datasources": [
{
"name": "example",
"schema": {
"attributes": [
{
"name": "list_attribute",
"list": {
"computed_optional_required": "computed",
"element_type": {
"string": {}
}
}
}
]
}
}
],
"provider": {
"name": "provider"
}
}
Using this specification with the following generate command:
tfplugingen-framework generate all --input <path_to_specification>
Creates the following Go code in output/datasource_example/example_data_source_gen.go
:
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package datasource_example
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func ExampleDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"list_attribute": schema.ListAttribute{
ElementType: types.StringType,
Computed: true,
},
},
}
}
type ExampleModel struct {
ListAttribute types.List `tfsdk:"list_attribute"`
}
Object Attributes
The ObjectAttribute
is defined using the object
type in the specification.
For example:
{
"datasources": [
{
"name": "example",
"schema": {
"attributes": [
{
"name": "object_attribute",
"object": {
"attribute_types": [
{
"name": "string_attr",
"string": {}
}
],
"computed_optional_required": "required"
}
}
]
}
}
],
"provider": {
"name": "provider"
}
}
Using this specification with the following generate command:
tfplugingen-framework generate all --input <path_to_specification>
Creates the following Go code in output/datasource_example/example_data_source_gen.go
:
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package datasource_example
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func ExampleDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"object_attribute": schema.ObjectAttribute{
AttributeTypes: map[string]attr.Type{
"string_attr": types.StringType,
},
Required: true,
},
},
}
}
type ExampleModel struct {
ObjectAttribute types.Object `tfsdk:"object_attribute"`
}
Nested Attributes
The nested attributes ListNestedAttribute
, MapNestedAttribute
, SetNestedAttribute
, and SingleNestedAttribute
are defined using the list_nested
, map_nested
, set_nested
, and single_nested
types, respectively in the specification.
For example:
{
"datasources": [
{
"name": "example",
"schema": {
"attributes": [
{
"name": "list_nested_attribute",
"list_nested": {
"computed_optional_required": "optional",
"nested_object": {
"attributes": [
{
"name": "bool_attribute",
"bool": {
"computed_optional_required": "optional"
}
}
]
}
}
}
]
}
}
],
"provider": {
"name": "provider"
}
}
Using this specification with the following generate command:
tfplugingen-framework generate all --input <path_to_specification>
Creates the following schema and model Go code in output/datasource_example/example_data_source_gen.go
:
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package datasource_example
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"strings"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func ExampleDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"list_nested_attribute": schema.ListNestedAttribute{
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"bool_attribute": schema.BoolAttribute{
Optional: true,
},
},
CustomType: ListNestedAttributeType{
ObjectType: types.ObjectType{
AttrTypes: ListNestedAttributeValue{}.AttributeTypes(ctx),
},
},
},
Optional: true,
},
},
}
}
type ExampleModel struct {
ListNestedAttribute types.List `tfsdk:"list_nested_attribute"`
}
For nested attributes, the ObjectTypable type that is used as the CustomType within the schema is also generated along with the corresponding ObjectValuable type.
For List, Map, and Set nested attributes, the ObjectTypable type is assigned to the CustomType field of the NestedObject field.
For Single nested attributes, the ObjectTypable type is assigned directly to the CustomType field.
Developing Custom Types provides details concerning the methods that need to be overridden for custom types, but the generated code provides a full implementation of all methods.
The generator outputs the following for ListNestedAttributeType
type:
var _ basetypes.ObjectTypable = ListNestedAttributeType{}
type ListNestedAttributeType struct {
basetypes.ObjectType
}
func (t ListNestedAttributeType) Equal(o attr.Type) bool {
other, ok := o.(ListNestedAttributeType)
if !ok {
return false
}
return t.ObjectType.Equal(other.ObjectType)
}
func (t ListNestedAttributeType) String() string {
return "ListNestedAttributeType"
}
func (t ListNestedAttributeType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
var diags diag.Diagnostics
state := attr.ValueStateKnown
attributes := in.Attributes()
boolAttribute, ok := attributes["bool_attribute"]
if !ok {
diags.AddError(
"Attribute Missing",
`bool_attribute is missing from object`)
return nil, diags
}
boolAttributeVal, ok := boolAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`bool_attribute expected to be basetypes.BoolValue, was: %T`, boolAttribute))
}
if boolAttributeVal.IsUnknown() {
state = attr.ValueStateUnknown
}
return ListNestedAttributeValue{
BoolAttribute: boolAttributeVal,
state: state,
}, diags
}
func NewListNestedAttributeValueNull() ListNestedAttributeValue {
return ListNestedAttributeValue{
state: attr.ValueStateNull,
}
}
func NewListNestedAttributeValueUnknown() ListNestedAttributeValue {
return ListNestedAttributeValue{
state: attr.ValueStateUnknown,
}
}
func NewListNestedAttributeValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (ListNestedAttributeValue, diag.Diagnostics) {
var diags diag.Diagnostics
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
ctx := context.Background()
for name, attributeType := range attributeTypes {
attribute, ok := attributes[name]
if !ok {
diags.AddError(
"Missing ListNestedAttributeValue Attribute Value",
"While creating a ListNestedAttributeValue value, a missing attribute value was detected. "+
"A ListNestedAttributeValue must contain values for all attributes, even if null or unknown. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("ListNestedAttributeValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
)
continue
}
if !attributeType.Equal(attribute.Type(ctx)) {
diags.AddError(
"Invalid ListNestedAttributeValue Attribute Type",
"While creating a ListNestedAttributeValue value, an invalid attribute value was detected. "+
"A ListNestedAttributeValue must use a matching attribute type for the value. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("ListNestedAttributeValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
fmt.Sprintf("ListNestedAttributeValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
)
}
}
for name := range attributes {
_, ok := attributeTypes[name]
if !ok {
diags.AddError(
"Extra ListNestedAttributeValue Attribute Value",
"While creating a ListNestedAttributeValue value, an extra attribute value was detected. "+
"A ListNestedAttributeValue must not contain values beyond the expected attribute types. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Extra ListNestedAttributeValue Attribute Name: %s", name),
)
}
}
if diags.HasError() {
return NewListNestedAttributeValueUnknown(), diags
}
state := attr.ValueStateKnown
boolAttribute, ok := attributes["bool_attribute"]
if !ok {
diags.AddError(
"Attribute Missing",
`bool_attribute is missing from object`)
return NewListNestedAttributeValueNull(), diags
}
boolAttributeVal, ok := boolAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`bool_attribute expected to be basetypes.BoolValue, was: %T`, boolAttribute))
}
if boolAttributeVal.IsUnknown() {
state = attr.ValueStateUnknown
}
return ListNestedAttributeValue{
BoolAttribute: boolAttributeVal,
state: state,
}, diags
}
func NewListNestedAttributeValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) ListNestedAttributeValue {
object, diags := NewListNestedAttributeValue(attributeTypes, attributes)
if diags.HasError() {
// This could potentially be added to the diag package.
diagsStrings := make([]string, 0, len(diags))
for _, diagnostic := range diags {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"%s | %s | %s",
diagnostic.Severity(),
diagnostic.Summary(),
diagnostic.Detail()))
}
panic("NewListNestedAttributeValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
}
return object
}
func (t ListNestedAttributeType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
if in.Type() == nil {
return NewListNestedAttributeValueNull(), nil
}
if !in.Type().Equal(t.TerraformType(ctx)) {
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
}
if !in.IsKnown() {
return NewListNestedAttributeValueUnknown(), nil
}
if in.IsNull() {
return NewListNestedAttributeValueNull(), nil
}
attributes := map[string]attr.Value{}
val := map[string]tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
for k, v := range val {
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
if err != nil {
return nil, err
}
attributes[k] = a
}
return NewListNestedAttributeValueMust(t.AttrTypes, attributes), nil
}
func (t ListNestedAttributeType) ValueType(ctx context.Context) attr.Value {
return ListNestedAttributeValue{}
}
The generator outputs the following for ListNestedAttributeValue
type:
var _ basetypes.ObjectValuable = ListNestedAttributeValue{}
type ListNestedAttributeValue struct {
BoolAttribute basetypes.BoolValue `tfsdk:"bool_attribute"`
state attr.ValueState
}
func (v ListNestedAttributeValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := make(map[string]tftypes.Type, 1)
var val tftypes.Value
var err error
attrTypes["bool_attribute"] = basetypes.BoolType{}.TerraformType(ctx)
objectType := tftypes.Object{AttributeTypes: attrTypes}
switch v.state {
case attr.ValueStateKnown:
vals := make(map[string]tftypes.Value, 1)
val, err = v.BoolAttribute.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["bool_attribute"] = val
if err := tftypes.ValidateValue(objectType, vals); err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
return tftypes.NewValue(objectType, vals), nil
case attr.ValueStateNull:
return tftypes.NewValue(objectType, nil), nil
case attr.ValueStateUnknown:
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
default:
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
}
}
func (v ListNestedAttributeValue) IsNull() bool {
return v.state == attr.ValueStateNull
}
func (v ListNestedAttributeValue) IsUnknown() bool {
return v.state == attr.ValueStateUnknown
}
func (v ListNestedAttributeValue) String() string {
return "ListNestedAttributeValue"
}
func (v ListNestedAttributeValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
objVal, diags := types.ObjectValue(
map[string]attr.Type{
"bool_attribute": basetypes.BoolType{},
},
map[string]attr.Value{
"bool_attribute": v.BoolAttribute,
})
return objVal, diags
}
func (v ListNestedAttributeValue) Equal(o attr.Value) bool {
other, ok := o.(ListNestedAttributeValue)
if !ok {
return false
}
if v.state != other.state {
return false
}
if v.state != attr.ValueStateKnown {
return true
}
if !v.BoolAttribute.Equal(other.BoolAttribute) {
return false
}
return true
}
func (v ListNestedAttributeValue) Type(ctx context.Context) attr.Type {
return ListNestedAttributeType{
basetypes.ObjectType{
AttrTypes: v.AttributeTypes(ctx),
},
}
}
func (v ListNestedAttributeValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
return map[string]attr.Type{
"bool_attribute": basetypes.BoolType{},
}
}
Nested Blocks
The nested blocks ListNestedBlock
, SetNestedBlock
, and SingleNestedBlock
are defined using the list_nested
, set_nested
, and single_nested
types, respectively in the specification.
For example:
{
"datasources": [
{
"name": "example",
"schema": {
"attributes": [],
"blocks": [
{
"name": "single_nested_block",
"single_nested": {
"attributes": [
{
"name": "bool_attribute",
"bool": {
"computed_optional_required": "computed"
}
}
]
}
}
]
}
}
],
"provider": {
"name": "provider"
}
}
Using this specification with the following generate command:
tfplugingen-framework generate all --input <path_to_specification>
Creates the following schema and model Go code in output/datasource_example/example_data_source_gen.go
:
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package datasource_example
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"strings"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func ExampleDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Blocks: map[string]schema.Block{
"single_nested_block": schema.SingleNestedBlock{
Attributes: map[string]schema.Attribute{
"bool_attribute": schema.BoolAttribute{
Computed: true,
},
},
CustomType: SingleNestedBlockType{
ObjectType: types.ObjectType{
AttrTypes: SingleNestedBlockValue{}.AttributeTypes(ctx),
},
},
},
},
}
}
type ExampleModel struct {
SingleNestedBlock types.Object `tfsdk:"single_nested_block"`
}
For nested blocks, the ObjectTypable type that is used as the CustomType within the schema is also generated along with the corresponding ObjectValuable type.
For List, and Set nested blockss, the ObjectTypable type is assigned to the CustomType field of the NestedObject field.
For Single nested blocks, the ObjectTypable type is assigned directly to the CustomType field.
Developing Custom Types provides details concerning the methods that need to be overridden for custom types, but the generated code provides a full implementation of all methods.
The generator outputs the following for SingleNestedBlockType
type:
var _ basetypes.ObjectTypable = SingleNestedBlockType{}
type SingleNestedBlockType struct {
basetypes.ObjectType
}
func (t SingleNestedBlockType) Equal(o attr.Type) bool {
other, ok := o.(SingleNestedBlockType)
if !ok {
return false
}
return t.ObjectType.Equal(other.ObjectType)
}
func (t SingleNestedBlockType) String() string {
return "SingleNestedBlockType"
}
func (t SingleNestedBlockType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
var diags diag.Diagnostics
state := attr.ValueStateKnown
attributes := in.Attributes()
boolAttribute, ok := attributes["bool_attribute"]
if !ok {
diags.AddError(
"Attribute Missing",
`bool_attribute is missing from object`)
return nil, diags
}
boolAttributeVal, ok := boolAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`bool_attribute expected to be basetypes.BoolValue, was: %T`, boolAttribute))
}
if boolAttributeVal.IsUnknown() {
state = attr.ValueStateUnknown
}
return SingleNestedBlockValue{
BoolAttribute: boolAttributeVal,
state: state,
}, diags
}
func NewSingleNestedBlockValueNull() SingleNestedBlockValue {
return SingleNestedBlockValue{
state: attr.ValueStateNull,
}
}
func NewSingleNestedBlockValueUnknown() SingleNestedBlockValue {
return SingleNestedBlockValue{
state: attr.ValueStateUnknown,
}
}
func NewSingleNestedBlockValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (SingleNestedBlockValue, diag.Diagnostics) {
var diags diag.Diagnostics
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
ctx := context.Background()
for name, attributeType := range attributeTypes {
attribute, ok := attributes[name]
if !ok {
diags.AddError(
"Missing SingleNestedBlockValue Attribute Value",
"While creating a SingleNestedBlockValue value, a missing attribute value was detected. "+
"A SingleNestedBlockValue must contain values for all attributes, even if null or unknown. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("SingleNestedBlockValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
)
continue
}
if !attributeType.Equal(attribute.Type(ctx)) {
diags.AddError(
"Invalid SingleNestedBlockValue Attribute Type",
"While creating a SingleNestedBlockValue value, an invalid attribute value was detected. "+
"A SingleNestedBlockValue must use a matching attribute type for the value. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("SingleNestedBlockValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
fmt.Sprintf("SingleNestedBlockValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
)
}
}
for name := range attributes {
_, ok := attributeTypes[name]
if !ok {
diags.AddError(
"Extra SingleNestedBlockValue Attribute Value",
"While creating a SingleNestedBlockValue value, an extra attribute value was detected. "+
"A SingleNestedBlockValue must not contain values beyond the expected attribute types. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Extra SingleNestedBlockValue Attribute Name: %s", name),
)
}
}
if diags.HasError() {
return NewSingleNestedBlockValueUnknown(), diags
}
state := attr.ValueStateKnown
boolAttribute, ok := attributes["bool_attribute"]
if !ok {
diags.AddError(
"Attribute Missing",
`bool_attribute is missing from object`)
return NewSingleNestedBlockValueNull(), diags
}
boolAttributeVal, ok := boolAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`bool_attribute expected to be basetypes.BoolValue, was: %T`, boolAttribute))
}
if boolAttributeVal.IsUnknown() {
state = attr.ValueStateUnknown
}
return SingleNestedBlockValue{
BoolAttribute: boolAttributeVal,
state: state,
}, diags
}
func NewSingleNestedBlockValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) SingleNestedBlockValue {
object, diags := NewSingleNestedBlockValue(attributeTypes, attributes)
if diags.HasError() {
// This could potentially be added to the diag package.
diagsStrings := make([]string, 0, len(diags))
for _, diagnostic := range diags {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"%s | %s | %s",
diagnostic.Severity(),
diagnostic.Summary(),
diagnostic.Detail()))
}
panic("NewSingleNestedBlockValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
}
return object
}
func (t SingleNestedBlockType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
if in.Type() == nil {
return NewSingleNestedBlockValueNull(), nil
}
if !in.Type().Equal(t.TerraformType(ctx)) {
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
}
if !in.IsKnown() {
return NewSingleNestedBlockValueUnknown(), nil
}
if in.IsNull() {
return NewSingleNestedBlockValueNull(), nil
}
attributes := map[string]attr.Value{}
val := map[string]tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
for k, v := range val {
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
if err != nil {
return nil, err
}
attributes[k] = a
}
return NewSingleNestedBlockValueMust(t.AttrTypes, attributes), nil
}
func (t SingleNestedBlockType) ValueType(ctx context.Context) attr.Value {
return SingleNestedBlockValue{}
}
The generator outputs the following for SingleNestedBlockValue
type:
var _ basetypes.ObjectValuable = SingleNestedBlockValue{}
type SingleNestedBlockValue struct {
BoolAttribute basetypes.BoolValue `tfsdk:"bool_attribute"`
state attr.ValueState
}
func (v SingleNestedBlockValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := make(map[string]tftypes.Type, 1)
var val tftypes.Value
var err error
attrTypes["bool_attribute"] = basetypes.BoolType{}.TerraformType(ctx)
objectType := tftypes.Object{AttributeTypes: attrTypes}
switch v.state {
case attr.ValueStateKnown:
vals := make(map[string]tftypes.Value, 1)
val, err = v.BoolAttribute.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["bool_attribute"] = val
if err := tftypes.ValidateValue(objectType, vals); err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
return tftypes.NewValue(objectType, vals), nil
case attr.ValueStateNull:
return tftypes.NewValue(objectType, nil), nil
case attr.ValueStateUnknown:
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
default:
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
}
}
func (v SingleNestedBlockValue) IsNull() bool {
return v.state == attr.ValueStateNull
}
func (v SingleNestedBlockValue) IsUnknown() bool {
return v.state == attr.ValueStateUnknown
}
func (v SingleNestedBlockValue) String() string {
return "SingleNestedBlockValue"
}
func (v SingleNestedBlockValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
objVal, diags := types.ObjectValue(
map[string]attr.Type{
"bool_attribute": basetypes.BoolType{},
},
map[string]attr.Value{
"bool_attribute": v.BoolAttribute,
})
return objVal, diags
}
func (v SingleNestedBlockValue) Equal(o attr.Value) bool {
other, ok := o.(SingleNestedBlockValue)
if !ok {
return false
}
if v.state != other.state {
return false
}
if v.state != attr.ValueStateKnown {
return true
}
if !v.BoolAttribute.Equal(other.BoolAttribute) {
return false
}
return true
}
func (v SingleNestedBlockValue) Type(ctx context.Context) attr.Type {
return SingleNestedBlockType{
basetypes.ObjectType{
AttrTypes: v.AttributeTypes(ctx),
},
}
}
func (v SingleNestedBlockValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
return map[string]attr.Type{
"bool_attribute": basetypes.BoolType{},
}
}