Custom Terraform Provider: Error in Value Conversion for Nested List Attributes

Hi Team,

I’m working on developing a custom Terraform provider for our internal infrastructure, which utilizes a REST API for management. The API’s CREATE endpoint expects the following input:

resource "test_environment" "edu1" {
  name     = "test"
  region   = "us-central"
  password = "dummy"
}

The REST API response is:

{
  "name": "test",
  "region": "us-central",
  "state": "RUNNING",
  "ip": "127.0.0.1",
  "dnsName": "test.env.xx..com",
  "owner": "ss@xx.com",
  "type": "xx",
  "services": [
    {
      "name": "test",      
      "credentials": [
        {
          "name": "username",
          "value": "xx"
        },
        {
          "name": "password",
          "value": "xx"
        }
      ]
    }
  ]
}

Here is the schema I’ve defined:

func (r *environmentResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			"name": schema.StringAttribute{
				Required:    true,
				Description: "The name of the environment.",
			},
			"last_updated": schema.StringAttribute{
				Computed:    true,
				Description: "The last time the environment was updated.",
			},
			"operation": schema.StringAttribute{
				Computed:    true,
				Description: "The last operation performed on the environment.",
			},
			"region": schema.StringAttribute{
				Required:    true,
				Description: "The region of the environment.",
			},
			"password": schema.StringAttribute{
				Required:    true,
				Sensitive:   true,
				Description: "The password for the environment.",
			},
			"state": schema.StringAttribute{
				Computed:    true,
				Description: "The current state of the environment.",
			},
			"ip": schema.StringAttribute{
				Computed:    true,
				Description: "The IP address of the environment.",
			},
			"dnsname": schema.StringAttribute{
				Computed:    true,
				Description: "The DNS name of the environment.",
			},
			"owner": schema.StringAttribute{
				Computed:    true,
				Description: "The owner of the environment.",
			},
			"type": schema.StringAttribute{
				Computed:    true,
				Description: "The type of the environment.",
			},
			"services": schema.ListNestedAttribute{
				Computed: true,
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"name": schema.StringAttribute{
							Computed:    true,
							Description: "The name of the service.",
						},
						"url": schema.StringAttribute{
							Computed:    true,
							Description: "The URL of the service.",
						},
						"credentials": schema.ListNestedAttribute{
							Computed: true,
							NestedObject: schema.NestedAttributeObject{
								Attributes: map[string]schema.Attribute{
									"name": schema.StringAttribute{
										Computed:    true,
										Description: "The name of the credential.",
									},
									"value": schema.StringAttribute{
										Computed:    true,
										Description: "The value of the credential.",
									},
								},
							},
						},
					},
				},
			},
		},
	}
}

And the models are defined as:

type environmentCredentialModel struct {
	Name  types.String `tfsdk:"name"`
	Value types.String `tfsdk:"value"`
}

type environmentServiceModel struct {
	Name        types.String                 `tfsdk:"name"`
	URL         types.String                 `tfsdk:"url"`
	Credentials []environmentCredentialModel `tfsdk:"credentials"`
}

type environmentResourceModel struct {
	Name        types.String              `tfsdk:"name"`
	Region      types.String              `tfsdk:"region"`
	State       types.String              `tfsdk:"state"`
	IP          types.String              `tfsdk:"ip"`
	DNSName     types.String              `tfsdk:"dnsname"`
	Owner       types.String              `tfsdk:"owner"`
	Type        types.String              `tfsdk:"type"`
	LastUpdated types.String              `tfsdk:"last_updated"`
	Operation   types.String              `tfsdk:"operation"`
	Password    types.String              `tfsdk:"password"`
	Services    []environmentServiceModel `tfsdk:"services"`
}

Here my Create method

func (r *environmentResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {

	var plan environmentResourceModel
	**diags := req.Plan.Get(ctx, &plan)** // here it is failing
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
	var envRequest client.EnvironmentCreateRequest
	envRequest.Name = plan.Name.ValueString()
	envRequest.Region = plan.Region.ValueString()
	envRequest.Password = plan.Password.ValueString()

	env, err := r.client.CreateEnvironment(envRequest)

	if err != nil {
		resp.Diagnostics.AddError("Failed to create environment", err.Error())
		return
	}

	environment := environmentResourceModel{
		Name:     types.StringValue(env.Name),
		Region:   types.StringValue(env.Region),
		State:    types.StringValue(env.State),
		IP:       types.StringValue(env.IP),
		DNSName:  types.StringValue(env.DNSName),
		Owner:    types.StringValue(env.Owner),
		Type:     types.StringValue(env.Type),
		Password: types.StringValue(envRequest.Password),
	}

	for _, service := range env.Services {
		s := environmentServiceModel{
			Name: types.StringValue(service.Name),
			URL:  types.StringValue(service.URL),
		}

		for _, cred := range service.Credentials {
			s.Credentials = append(s.Credentials, environmentCredentialModel{
				Name:  types.StringValue(cred.Name),
				Value: types.StringValue(cred.Value),
			})
		}

		environment.Services = append(environment.Services, s)
	}

	plan = environment

	// Set state to fully populated data
	diags = resp.State.Set(ctx, &plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}

The terraform configuration file is main.tf:

resource "test_environment" "edu1" {
  name     = "teste"
  region   = "us-central"
  password = "environment"
}

However, I’m encountering the following error:

test_environment.edu1: Creating...
╷
│ Error: Value Conversion Error
│
│   with test_environment.edu1,
│   on main.tf line 13, in resource "test_environment" "edu1":
│   13: resource "test_environment" "edu1" {
│
│ An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:
│
│ Received unknown value, however the target type cannot handle unknown values. Use the corresponding `types` package type or a custom type that handles unknown values.
│
│ Path: services
│ Target Type: []provider.environmentServiceModel
│ Suggested Type: basetypes.ListValue

Any insights or suggestions on how to resolve this issue would be greatly appreciated!