hashicorp / terraform-plugin-sdk

Terraform Plugin SDK enables building plugins (providers) to manage any service providers or custom in-house solutions

Home Page:https://developer.hashicorp.com/terraform/plugin

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Schema validation fails when RequiredWith attributes are combined with DefaultFunc

adamrothman opened this issue · comments

SDK version

v2.16.0

I know this isn't the latest version, but it's the version specified in the go.mod file of 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 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:

// 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:

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() calls (*ResourceConfig).get() which I won't reproduce here.

Terraform Configuration Files

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.

Expected Behavior

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 DefaultFuncs 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.
  2. Set values (doesn't matter what) for environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
  3. Run any Terraform command that validates the configuration, e.g. terraform validate, terraform plan, or terraform apply.
  4. Observe failure.

References