Unknown attribute value is only interpolated at Create/Read/Update/Delete stage. So basically no validator, config validation, plan modification functions can check and validate the value just before hitting the CRUD process?
You of course cannot validate a value which is not known, but that value must eventually become known before the changes can be applied, and you will be able to validate the configuration at that point. From within Terraform, that may be not be happening until the apply step, but it is still happening nonetheless.
@jbardin I think it will be helpful to add a new layer between resource config validation and CRUD funcs, to clearly separate validation of the final plan/state from the CRUD funcs.
Currently I’m doing the validation at the beginning of the Create/Update func because the validation relies on attributes across the configuration.
My use case: Validating a password based on practitioner defined password policy in the configuration. The password can be a reference from the random provider so its value will be unknown until CRUD time. But for known password value, validation can be applied in normal way and allow ‘terraform validate’ or ‘terraform plan’ to return validation error. So my code now performs this validation twice: one time for known value, second time for unknown value which will be known by CRUD func.
So something like ‘PreCreateValidate()’ will be helpful to consolidate validation into single place.
Terraform calls the ValidateResourceConfig RPC before any apply actions are taken, so there is always a chance to validate the fully-known configuration. From the resource perspective you cannot tell exactly when the validate call is happening in the series of Terraform cli operations, but you can know that once the configuration is entirely known that is the final value you will be receiving.
Oh yes, it will be unknown during the initial validations, but the final time that is called will have all known values. In general you just ignore unknown values when validating, all they can really indicate is that something might be set.
Note: When implementing validation logic, configuration values may be unknown based on the source of the value. Implementations must account for this case, typically by returning early without returning new diagnostics.
Our official validators all follow this pattern that you can reference, for example, the “string length between” validator:
I’m aware of this section of the doc and I’ve implemented validator for attribute, resource, and provider myself.
Many times.
I think you missed 2 points I am making:
I need to validate an attribute based on values from other attributes. So attribute validator won’t work in the use case
ValidateConfig() func never have the attribute value as known, when the attribute has a reference value. May be this is a bug in the plugin-framework? I don’t know.
This means the only time I can apply cross attributes validation is at the CRUD funcs, when all attribute values are known at that stage.
I’m aware of this section of the doc and I’ve implemented validator for attribute, resource, and provider myself.
Many times.
Apologies if my comment gave a negative impression, I didn’t see the doc referenced anywhere in this conversation and it could be helpful for any future travelers to this post.
The attribute validator example was meant as a reference because all validations (attribute validators, config validators, the ValidateConfig function) are executed at the same time, so the rules around config values eventually being known are the same.
Thanks for providing the example schema + ValidateConfig implementation. In addition to that, could you provide a Terraform configuration that you’re seeing the behavior where a config value (referenced from a random provider resource) doesn’t eventually pass a known value to ValidateConfig? I’d like to try and recreate the behavior to ensure terraform-plugin-framework doesn’t have a bug.
Here is my first attempt to recreate what you’re describing in my sandbox, with a resource that has a ValidateConfig method that compares two attributes only when they are both known and an acceptance test to verify the behavior that a validation message will be eventually raised when using a reference to a resource such as random_string.
Running this config with Terraform 1.9.2, I see a validation error raised
$ terraform apply -auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# examplecloud_thing.test will be created
+ resource "examplecloud_thing" "test" {
+ max_length = 5
+ str_value = (known after apply)
}
# random_string.a will be created
+ resource "random_string" "a" {
+ id = (known after apply)
+ length = 10
+ lower = true
+ min_lower = 0
+ min_numeric = 0
+ min_special = 0
+ min_upper = 0
+ number = true
+ numeric = true
+ result = (known after apply)
+ special = true
+ upper = true
}
Plan: 2 to add, 0 to change, 0 to destroy.
random_string.a: Creating...
random_string.a: Creation complete after 0s [id=tqL#KqVfYv]
╷
│ Error: Invalid Length
│
│ with examplecloud_thing.test,
│ on resource.tf line 13, in resource "examplecloud_thing" "test":
│ 13: str_value = random_string.a.result
│
│ string length must be less then 5
Let me know if my attempt to recreate what you’re describing is missing something.
I went back to my code and comment out the calls to the validation func in Create/Update, and the resource passes the tests with interpolated values (same results as your example).
I swear I was 99% certain it was behaving differently when I initially investigating this issue. Hence adding extra invocations before API calls.
Because they can result from interpolations in the practitioner’s config, you have no control over what attributes may contain an unknown value. However, by the time a resource is expected to be created, read, updated, or deleted, only its computed attributes can be unknown. The rest are guaranteed to have known values (or be null).
Hence I interpret it as meaning “interpolated value will only be known at Create/Read/Update/Delete time, and not in any of the validation steps”.
Perhaps adding a paragraph in the Validation section to mention that even though value may be unknown, it will eventually be interpolated and known.