Order of Configure() calls in framework >= v0.12.0

Hi again @bumble :wave:

Currently your suspicions here are correct; the data source or resource Configure() method may get called before the provider Configure() method is called. In particular, I think I remember when doing this refactoring that the ValidateDataSourceConfig and ValidateResourceConfig RPCs may wind up calling the data source and resource Configure() methods while the validation phase in Terraform is currently intended to be an offline operation, so never configuring the provider.

Whether this is a framework bug or a feature is an interesting question – there could theoretically be use cases where configuring the data source or resource doesn’t require provider level data, so having the Configure() method executed without the provider level data would be necessary. It is also possible that even during planning or apply phases, that the provider Configure() method exits early due to unknown provider configuration values, etc. so it’s not necessarily a situation we can always protect against in the framework itself.

The best recommendations I can provide at the moment are:

  • Use a pointer type within the data source or resource type for whatever provider data/client may be expected
  • Use a nil check and early return at the top of the data source or resource Configure method
  • If the data/client happens to be unexpectedly absent during Create or other methods and cause a panic, doing a nil check on the data source or resource data/client field to raise an error diagnostic instead

Putting it all together:

type MyResource struct {
  client *example.Client
}

func (r *MyResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
  if req.ProviderData == nil {
    return
  }

  client, ok := req.ProviderData.(*example.Client)

  if !ok {
    resp.Diagnostics.AddError(/*...*/)
    return
  }

  r.client = client
}

func (r MyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
  if r.client == nil {
    resp.Diagnostics.AddError(/*...*/)
    return
  }
}

Hope this helps.