Validators - Predefined
Note: The Plugin Framework is in beta.
Attribute validators ensure that attributes do or do not contain specific values. You can use predefined validators for many use cases, or implement custom validators. Refer to Schemas - Validators in the Framework documentation for details. Refer to the Attributes - Custom Validators page in this guide to learn how to implement custom validators.
This page explains how to migrate a predefined validator from SDKv2 to the Framework.
SDKv2
In SDKv2, the ConflictsWith
, ExactlyOneOf
, AtLeastOneOf
, and RequiredWith
fields on an attribute's
schema.Schema
struct perform predefined validations on the list of attributes set for these fields.
func resourceExample() *schema.Resource {
return &schema.Resource{
/* ... */
Schema: map[string]*schema.Schema{
"attribute_example": {
ConflictsWith: []string{ /* ... */ },
ExactlyOneOf: []string{ /* ... */ },
AtLeastOneOf: []string{ /* ... */ },
RequiredWith: []string{ /* ... */ },
/* ... */
Framework
In the Framework, you implement either type of validation by setting the Validators
field on the schema.Attribute
implementation. Validators that perform the same checks as the
predefined validators in SDKv2 are
available for the Framework. If the predefined
validators do not meet your needs, you must define
custom validators.
func (r *resourceExample) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
/* ... */
Attributes: map[string]schema.Attribute{
"attribute_example": schema.StringAttribute{
Validators: []validator.String{
stringvalidator.ConflictsWith( /* ... */ ),
/* ... */
Configuration validators can also be defined for
providers,
resources and
data sources by
implementing ProviderWithConfigValidators
, ResourceWithConfigValidators
, and DataSourceWithConfigValidators
interfaces, respectively.
func (r *resourceExample) ConfigValidators(ctx context.Context) []resource.ConfigValidator {
return []resource.ConfigValidator{
resourcevalidator.Conflicting(
/* ... */
),
/* ... */
Migration Notes
Remember the following details when migrating from SDKv2 to the Framework.
- In SDKv2,
ValidateDiagFunc
is a field onschema.Schema
that you can use to define validation functions. In SDKv2, there are also built-in validations. For example,ConflictsWith
is a field on theschema.Schema
struct in SDKv2. In the Framework,Validators
is a field on eachschema.Attribute
implementation. - Validators replicating the behavior of
ConflictsWith
,ExactlyOneOf
,AtLeastOneOf
, andRequiredWith
in SDKv2 are available for the Framework in each of the type-specific packages of terraform-plugin-framework-validators. - Define custom validators when the predefined validators do not meet your requirements.
Example
The following examples show how to migrate portions of the tls provider.
For a complete example, clone the
terraform-provider-tls
repository and compare the provider.go
file in
v3.4.0
with v4.0.1.
SDKv2
The following example from the provider.go
file shows the implementation of the ConflictsWith
field on the
provider's proxy
block's url
attribute. This validator checks that the provider does not use the url
attribute
when the proxy's url is set through the environment. The example also uses the RequiredWith
field to ensure that the
password
attribute is configured when username
is, and vice-versa.
func New() (*schema.Provider, error) {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"proxy": {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"url": {
ConflictsWith: []string{"proxy.0.from_env"},
/* ... */
},
"username": {
RequiredWith: []string{"proxy.0.url"},
/* ... */
},
"password": {
RequiredWith: []string{"proxy.0.username"},
/* ... */
},
"from_env": {
ConflictsWith: []string{"proxy.0.url", "proxy.0.username", "proxy.0.password"},
/* ... */
},
},
},
},
},
}, nil
}
Framework
The following shows the same section of provider code after the migration.
This code implements the ConflictsWith
and AlsoRequires
validators with the Framework. The validators are configured
via the Validators
field of the provider's proxy
block's attribute schema.
func (p *TlsProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Blocks: map[string]schema.Block{
"proxy": schema.ListNestedBlock{
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"url": schema.StringAttribute{
Validators: []validator.String{
stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("from_env")),
},
/* ... */
},
"username": schema.StringAttribute{
Validators: []validator.String{
stringvalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("url")),
},
/* ... */
},
"password": schema.StringAttribute{
Validators: []validator.String{
stringvalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("username")),
},
/* ... */
},
"from_env": schema.BoolAttribute{
Validators: []validator.Bool{
boolvalidator.ConflictsWith(
path.MatchRelative().AtParent().AtName("url"),
path.MatchRelative().AtParent().AtName("username"),
path.MatchRelative().AtParent().AtName("password"),
),
},
/* ... */
},
},
},
},
}
}