Muxing upgraded tfsdk and framework provider with default provider configuration

Hi,

i’m muxing a tfsdk and framework (proto6) provider:

debugFlag := flag.Bool("debug", false, "Start provider in debug mode.")
	flag.Parse()

	ctx := context.Background()
	upgradedSdkProvider, err := tf5to6server.UpgradeServer(ctx, sdkv2provider.Provider().GRPCProvider)
	providers := []func() tfprotov6.ProviderServer{
		func() tfprotov6.ProviderServer {
			return upgradedSdkProvider
		},
		providerserver.NewProtocol6(framework.New(version)),
	}

	muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)

However my sdkv2 provider has a defaultfunc defined:

func Provider() *schema.Provider {
	return &schema.Provider{
		Schema: map[string]*schema.Schema{
			"username": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: descriptions["username"],
				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"EXAMPLE_USERNAME"}, "Administrator"),
			},

When this is because the default values make it conflict with the framework provider which throws:

│ The plugin returned an unexpected error from plugin6.(*GRPCProvider).ValidateProviderConfig: rpc error: code = Unknown desc = got different PrepareProviderConfig PreparedConfig response from multiple servers, not sure which to use

I have tried adding a PlanModifer to also set a default value but this isn’t working

func (p *example) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) {
	return tfsdk.Schema{
		Attributes: map[string]tfsdk.Attribute{
			"username": {
				Optional:    true,
				Type:        types.StringType,
				PlanModifiers: []tfsdk.AttributePlanModifier{
					stringDefault("Administrator"),
				},
			},

What is the correct way to set defaults and use the mux server?

Hi again @iwarapter :wave:

This is certainly an interesting issue! Here are the factors involved:

  • Terraform Plugin Protocol version 5 supports a PreparedConfig field from the PrepareProviderConfig RPC (and technically the same in version 6 in the equivalent ValidateProviderConfig RPC)
  • terraform-plugin-mux expects all provider servers to return the same PreparedConfig response from the PrepareProviderConfig RPC to prevent potential coding issues with differing provider logic between provider servers; it does not know which server’s response should be used since they differ.
  • terraform-plugin-sdk supports modification of this response field with schema.Schema type Default/DefaultFunc field usage
  • terraform-plugin-framework does not support modification of this response field (plan modifiers do not apply to providers as there is no actual plan for them, only a configuration to validate)

Essentially, the terraform-plugin-go documentation summarizes the issues with this field: tfprotov5 package - github.com/hashicorp/terraform-plugin-go/tfprotov5 - Go Packages

// This RPC call exists because early versions of the Terraform Plugin
// SDK allowed providers to set defaults for provider configurations in
// such a way that Terraform couldn’t validate the provider config
// without retrieving the default values first. As providers using
// terraform-plugin-go directly and new frameworks built on top of it
// have no such requirement, it is safe and recommended to simply set
// PreparedConfig to the value of the PrepareProviderConfigRequest’s
// Config property, indicating that no changes are needed to the
// configuration.

The framework follows that recommendation as the prior SDK behavior is technically invalid and Terraform’s support for allowing response changes was a pragmatic compromise given the provider ecosystem had become dependent on “doing the wrong thing” because the prior SDK had enabled it.

As for what to do now to get it working, the best recommendation I can provide is to migrate the default value logic in the SDK provider from the Schema into the Configure handling, e.g. using os.Getenv("..."), similar to how it would be implemented in the framework provider.

Hope this additional context is useful.