hashicorp / hashicorp/terraform-plugin-sdk
Schema validation fails when RequiredWith attributes are combined with DefaultFunc
- 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
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