hashicorp / hashicorp/terraform-plugin-sdk

Schema validation fails when RequiredWith attributes are combined with DefaultFunc

Open
#1,099 2 comments 9 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
485
Forks
244
Avg merge
19h 57m
Merged PRs (30d)
4

Description

### SDK version

```
v2.16.0
```

I know this isn't the latest version, but it's the version specified in the [go.mod](https://github.com/hashicorp/terraform-provider-vault/blob/v3.10.0/go.mod#L35) file of [github.com/hashicorp/terraform-provider-vault](https://github.com/hashicorp/terraform-provider-vault) v3.10.0 (latest) and the relevant pieces of code do not appear to have changed since this version.

### Relevant provider source code

The Vault provider's [`auth_login_aws`](https://registry.terraform.io/providers/hashicorp/vault/latest/docs#aws) attribute has attributes `aws_access_key_id` and `aws_secret_access_key` and uses `RequiredWith` to assert that they must be set together. Schemata for these fields are defined in [internal/provider/auth_aws.go](https://github.com/hashicorp/terraform-provider-vault/blob/v3.10.0/internal/provider/auth_aws.go#L33-L46):

```go
// static credential fields
consts.FieldAWSAccessKeyID: {
Type: schema.TypeString,
Optional: true,
Description: `The AWS access key ID.`,
DefaultFunc: schema.EnvDefaultFunc("AWS_ACCESS_KEY_ID", nil),
},
consts.FieldAWSSecretAccessKey: {
Type: schema.TypeString,
Optional: true,
Description: `The AWS secret access key.`,
DefaultFunc: schema.EnvDefaultFunc("AWS_SECRET_ACCESS_KEY", nil),
RequiredWith: []string{fmt.Sprintf("%s.0.%s", authField, consts.FieldAWSAccessKeyID)},
},
```

The validator in play is `validateRequiredWithAttribute()` in [helper/schema/schema.go](https://github.com/hashicorp/terraform-plugin-sdk/blob/v2.16.0/helper/schema/schema.go#L1797-L1816):

```go
func validateRequiredWithAttribute(
k string,
schema *Schema,
c *terraform.ResourceConfig) error {

if len(schema.RequiredWith) == 0 {
return nil
}

allKeys := removeDuplicates(append(schema.RequiredWith, k))
sort.Strings(allKeys)

for _, key := range allKeys {
if _, ok := c.Get(key); !ok {
return fmt.Errorf("%q: all of `%s` must be specified", k, strings.Join(allKeys, ","))
}
}

return nil
}
```

[`(*ResourceConfig).Get()`](https://github.com/hashicorp/terraform-plugin-sdk/blob/v2.16.0/terraform/resource.go#L202-L211) calls [`(*ResourceConfig).get()`](https://github.com/hashicorp/terraform-plugin-sdk/blob/v2.16.0/terraform/resource.go#L245-L318) which I won't reproduce here.

### Terraform Configuration Files

```hcl
provider "vault" {
address = "http://my.vault.host:8200"
skip_tls_verify = true

auth_login_aws {
role = "my-role"
header_value = local.aws_auth_header
}
}
```

### Debug Output

I've encrypted the output of `TF_LOG=trace terraform validate` with Hashicorp's PGP key `72D7468F` and uploaded the result as a Gist [here](https://gist.github.com/adamrothman/16fcab8798984a3e2fcaac5002523e9c).

### Expected Behavior

```hcl
provider "vault" {
address = "http://my.vault.host:8200"
skip_tls_verify = true

auth_login_aws {
role = "my-role"
header_value = local.aws_auth_header
}
}
```

When the environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are set, the provider configuration above is valid and well-formed. Validation of the `auth_login_aws` attribute should succeed even though its `aws_access_key_id` and `aws_secret_access_key` attributes are not set.

As far as I can tell, this happens because `validateRequiredWithAttribute()` does not take the default values for these attributes as provided by their `DefaultFunc`s into account.

### Actual Behavior

Validation of the `auth_login_aws` attribute fails:

```

│ Error: Missing required argument

│ with provider["registry.terraform.io/hashicorp/vault"],
│ on providers.tf line 13, in provider "vault":
│ 13: auth_login_aws {

│ "auth_login_aws.0.aws_secret_access_key": all of `auth_login_aws.0.aws_access_key_id,auth_login_aws.0.aws_secret_access_key` must be specified

```

### Steps to Reproduce

1. Create a Terraform environment that includes the Vault provider as configured above.
1. Set values (doesn't matter what) for environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
1. Run any Terraform command that validates the configuration, e.g. `terraform validate`, `terraform plan`, or `terraform apply`.
1. Observe failure.

### References

- https://github.com/hashicorp/terraform-plugin-sdk/pull/342

Contributor guide

Open the contributing guide

Research direction

Start with validateRequiredWithAttribute() in helper/schema/schema.go and follow ResourceConfig.Get() through terraform/resource.go. Reproduce the failure using the Vault auth_login_aws configuration and the AWS environment variables described in the issue. Done means validation accepts values supplied by DefaultFunc while still enforcing RequiredWith when values are absent.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, terraform
Domain
backend-api-design, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.