Optional block with required attribute in framework

Hi,

We are in the process of migrating our provider from SDKv2 to framework. One of our existing resource contains optional blocks at the root level, with one required attribute inside these blocks.

The repo block is optional with repositories attribute being required inside the block. There are 2 more root attributes (build and release_bundle) with identical block schema (not shown in this example below).

resource "artifactory_permission_target" "test-perm" {
  name = "test-perm"

  repo {
    includes_pattern = ["foo/**"]
    excludes_pattern = ["bar/**"]
    repositories     = ["example-repo-local"]

    actions {
      users {
        name        = "anonymous"
        permissions = ["read", "write"]
      }

      groups {
        name        = "readers"
        permissions = ["read"]
      }
    }
  }
}

See Terraform Registry for details.

Our acceptance tests (that pass for SDKv2 version of the resource) fail with the following error message:

resource_artifactory_permission_target_test.go:335: Step 1/5 error: Error running pre-apply refresh: exit status 1

Error: Missing Configuration for Required Attribute

  with artifactory_permission_target.test-perm6951051,
  on terraform_plugin_test.tf line 6, in resource "artifactory_permission_target" "test-perm6951051":
    6: 	resource "artifactory_permission_target" "test-perm6951051" {

Must set a configuration value for the build.repositories attribute as the
provider has marked it as required.

Refer to the provider documentation or contact the provider developers for
additional information about configurable attributes that are required.

Error: Missing Configuration for Required Attribute

  with artifactory_permission_target.test-perm6951051,
  on terraform_plugin_test.tf line 6, in resource "artifactory_permission_target" "test-perm6951051":
    6: 	resource "artifactory_permission_target" "test-perm6951051" {

Must set a configuration value for the release_bundle.repositories attribute
as the provider has marked it as required.

Refer to the provider documentation or contact the provider developers for
additional information about configurable attributes that are required.

(The acceptance test only have configuration for repo attribute)

This suggests that the provider bypass(?) the optional nature of the block and determines that the block’s required attribute “repositories” means the blocks are now required as well?

The root attribute repo is a SingleNestedBlock with attribute repositories’s required set to true. From the documentation, my understanding is that SingleNestedBlock is optional out of the box.

Or have I setup the block incorrectly? Or may be I should be using SingleNestedAttribute instead?

Never mind about using SingleNestedAttribute since that is not backward compatible with existing schema.

Hi @alexhung :wave: Thank you for raising this, it is a great question. Allow Optional SingleNestedBlocks and Required Attributes within · Issue #740 · hashicorp/terraform-plugin-framework · GitHub happens to be a recent GitHub issue which describes a more about the Terraform schema versus data handling in this situation. Hopefully you found that migrating the framework schema to be the same list/set nesting mode of block as the prior SDK schema should be the proper migration path in your situation to not introduce a breaking change or changes in validation behaviors.

The quick reason why single nested blocks work differently is because Terraform always treats them as having a known “entire object” value with null values for all underlying attributes when not configured. Since the “object” is known, the framework then begins running validation checks on underlying attributes. To support having the “entire object” be nullable from configuration and therefore have the underlying attribute checks skipped when the “object” is not configured, the schema would need to be switched to a single nested attribute available in protocol version 6 only. Neither of these schema concepts were implementable in the prior SDK though and changing from a prior SDK schema definition to those would introduce breaking changes for practitioners in some form, typically best reserved for a major version release of the provider.

Thank you for the link to the GitHub issue. It describes exactly my issue. I understand the underlying rationale from your explanation. However, as you mentioned, we are trying very hard to keep backward compatibility so our practitioners will not need to make any changes to HCL.

We were able to previously set the block to be Optional because SDKv2 doesn’t support blocks natively and we use either schema.TypeList or schema.TypeSet with MinItems: 1, and MaxItems: 1 to simulate the same. In all honesty, we were probably abusing the SDKv2 this way but it is what it is now.

So far I have set the repositories attribute to be optional and added a custom validator to check if it is unknown or null when its parent is set. Not ideal but at least it mimic/match the current implementation behavior.

@bflad I guess alternatively I can try to replicate the old code by using ListNestedBlock with validators to enforce only 1 item in the list. Much prefer my current workaround from above over this though.