hashicorp / hashicorp/terraform-plugin-sdk

Nested data resource optional fields are initialised to golang defaults irrespective of declaration in resource plan.

Open
#498 1 comment 10 reactions 0 assignees View on GitHub
subsystem/types terraform-plugin-framework
Dominant language
Go
Stars
485
Forks
244
Avg merge
19h 57m
Merged PRs (30d)
4

Description

### Terraform Version

```
Terraform v0.12.26

```

### Terraform Configuration Files

**Terraform plan:**
```terraform
data "avi_tenant" "default_tenant" {
name = "admin"
}
resource "avi_pool" "lb_pool" {
name = "pool1"
lb_algorithm = "LB_ALGORITHM_ROUND_ROBIN"
#min_servers_up
#min_health_monitors_up
servers {
ip {
type = "V4"
addr = "10.10.23.32"
}
#port = 2254
}
tenant_ref = data.avi_tenant.default_tenant.id
}
```
**`lb_pool`** and **`servers`** from the resource plan are mapped to **ResourcePoolSchema()** and **ResourceServerSchema()** respectively.

**Schema Config:**
```go
func ResourcePoolSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"min_health_monitors_up": {
Type: schema.TypeInt,
Optional: true,
},
"min_servers_up": {
Type: schema.TypeInt,
Optional: true,
},
.,
.,
.,
"servers": {
Type: schema.TypeList,
Optional: true,
Elem: ResourceServerSchema(),
},
.,
.,
.,
}
}

func ResourceServerSchema() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"autoscaling_group_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
.,
.,
.,
"external_uuid": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
.,
.,
.,
"ip": {
Type: schema.TypeSet,
Required: true,
Elem: ResourceIpAddrSchema(),
},
"port": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
}
}
```
### Debug Output

2020-07-10T04:43:15.813Z [DEBUG] plugin.terraform-provider-avi: 2020/07/10 04:43:15 **DEBUG: Found servers with type: []interface {}.**
2020-07-10T04:43:15.813Z [DEBUG] plugin.terraform-provider-avi: 2020/07/10 04:43:15 DATA: map[autoscaling_group_name: availability_zone: description: discovered_networks:[] enabled:true external_orchestration_id: **external_uuid:** hostname: ip:0xc000b03460 location:0xc000b030e0 mac_address: nw_ref: **port:0** **prst_hdr_val:** ratio:1 resolve_server_by_dns:false rewrite_host_header:false server_node: static:false verify_network:false vm_ref:],
2020-07-10T04:43:15.813Z [DEBUG] plugin.terraform-provider-avi: TYPE: **map[string]interface {}**

### Expected Behavior
The nested resource's fields should not be initialised to default values by itself, when not declared in the plan.

### Actual Behavior

In my resource plan I have intentionally commented out three fields in `min_servers_up`, `min_health_monitors_up` and `port` where port is the field resource `servers` which is nested in `lb_pool`. The behaviour to check if the keys in the top level resource (**lb_pool**) were set/not set is fine as methods of `schema.ResourceData` like GetOk()/GetOkExists() are accessible.

But when I try to fetch the nested resource `servers` from `schema.ResourceData`

`servers , _ := d.GetOk("servers")`

the type of `servers` is `[]interface{}` which is correct as the type declared in the `schema.Resource` is `schema.TypeList`. This slice further has the interfaces of type **`map[string]interface{}`** which is the main issue as it should be of type `*schema.ResourceData`. With this behaviour I cannot use methods like GetOk()/GetOkExists() etc. on the `map[string]interface{}` type to determine if a particular key was declared in the resource plan or was set to default by golang-default initialiser and there is always a value set for particular key in the map, though these are default values but they have a serious impact onto the end-point which gets configured with these unintended values.

### Steps to Reproduce
1. `terraform init`
2. `terraform apply`

Contributor guide

Open the contributing guide

Research direction

Start by tracing schema.ResourceData.GetOk("servers") through nested TypeList handling and inspect how Elem: ResourceServerSchema() populates each map[string]interface{} value. Done means optional nested fields remain distinguishable as unset rather than receiving Go zero values, with coverage for min_servers_up, min_health_monitors_up, and port.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.