github.com/opentofu/opentofu@v1.7.1/internal/configs/provider_validation.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package configs
     7  
     8  import (
     9  	"fmt"
    10  	"sort"
    11  	"strings"
    12  
    13  	"github.com/hashicorp/hcl/v2"
    14  
    15  	"github.com/opentofu/opentofu/internal/addrs"
    16  )
    17  
    18  // validateProviderConfigsForTests performs the same role as
    19  // validateProviderConfigs except it validates the providers configured within
    20  // test files.
    21  //
    22  // To do this is calls out to validateProviderConfigs for each run block that
    23  // has ConfigUnderTest set.
    24  //
    25  // In addition, for each run block that executes against the main config it
    26  // validates the providers the run block wants to use match the providers
    27  // specified in the main configuration. It does this without reaching out to
    28  // validateProviderConfigs because the main configuration has already been
    29  // validated, and we don't want to redo all the work that happens in that
    30  // function. So, we only validate the providers our test files define match
    31  // the providers required by the main configuration.
    32  //
    33  // This function does some fairly controversial conversions into structures
    34  // expected by validateProviderConfigs but since we're just using it for
    35  // validation we'll still get the correct error messages, and we can make the
    36  // declaration ranges line up sensibly so we'll even get good diagnostics.
    37  func validateProviderConfigsForTests(cfg *Config) (diags hcl.Diagnostics) {
    38  
    39  	for name, test := range cfg.Module.Tests {
    40  		for _, run := range test.Runs {
    41  
    42  			if run.ConfigUnderTest == nil {
    43  				// Then we're calling out to the main configuration under test.
    44  				//
    45  				// We just need to make sure that the providers we are setting
    46  				// actually match the providers in the configuration. The main
    47  				// configuration has already been validated, so we don't need to
    48  				// do the whole thing again.
    49  
    50  				if len(run.Providers) > 0 {
    51  					// This is the easy case, we can just validate that the
    52  					// provider types match.
    53  					for _, provider := range run.Providers {
    54  
    55  						parentType, childType := provider.InParent.providerType, provider.InChild.providerType
    56  						if parentType.IsZero() {
    57  							parentType = addrs.NewDefaultProvider(provider.InParent.Name)
    58  						}
    59  						if childType.IsZero() {
    60  							childType = addrs.NewDefaultProvider(provider.InChild.Name)
    61  						}
    62  
    63  						if !childType.Equals(parentType) {
    64  							diags = append(diags, &hcl.Diagnostic{
    65  								Severity: hcl.DiagError,
    66  								Summary:  "Provider type mismatch",
    67  								Detail: fmt.Sprintf(
    68  									"The local name %q in %s represents provider %q, but %q in the root module represents %q.\n\nThis means the provider definition for %q within %s, or other provider definitions with the same name, have been referenced by multiple run blocks and assigned to different provider types.",
    69  									provider.InParent.Name, name, parentType, provider.InChild.Name, childType, provider.InParent.Name, name),
    70  								Subject: provider.InParent.NameRange.Ptr(),
    71  							})
    72  						}
    73  					}
    74  
    75  					// Skip to the next file, we only need to verify the types
    76  					// specified here.
    77  					continue
    78  				}
    79  
    80  				// Otherwise, we need to verify that the providers required by
    81  				// the configuration match the types defined by our test file.
    82  
    83  				for _, requirement := range cfg.Module.ProviderRequirements.RequiredProviders {
    84  					if provider, exists := test.Providers[requirement.Name]; exists {
    85  
    86  						providerType := provider.providerType
    87  						if providerType.IsZero() {
    88  							providerType = addrs.NewDefaultProvider(provider.Name)
    89  						}
    90  
    91  						if !providerType.Equals(requirement.Type) {
    92  							diags = append(diags, &hcl.Diagnostic{
    93  								Severity: hcl.DiagError,
    94  								Summary:  "Provider type mismatch",
    95  								Detail: fmt.Sprintf(
    96  									"The provider %q in %s represents provider %q, but %q in the root module represents %q.\n\nThis means the provider definition for %q within %s, or other provider definitions with the same name, have been referenced by multiple run blocks and assigned to different provider types.",
    97  									provider.moduleUniqueKey(), name, providerType, requirement.Name, requirement.Type, provider.moduleUniqueKey(), name),
    98  								Subject: provider.DeclRange.Ptr(),
    99  							})
   100  						}
   101  					}
   102  
   103  					for _, alias := range requirement.Aliases {
   104  						if provider, exists := test.Providers[alias.StringCompact()]; exists {
   105  
   106  							providerType := provider.providerType
   107  							if providerType.IsZero() {
   108  								providerType = addrs.NewDefaultProvider(provider.Name)
   109  							}
   110  
   111  							if !providerType.Equals(requirement.Type) {
   112  								diags = append(diags, &hcl.Diagnostic{
   113  									Severity: hcl.DiagError,
   114  									Summary:  "Provider type mismatch",
   115  									Detail: fmt.Sprintf(
   116  										"The provider %q in %s represents provider %q, but %q in the root module represents %q.\n\nThis means the provider definition for %q within %s, or other provider definitions with the same name, have been referenced by multiple run blocks and assigned to different provider types.",
   117  										provider.moduleUniqueKey(), name, providerType, alias.StringCompact(), requirement.Type, provider.moduleUniqueKey(), name),
   118  									Subject: provider.DeclRange.Ptr(),
   119  								})
   120  							}
   121  						}
   122  					}
   123  				}
   124  
   125  				for _, provider := range cfg.Module.ProviderConfigs {
   126  
   127  					providerType := provider.providerType
   128  					if providerType.IsZero() {
   129  						providerType = addrs.NewDefaultProvider(provider.Name)
   130  					}
   131  
   132  					if testProvider, exists := test.Providers[provider.moduleUniqueKey()]; exists {
   133  
   134  						testProviderType := testProvider.providerType
   135  						if testProviderType.IsZero() {
   136  							testProviderType = addrs.NewDefaultProvider(testProvider.Name)
   137  						}
   138  
   139  						if !providerType.Equals(testProviderType) {
   140  							diags = append(diags, &hcl.Diagnostic{
   141  								Severity: hcl.DiagError,
   142  								Summary:  "Provider type mismatch",
   143  								Detail: fmt.Sprintf(
   144  									"The provider %q in %s represents provider %q, but %q in the root module represents %q.\n\nThis means the provider definition for %q within %s has been referenced by multiple run blocks and assigned to different provider types.",
   145  									testProvider.moduleUniqueKey(), name, testProviderType, provider.moduleUniqueKey(), providerType, testProvider.moduleUniqueKey(), name),
   146  								Subject: testProvider.DeclRange.Ptr(),
   147  							})
   148  						}
   149  					}
   150  				}
   151  
   152  			} else {
   153  				// Then we're executing another module. We'll just call out to
   154  				// validateProviderConfigs and let it do the whole thing.
   155  
   156  				providers := run.Providers
   157  				if len(providers) == 0 {
   158  					// If the test run didn't provide us a subset of providers
   159  					// to use, we'll build our own. This is so that we can fit
   160  					// into the schema expected by validateProviderConfigs.
   161  
   162  					matchedProviders := make(map[string]PassedProviderConfig)
   163  
   164  					// We'll go over all the requirements in the module first
   165  					// and see if we have defined any providers for that
   166  					// requirement. If we have, then we'll take not of that.
   167  
   168  					for _, requirement := range cfg.Module.ProviderRequirements.RequiredProviders {
   169  
   170  						if provider, exists := test.Providers[requirement.Name]; exists {
   171  							matchedProviders[requirement.Name] = PassedProviderConfig{
   172  								InChild: &ProviderConfigRef{
   173  									Name:         requirement.Name,
   174  									NameRange:    requirement.DeclRange,
   175  									providerType: requirement.Type,
   176  								},
   177  								InParent: &ProviderConfigRef{
   178  									Name:         provider.Name,
   179  									NameRange:    provider.NameRange,
   180  									Alias:        provider.Alias,
   181  									AliasRange:   provider.AliasRange,
   182  									providerType: provider.providerType,
   183  								},
   184  							}
   185  						}
   186  
   187  						// Also, remember to check for any aliases the module
   188  						// expects.
   189  
   190  						for _, alias := range requirement.Aliases {
   191  							key := alias.StringCompact()
   192  
   193  							if provider, exists := test.Providers[key]; exists {
   194  								matchedProviders[key] = PassedProviderConfig{
   195  									InChild: &ProviderConfigRef{
   196  										Name:         requirement.Name,
   197  										NameRange:    requirement.DeclRange,
   198  										Alias:        alias.Alias,
   199  										AliasRange:   requirement.DeclRange.Ptr(),
   200  										providerType: requirement.Type,
   201  									},
   202  									InParent: &ProviderConfigRef{
   203  										Name:         provider.Name,
   204  										NameRange:    provider.NameRange,
   205  										Alias:        provider.Alias,
   206  										AliasRange:   provider.AliasRange,
   207  										providerType: provider.providerType,
   208  									},
   209  								}
   210  							}
   211  
   212  						}
   213  
   214  					}
   215  
   216  					// Next, we'll look at any providers the module has defined
   217  					// directly. If we have an equivalent provider in the test
   218  					// file then we'll add that in to override it. If the module
   219  					// has both built a required providers block and a provider
   220  					// block for the same provider, we'll overwrite the one we
   221  					// made for the requirement provider. We get more precise
   222  					// DeclRange objects from provider blocks so it makes for
   223  					// better error messages to use these.
   224  
   225  					for _, provider := range cfg.Module.ProviderConfigs {
   226  						key := provider.moduleUniqueKey()
   227  
   228  						if testProvider, exists := test.Providers[key]; exists {
   229  							matchedProviders[key] = PassedProviderConfig{
   230  								InChild: &ProviderConfigRef{
   231  									Name:         provider.Name,
   232  									NameRange:    provider.DeclRange,
   233  									Alias:        provider.Alias,
   234  									AliasRange:   provider.DeclRange.Ptr(),
   235  									providerType: provider.providerType,
   236  								},
   237  								InParent: &ProviderConfigRef{
   238  									Name:         testProvider.Name,
   239  									NameRange:    testProvider.NameRange,
   240  									Alias:        testProvider.Alias,
   241  									AliasRange:   testProvider.AliasRange,
   242  									providerType: testProvider.providerType,
   243  								},
   244  							}
   245  						}
   246  					}
   247  
   248  					// Last thing to do here is add them into the actual
   249  					// providers list that is going into the module call below.
   250  					for _, provider := range matchedProviders {
   251  						providers = append(providers, provider)
   252  					}
   253  
   254  				}
   255  
   256  				// Let's make a little fake module call that we can use to call
   257  				// into validateProviderConfigs.
   258  				mc := &ModuleCall{
   259  					Name:            run.Name,
   260  					SourceAddr:      run.Module.Source,
   261  					SourceAddrRange: run.Module.SourceDeclRange,
   262  					SourceSet:       true,
   263  					Version:         run.Module.Version,
   264  					Providers:       providers,
   265  					DeclRange:       run.Module.DeclRange,
   266  				}
   267  
   268  				diags = append(diags, validateProviderConfigs(mc, run.ConfigUnderTest, nil)...)
   269  			}
   270  		}
   271  	}
   272  
   273  	return diags
   274  }
   275  
   276  // validateProviderConfigs walks the full configuration tree from the root
   277  // module outward, static validation rules to the various combinations of
   278  // provider configuration, required_providers values, and module call providers
   279  // mappings.
   280  //
   281  // To retain compatibility with previous terraform versions, empty "proxy
   282  // provider blocks" are still allowed within modules, though they will
   283  // generate warnings when the configuration is loaded. The new validation
   284  // however will generate an error if a suitable provider configuration is not
   285  // passed in through the module call.
   286  //
   287  // The call argument is the ModuleCall for the provided Config cfg. The
   288  // noProviderConfigRange argument is passed down the call stack, indicating
   289  // that the module call, or a parent module call, has used a feature (at the
   290  // specified source location) that precludes providers from being configured at
   291  // all within the module.
   292  func validateProviderConfigs(parentCall *ModuleCall, cfg *Config, noProviderConfigRange *hcl.Range) (diags hcl.Diagnostics) {
   293  	mod := cfg.Module
   294  
   295  	for name, child := range cfg.Children {
   296  		mc := mod.ModuleCalls[name]
   297  		childNoProviderConfigRange := noProviderConfigRange
   298  		// if the module call has any of count, for_each or depends_on,
   299  		// providers are prohibited from being configured in this module, or
   300  		// any module beneath this module.
   301  		switch {
   302  		case mc.Count != nil:
   303  			childNoProviderConfigRange = mc.Count.Range().Ptr()
   304  		case mc.ForEach != nil:
   305  			childNoProviderConfigRange = mc.ForEach.Range().Ptr()
   306  		case mc.DependsOn != nil:
   307  			if len(mc.DependsOn) > 0 {
   308  				childNoProviderConfigRange = mc.DependsOn[0].SourceRange().Ptr()
   309  			} else {
   310  				// Weird! We'll just use the call itself, then.
   311  				childNoProviderConfigRange = mc.DeclRange.Ptr()
   312  			}
   313  		}
   314  		diags = append(diags, validateProviderConfigs(mc, child, childNoProviderConfigRange)...)
   315  	}
   316  
   317  	// the set of provider configuration names passed into the module, with the
   318  	// source range of the provider assignment in the module call.
   319  	passedIn := map[string]PassedProviderConfig{}
   320  
   321  	// the set of empty configurations that could be proxy configurations, with
   322  	// the source range of the empty configuration block.
   323  	emptyConfigs := map[string]hcl.Range{}
   324  
   325  	// the set of provider with a defined configuration, with the source range
   326  	// of the configuration block declaration.
   327  	configured := map[string]hcl.Range{}
   328  
   329  	// the set of configuration_aliases defined in the required_providers
   330  	// block, with the fully qualified provider type.
   331  	configAliases := map[string]addrs.AbsProviderConfig{}
   332  
   333  	// the set of provider names defined in the required_providers block, and
   334  	// their provider types.
   335  	localNames := map[string]addrs.Provider{}
   336  
   337  	for _, pc := range mod.ProviderConfigs {
   338  		name := providerName(pc.Name, pc.Alias)
   339  		// Validate the config against an empty schema to see if it's empty.
   340  		_, pcConfigDiags := pc.Config.Content(&hcl.BodySchema{})
   341  		if pcConfigDiags.HasErrors() || pc.Version.Required != nil {
   342  			configured[name] = pc.DeclRange
   343  		} else {
   344  			emptyConfigs[name] = pc.DeclRange
   345  		}
   346  	}
   347  
   348  	if mod.ProviderRequirements != nil {
   349  		// Track all known local types too to ensure we don't have duplicated
   350  		// with different local names.
   351  		localTypes := map[string]bool{}
   352  
   353  		// check for duplicate requirements of the same type
   354  		for _, req := range mod.ProviderRequirements.RequiredProviders {
   355  			if localTypes[req.Type.String()] {
   356  				// find the last declaration to give a better error
   357  				prevDecl := ""
   358  				for localName, typ := range localNames {
   359  					if typ.Equals(req.Type) {
   360  						prevDecl = localName
   361  					}
   362  				}
   363  
   364  				diags = append(diags, &hcl.Diagnostic{
   365  					Severity: hcl.DiagWarning,
   366  					Summary:  "Duplicate required provider",
   367  					Detail: fmt.Sprintf(
   368  						"Provider %s with the local name %q was previously required as %q. A provider can only be required once within required_providers.",
   369  						req.Type.ForDisplay(), req.Name, prevDecl,
   370  					),
   371  					Subject: &req.DeclRange,
   372  				})
   373  			} else if addrs.IsDefaultProvider(req.Type) {
   374  				// Now check for possible implied duplicates, where a provider
   375  				// block uses a default namespaced provider, but that provider
   376  				// was required via a different name.
   377  				impliedLocalName := req.Type.Type
   378  				// We have to search through the configs for a match, since the keys contains any aliases.
   379  				for _, pc := range mod.ProviderConfigs {
   380  					if pc.Name == impliedLocalName && req.Name != impliedLocalName {
   381  						diags = append(diags, &hcl.Diagnostic{
   382  							Severity: hcl.DiagWarning,
   383  							Summary:  "Duplicate required provider",
   384  							Detail: fmt.Sprintf(
   385  								"Provider %s with the local name %q was implicitly required via a configuration block as %q. The provider configuration block name must match the name used in required_providers.",
   386  								req.Type.ForDisplay(), req.Name, req.Type.Type,
   387  							),
   388  							Subject: &req.DeclRange,
   389  						})
   390  						break
   391  					}
   392  				}
   393  			}
   394  
   395  			localTypes[req.Type.String()] = true
   396  
   397  			localNames[req.Name] = req.Type
   398  			for _, alias := range req.Aliases {
   399  				addr := addrs.AbsProviderConfig{
   400  					Module:   cfg.Path,
   401  					Provider: req.Type,
   402  					Alias:    alias.Alias,
   403  				}
   404  				configAliases[providerName(alias.LocalName, alias.Alias)] = addr
   405  			}
   406  		}
   407  	}
   408  
   409  	checkImpliedProviderNames := func(resourceConfigs map[string]*Resource) {
   410  		// Now that we have all the provider configs and requirements validated,
   411  		// check for any resources which use an implied localname which doesn't
   412  		// match that of required_providers
   413  		for _, r := range resourceConfigs {
   414  			// We're looking for resources with no specific provider reference
   415  			if r.ProviderConfigRef != nil {
   416  				continue
   417  			}
   418  
   419  			localName := r.Addr().ImpliedProvider()
   420  
   421  			_, err := addrs.ParseProviderPart(localName)
   422  			if err != nil {
   423  				diags = append(diags, &hcl.Diagnostic{
   424  					Severity: hcl.DiagError,
   425  					Summary:  "Invalid provider local name",
   426  					Detail:   fmt.Sprintf("%q is an invalid implied provider local name: %s", localName, err),
   427  					Subject:  r.DeclRange.Ptr(),
   428  				})
   429  				continue
   430  			}
   431  
   432  			if _, ok := localNames[localName]; ok {
   433  				// OK, this was listed directly in the required_providers
   434  				continue
   435  			}
   436  
   437  			defAddr := addrs.ImpliedProviderForUnqualifiedType(localName)
   438  
   439  			// Now make sure we don't have the same provider required under a
   440  			// different name.
   441  			for prevLocalName, addr := range localNames {
   442  				if addr.Equals(defAddr) {
   443  					diags = append(diags, &hcl.Diagnostic{
   444  						Severity: hcl.DiagWarning,
   445  						Summary:  "Duplicate required provider",
   446  						Detail: fmt.Sprintf(
   447  							"Provider %q was implicitly required via resource %q, but listed in required_providers as %q. Either the local name in required_providers must match the resource name, or the %q provider must be assigned within the resource block.",
   448  							defAddr, r.Addr(), prevLocalName, prevLocalName,
   449  						),
   450  						Subject: &r.DeclRange,
   451  					})
   452  				}
   453  			}
   454  		}
   455  	}
   456  	checkImpliedProviderNames(mod.ManagedResources)
   457  	checkImpliedProviderNames(mod.DataResources)
   458  
   459  	// collect providers passed from the parent
   460  	if parentCall != nil {
   461  		for _, passed := range parentCall.Providers {
   462  			name := providerName(passed.InChild.Name, passed.InChild.Alias)
   463  			passedIn[name] = passed
   464  		}
   465  	}
   466  
   467  	parentModuleText := "the root module"
   468  	moduleText := "the root module"
   469  	if !cfg.Path.IsRoot() {
   470  		moduleText = cfg.Path.String()
   471  		if parent := cfg.Path.Parent(); !parent.IsRoot() {
   472  			// module address are prefixed with `module.`
   473  			parentModuleText = parent.String()
   474  		}
   475  	}
   476  
   477  	// Verify that any module calls only refer to named providers, and that
   478  	// those providers will have a configuration at runtime. This way we can
   479  	// direct users where to add the missing configuration, because the runtime
   480  	// error is only "missing provider X".
   481  	for _, modCall := range mod.ModuleCalls {
   482  		for _, passed := range modCall.Providers {
   483  			// aliased providers are handled more strictly, and are never
   484  			// inherited, so they are validated within modules further down.
   485  			// Skip these checks to prevent redundant diagnostics.
   486  			if passed.InParent.Alias != "" {
   487  				continue
   488  			}
   489  
   490  			name := passed.InParent.String()
   491  			_, confOK := configured[name]
   492  			_, localOK := localNames[name]
   493  			_, passedOK := passedIn[name]
   494  
   495  			// This name was not declared somewhere within in the
   496  			// configuration. We ignore empty configs, because they will
   497  			// already produce a warning.
   498  			if !(confOK || localOK) {
   499  				defAddr := addrs.NewDefaultProvider(name)
   500  				diags = append(diags, &hcl.Diagnostic{
   501  					Severity: hcl.DiagWarning,
   502  					Summary:  "Reference to undefined provider",
   503  					Detail: fmt.Sprintf(
   504  						"There is no explicit declaration for local provider name %q in %s, so OpenTofu is assuming you mean to pass a configuration for provider %q.\n\nTo clarify your intent and silence this warning, add to %s a required_providers entry named %q with source = %q, or a different source address if appropriate.",
   505  						name, moduleText, defAddr.ForDisplay(),
   506  						parentModuleText, name, defAddr.ForDisplay(),
   507  					),
   508  					Subject: &passed.InParent.NameRange,
   509  				})
   510  				continue
   511  			}
   512  
   513  			// Now we may have named this provider within the module, but
   514  			// there won't be a configuration available at runtime if the
   515  			// parent module did not pass one in.
   516  			if !cfg.Path.IsRoot() && !(confOK || passedOK) {
   517  				defAddr := addrs.NewDefaultProvider(name)
   518  				diags = append(diags, &hcl.Diagnostic{
   519  					Severity: hcl.DiagWarning,
   520  					Summary:  "Missing required provider configuration",
   521  					Detail: fmt.Sprintf(
   522  						"The configuration for %s expects to inherit a configuration for provider %s with local name %q, but %s doesn't pass a configuration under that name.\n\nTo satisfy this requirement, add an entry for %q to the \"providers\" argument in the module %q block.",
   523  						moduleText, defAddr.ForDisplay(), name, parentModuleText,
   524  						name, parentCall.Name,
   525  					),
   526  					Subject: parentCall.DeclRange.Ptr(),
   527  				})
   528  			}
   529  		}
   530  	}
   531  
   532  	if cfg.Path.IsRoot() {
   533  		// nothing else to do in the root module
   534  		return diags
   535  	}
   536  
   537  	// there cannot be any configurations if no provider config is allowed
   538  	if len(configured) > 0 && noProviderConfigRange != nil {
   539  		// We report this from the perspective of the use of count, for_each,
   540  		// or depends_on rather than from inside the module, because the
   541  		// recipient of this message is more likely to be the author of the
   542  		// calling module (trying to use an older module that hasn't been
   543  		// updated yet) than of the called module.
   544  		diags = append(diags, &hcl.Diagnostic{
   545  			Severity: hcl.DiagError,
   546  			Summary:  "Module is incompatible with count, for_each, and depends_on",
   547  			Detail: fmt.Sprintf(
   548  				"The module at %s is a legacy module which contains its own local provider configurations, and so calls to it may not use the count, for_each, or depends_on arguments.\n\nIf you also control the module %q, consider updating this module to instead expect provider configurations to be passed by its caller.",
   549  				cfg.Path, cfg.SourceAddr,
   550  			),
   551  			Subject: noProviderConfigRange,
   552  		})
   553  	}
   554  
   555  	// now check that the user is not attempting to override a config
   556  	for name := range configured {
   557  		if passed, ok := passedIn[name]; ok {
   558  			diags = append(diags, &hcl.Diagnostic{
   559  				Severity: hcl.DiagError,
   560  				Summary:  "Cannot override provider configuration",
   561  				Detail: fmt.Sprintf(
   562  					"The configuration of %s has its own local configuration for %s, and so it cannot accept an overridden configuration provided by %s.",
   563  					moduleText, name, parentModuleText,
   564  				),
   565  				Subject: &passed.InChild.NameRange,
   566  			})
   567  		}
   568  	}
   569  
   570  	// A declared alias requires either a matching configuration within the
   571  	// module, or one must be passed in.
   572  	for name, providerAddr := range configAliases {
   573  		_, confOk := configured[name]
   574  		_, passedOk := passedIn[name]
   575  
   576  		if confOk || passedOk {
   577  			continue
   578  		}
   579  
   580  		diags = append(diags, &hcl.Diagnostic{
   581  			Severity: hcl.DiagError,
   582  			Summary:  "Missing required provider configuration",
   583  			Detail: fmt.Sprintf(
   584  				"The child module requires an additional configuration for provider %s, with the local name %q.\n\nRefer to the module's documentation to understand the intended purpose of this additional provider configuration, and then add an entry for %s in the \"providers\" meta-argument in the module block to choose which provider configuration the module should use for that purpose.",
   585  				providerAddr.Provider.ForDisplay(), name,
   586  				name,
   587  			),
   588  			Subject: &parentCall.DeclRange,
   589  		})
   590  	}
   591  
   592  	// You cannot pass in a provider that cannot be used
   593  	for name, passed := range passedIn {
   594  		childTy := passed.InChild.providerType
   595  		// get a default type if there was none set
   596  		if childTy.IsZero() {
   597  			// This means the child module is only using an inferred
   598  			// provider type. We allow this but will generate a warning to
   599  			// declare provider_requirements below.
   600  			childTy = addrs.NewDefaultProvider(passed.InChild.Name)
   601  		}
   602  
   603  		providerAddr := addrs.AbsProviderConfig{
   604  			Module:   cfg.Path,
   605  			Provider: childTy,
   606  			Alias:    passed.InChild.Alias,
   607  		}
   608  
   609  		localAddr, localName := localNames[name]
   610  		if localName {
   611  			providerAddr.Provider = localAddr
   612  		}
   613  
   614  		aliasAddr, configAlias := configAliases[name]
   615  		if configAlias {
   616  			providerAddr = aliasAddr
   617  		}
   618  
   619  		_, emptyConfig := emptyConfigs[name]
   620  
   621  		if !(localName || configAlias || emptyConfig) {
   622  
   623  			// we still allow default configs, so switch to a warning if the incoming provider is a default
   624  			if addrs.IsDefaultProvider(providerAddr.Provider) {
   625  				diags = append(diags, &hcl.Diagnostic{
   626  					Severity: hcl.DiagWarning,
   627  					Summary:  "Reference to undefined provider",
   628  					Detail: fmt.Sprintf(
   629  						"There is no explicit declaration for local provider name %q in %s, so OpenTofu is assuming you mean to pass a configuration for %q.\n\nIf you also control the child module, add a required_providers entry named %q with the source address %q.",
   630  						name, moduleText, providerAddr.Provider.ForDisplay(),
   631  						name, providerAddr.Provider.ForDisplay(),
   632  					),
   633  					Subject: &passed.InChild.NameRange,
   634  				})
   635  			} else {
   636  				diags = append(diags, &hcl.Diagnostic{
   637  					Severity: hcl.DiagError,
   638  					Summary:  "Reference to undefined provider",
   639  					Detail: fmt.Sprintf(
   640  						"The child module does not declare any provider requirement with the local name %q.\n\nIf you also control the child module, you can add a required_providers entry named %q with the source address %q to accept this provider configuration.",
   641  						name, name, providerAddr.Provider.ForDisplay(),
   642  					),
   643  					Subject: &passed.InChild.NameRange,
   644  				})
   645  			}
   646  		}
   647  
   648  		// The provider being passed in must also be of the correct type.
   649  		pTy := passed.InParent.providerType
   650  		if pTy.IsZero() {
   651  			// While we would like to ensure required_providers exists here,
   652  			// implied default configuration is still allowed.
   653  			pTy = addrs.NewDefaultProvider(passed.InParent.Name)
   654  		}
   655  
   656  		// use the full address for a nice diagnostic output
   657  		parentAddr := addrs.AbsProviderConfig{
   658  			Module:   cfg.Parent.Path,
   659  			Provider: pTy,
   660  			Alias:    passed.InParent.Alias,
   661  		}
   662  
   663  		if cfg.Parent.Module.ProviderRequirements != nil {
   664  			req, defined := cfg.Parent.Module.ProviderRequirements.RequiredProviders[name]
   665  			if defined {
   666  				parentAddr.Provider = req.Type
   667  			}
   668  		}
   669  
   670  		if !providerAddr.Provider.Equals(parentAddr.Provider) {
   671  			// If this module declares the same source address for a different
   672  			// local name then we'll prefer to suggest changing to match
   673  			// the child module's chosen name, assuming that it was the local
   674  			// name that was wrong rather than the source address.
   675  			var otherLocalName string
   676  			for localName, sourceAddr := range localNames {
   677  				if sourceAddr.Equals(parentAddr.Provider) {
   678  					otherLocalName = localName
   679  					break
   680  				}
   681  			}
   682  
   683  			const errSummary = "Provider type mismatch"
   684  			if otherLocalName != "" {
   685  				diags = append(diags, &hcl.Diagnostic{
   686  					Severity: hcl.DiagError,
   687  					Summary:  errSummary,
   688  					Detail: fmt.Sprintf(
   689  						"The assigned configuration is for provider %q, but local name %q in %s represents %q.\n\nTo pass this configuration to the child module, use the local name %q instead.",
   690  						parentAddr.Provider.ForDisplay(), passed.InChild.Name,
   691  						parentModuleText, providerAddr.Provider.ForDisplay(),
   692  						otherLocalName,
   693  					),
   694  					Subject: &passed.InChild.NameRange,
   695  				})
   696  			} else {
   697  				// If there is no declared requirement for the provider the
   698  				// caller is trying to pass under any name then we'll instead
   699  				// report it as an unsuitable configuration to pass into the
   700  				// child module's provider configuration slot.
   701  				diags = append(diags, &hcl.Diagnostic{
   702  					Severity: hcl.DiagError,
   703  					Summary:  errSummary,
   704  					Detail: fmt.Sprintf(
   705  						"The local name %q in %s represents provider %q, but %q in %s represents %q.\n\nEach provider has its own distinct configuration schema and provider types, so this module's %q can be assigned only a configuration for %s, which is not required by %s.",
   706  						passed.InParent, parentModuleText, parentAddr.Provider.ForDisplay(),
   707  						passed.InChild, moduleText, providerAddr.Provider.ForDisplay(),
   708  						passed.InChild, providerAddr.Provider.ForDisplay(),
   709  						moduleText,
   710  					),
   711  					Subject: passed.InParent.NameRange.Ptr(),
   712  				})
   713  			}
   714  		}
   715  	}
   716  
   717  	// Empty configurations are no longer needed. Since the replacement for
   718  	// this calls for one entry per provider rather than one entry per
   719  	// provider _configuration_, we'll first gather them up by provider
   720  	// and then report a single warning for each, whereby we can show a direct
   721  	// example of what the replacement should look like.
   722  	type ProviderReqSuggestion struct {
   723  		SourceAddr      addrs.Provider
   724  		SourceRanges    []hcl.Range
   725  		RequiredConfigs []string
   726  		AliasCount      int
   727  	}
   728  	providerReqSuggestions := make(map[string]*ProviderReqSuggestion)
   729  	for name, src := range emptyConfigs {
   730  		providerLocalName := name
   731  		if idx := strings.IndexByte(providerLocalName, '.'); idx >= 0 {
   732  			providerLocalName = providerLocalName[:idx]
   733  		}
   734  
   735  		sourceAddr, ok := localNames[name]
   736  		if !ok {
   737  			sourceAddr = addrs.NewDefaultProvider(providerLocalName)
   738  		}
   739  
   740  		suggestion := providerReqSuggestions[providerLocalName]
   741  		if suggestion == nil {
   742  			providerReqSuggestions[providerLocalName] = &ProviderReqSuggestion{
   743  				SourceAddr: sourceAddr,
   744  			}
   745  			suggestion = providerReqSuggestions[providerLocalName]
   746  		}
   747  
   748  		if providerLocalName != name {
   749  			// It's an aliased provider config, then.
   750  			suggestion.AliasCount++
   751  		}
   752  
   753  		suggestion.RequiredConfigs = append(suggestion.RequiredConfigs, name)
   754  		suggestion.SourceRanges = append(suggestion.SourceRanges, src)
   755  	}
   756  	for name, suggestion := range providerReqSuggestions {
   757  		var buf strings.Builder
   758  
   759  		fmt.Fprintf(
   760  			&buf,
   761  			"Earlier versions of OpenTofu used empty provider blocks (\"proxy provider configurations\") for child modules to declare their need to be passed a provider configuration by their callers. That approach was ambiguous and is now deprecated.\n\nIf you control this module, you can migrate to the new declaration syntax by removing all of the empty provider %q blocks and then adding or updating an entry like the following to the required_providers block of %s:\n",
   762  			name, moduleText,
   763  		)
   764  		fmt.Fprintf(&buf, "    %s = {\n", name)
   765  		fmt.Fprintf(&buf, "      source = %q\n", suggestion.SourceAddr.ForDisplay())
   766  		if suggestion.AliasCount > 0 {
   767  			// A lexical sort is fine because all of these strings are
   768  			// guaranteed to start with the same provider local name, and
   769  			// so we're only really sorting by the alias part.
   770  			sort.Strings(suggestion.RequiredConfigs)
   771  			fmt.Fprintln(&buf, "      configuration_aliases = [")
   772  			for _, addrStr := range suggestion.RequiredConfigs {
   773  				fmt.Fprintf(&buf, "        %s,\n", addrStr)
   774  			}
   775  			fmt.Fprintln(&buf, "      ]")
   776  
   777  		}
   778  		fmt.Fprint(&buf, "    }")
   779  
   780  		// We're arbitrarily going to just take the one source range that
   781  		// sorts earliest here. Multiple should be rare, so this is only to
   782  		// ensure that we produce a deterministic result in the edge case.
   783  		sort.Slice(suggestion.SourceRanges, func(i, j int) bool {
   784  			return suggestion.SourceRanges[i].String() < suggestion.SourceRanges[j].String()
   785  		})
   786  		diags = append(diags, &hcl.Diagnostic{
   787  			Severity: hcl.DiagWarning,
   788  			Summary:  "Redundant empty provider block",
   789  			Detail:   buf.String(),
   790  			Subject:  suggestion.SourceRanges[0].Ptr(),
   791  		})
   792  	}
   793  
   794  	return diags
   795  }
   796  
   797  func providerName(name, alias string) string {
   798  	if alias != "" {
   799  		name = name + "." + alias
   800  	}
   801  	return name
   802  }