github.com/opentofu/opentofu@v1.7.1/internal/command/init.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 command
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"log"
    12  	"reflect"
    13  	"sort"
    14  	"strings"
    15  
    16  	"github.com/hashicorp/hcl/v2"
    17  	svchost "github.com/hashicorp/terraform-svchost"
    18  	"github.com/posener/complete"
    19  	"github.com/zclconf/go-cty/cty"
    20  	"go.opentelemetry.io/otel/attribute"
    21  	"go.opentelemetry.io/otel/codes"
    22  	"go.opentelemetry.io/otel/trace"
    23  
    24  	"github.com/opentofu/opentofu/internal/addrs"
    25  	"github.com/opentofu/opentofu/internal/backend"
    26  	backendInit "github.com/opentofu/opentofu/internal/backend/init"
    27  	"github.com/opentofu/opentofu/internal/cloud"
    28  	"github.com/opentofu/opentofu/internal/command/arguments"
    29  	"github.com/opentofu/opentofu/internal/command/views"
    30  	"github.com/opentofu/opentofu/internal/configs"
    31  	"github.com/opentofu/opentofu/internal/configs/configschema"
    32  	"github.com/opentofu/opentofu/internal/encryption"
    33  	"github.com/opentofu/opentofu/internal/getproviders"
    34  	"github.com/opentofu/opentofu/internal/providercache"
    35  	"github.com/opentofu/opentofu/internal/states"
    36  	"github.com/opentofu/opentofu/internal/tfdiags"
    37  	"github.com/opentofu/opentofu/internal/tofu"
    38  	"github.com/opentofu/opentofu/internal/tofumigrate"
    39  	tfversion "github.com/opentofu/opentofu/version"
    40  )
    41  
    42  // InitCommand is a Command implementation that takes a Terraform
    43  // module and clones it to the working directory.
    44  type InitCommand struct {
    45  	Meta
    46  }
    47  
    48  func (c *InitCommand) Run(args []string) int {
    49  	var flagFromModule, flagLockfile, testsDirectory string
    50  	var flagBackend, flagCloud, flagGet, flagUpgrade bool
    51  	var flagPluginPath FlagStringSlice
    52  	flagConfigExtra := newRawFlags("-backend-config")
    53  
    54  	args = c.Meta.process(args)
    55  	cmdFlags := c.Meta.extendedFlagSet("init")
    56  	cmdFlags.BoolVar(&flagBackend, "backend", true, "")
    57  	cmdFlags.BoolVar(&flagCloud, "cloud", true, "")
    58  	cmdFlags.Var(flagConfigExtra, "backend-config", "")
    59  	cmdFlags.StringVar(&flagFromModule, "from-module", "", "copy the source of the given module into the directory before init")
    60  	cmdFlags.BoolVar(&flagGet, "get", true, "")
    61  	cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
    62  	cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
    63  	cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
    64  	cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure")
    65  	cmdFlags.BoolVar(&c.migrateState, "migrate-state", false, "migrate state")
    66  	cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "")
    67  	cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory")
    68  	cmdFlags.StringVar(&flagLockfile, "lockfile", "", "Set a dependency lockfile mode")
    69  	cmdFlags.BoolVar(&c.Meta.ignoreRemoteVersion, "ignore-remote-version", false, "continue even if remote and local OpenTofu versions are incompatible")
    70  	cmdFlags.StringVar(&testsDirectory, "test-directory", "tests", "test-directory")
    71  	cmdFlags.BoolVar(&c.outputInJSON, "json", false, "json")
    72  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    73  	if err := cmdFlags.Parse(args); err != nil {
    74  		return 1
    75  	}
    76  
    77  	if c.outputInJSON {
    78  		c.Meta.color = false
    79  		c.Meta.Color = false
    80  		c.oldUi = c.Ui
    81  		c.Ui = &WrappedUi{
    82  			cliUi:        c.oldUi,
    83  			jsonView:     views.NewJSONView(c.View),
    84  			outputInJSON: true,
    85  		}
    86  	}
    87  
    88  	backendFlagSet := arguments.FlagIsSet(cmdFlags, "backend")
    89  	cloudFlagSet := arguments.FlagIsSet(cmdFlags, "cloud")
    90  
    91  	switch {
    92  	case backendFlagSet && cloudFlagSet:
    93  		c.Ui.Error("The -backend and -cloud options are aliases of one another and mutually-exclusive in their use")
    94  		return 1
    95  	case backendFlagSet:
    96  		flagCloud = flagBackend
    97  	case cloudFlagSet:
    98  		flagBackend = flagCloud
    99  	}
   100  
   101  	if c.migrateState && c.reconfigure {
   102  		c.Ui.Error("The -migrate-state and -reconfigure options are mutually-exclusive")
   103  		return 1
   104  	}
   105  
   106  	// Copying the state only happens during backend migration, so setting
   107  	// -force-copy implies -migrate-state
   108  	if c.forceInitCopy {
   109  		c.migrateState = true
   110  	}
   111  
   112  	var diags tfdiags.Diagnostics
   113  
   114  	if len(flagPluginPath) > 0 {
   115  		c.pluginPath = flagPluginPath
   116  	}
   117  
   118  	// Validate the arg count and get the working directory
   119  	args = cmdFlags.Args()
   120  	path, err := modulePath(args)
   121  	if err != nil {
   122  		c.Ui.Error(err.Error())
   123  		return 1
   124  	}
   125  
   126  	if err := c.storePluginPath(c.pluginPath); err != nil {
   127  		c.Ui.Error(fmt.Sprintf("Error saving -plugin-path values: %s", err))
   128  		return 1
   129  	}
   130  
   131  	// Initialization can be aborted by interruption signals
   132  	ctx, done := c.InterruptibleContext(c.CommandContext())
   133  	defer done()
   134  
   135  	// This will track whether we outputted anything so that we know whether
   136  	// to output a newline before the success message
   137  	var header bool
   138  
   139  	if flagFromModule != "" {
   140  		src := flagFromModule
   141  
   142  		empty, err := configs.IsEmptyDir(path)
   143  		if err != nil {
   144  			c.Ui.Error(fmt.Sprintf("Error validating destination directory: %s", err))
   145  			return 1
   146  		}
   147  		if !empty {
   148  			c.Ui.Error(strings.TrimSpace(errInitCopyNotEmpty))
   149  			return 1
   150  		}
   151  
   152  		c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
   153  			"[reset][bold]Copying configuration[reset] from %q...", src,
   154  		)))
   155  		header = true
   156  
   157  		hooks := uiModuleInstallHooks{
   158  			Ui:             c.Ui,
   159  			ShowLocalPaths: false, // since they are in a weird location for init
   160  		}
   161  
   162  		ctx, span := tracer.Start(ctx, "-from-module=...", trace.WithAttributes(
   163  			attribute.String("module_source", src),
   164  		))
   165  
   166  		initDirFromModuleAbort, initDirFromModuleDiags := c.initDirFromModule(ctx, path, src, hooks)
   167  		diags = diags.Append(initDirFromModuleDiags)
   168  		if initDirFromModuleAbort || initDirFromModuleDiags.HasErrors() {
   169  			c.showDiagnostics(diags)
   170  			span.SetStatus(codes.Error, "module installation failed")
   171  			span.End()
   172  			return 1
   173  		}
   174  		span.End()
   175  
   176  		c.Ui.Output("")
   177  	}
   178  
   179  	// If our directory is empty, then we're done. We can't get or set up
   180  	// the backend with an empty directory.
   181  	empty, err := configs.IsEmptyDir(path)
   182  	if err != nil {
   183  		diags = diags.Append(fmt.Errorf("Error checking configuration: %w", err))
   184  		c.showDiagnostics(diags)
   185  		return 1
   186  	}
   187  	if empty {
   188  		c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitEmpty)))
   189  		return 0
   190  	}
   191  
   192  	// Load just the root module to begin backend and module initialization
   193  	rootModEarly, earlyConfDiags := c.loadSingleModuleWithTests(path, testsDirectory)
   194  
   195  	// There may be parsing errors in config loading but these will be shown later _after_
   196  	// checking for core version requirement errors. Not meeting the version requirement should
   197  	// be the first error displayed if that is an issue, but other operations are required
   198  	// before being able to check core version requirements.
   199  	if rootModEarly == nil {
   200  		c.Ui.Error(c.Colorize().Color(strings.TrimSpace(errInitConfigError)))
   201  		diags = diags.Append(earlyConfDiags)
   202  		c.showDiagnostics(diags)
   203  
   204  		return 1
   205  	}
   206  
   207  	// Load the encryption configuration
   208  	enc, encDiags := c.EncryptionFromModule(rootModEarly)
   209  	diags = diags.Append(encDiags)
   210  	if encDiags.HasErrors() {
   211  		c.showDiagnostics(diags)
   212  		return 1
   213  	}
   214  
   215  	var back backend.Backend
   216  
   217  	// There may be config errors or backend init errors but these will be shown later _after_
   218  	// checking for core version requirement errors.
   219  	var backDiags tfdiags.Diagnostics
   220  	var backendOutput bool
   221  
   222  	switch {
   223  	case flagCloud && rootModEarly.CloudConfig != nil:
   224  		back, backendOutput, backDiags = c.initCloud(ctx, rootModEarly, flagConfigExtra, enc)
   225  	case flagBackend:
   226  		back, backendOutput, backDiags = c.initBackend(ctx, rootModEarly, flagConfigExtra, enc)
   227  	default:
   228  		// load the previously-stored backend config
   229  		back, backDiags = c.Meta.backendFromState(ctx, enc.State())
   230  	}
   231  	if backendOutput {
   232  		header = true
   233  	}
   234  
   235  	var state *states.State
   236  
   237  	// If we have a functional backend (either just initialized or initialized
   238  	// on a previous run) we'll use the current state as a potential source
   239  	// of provider dependencies.
   240  	if back != nil {
   241  		c.ignoreRemoteVersionConflict(back)
   242  		workspace, err := c.Workspace()
   243  		if err != nil {
   244  			c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err))
   245  			return 1
   246  		}
   247  		sMgr, err := back.StateMgr(workspace)
   248  		if err != nil {
   249  			c.Ui.Error(fmt.Sprintf("Error loading state: %s", err))
   250  			return 1
   251  		}
   252  
   253  		if err := sMgr.RefreshState(); err != nil {
   254  			c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err))
   255  			return 1
   256  		}
   257  
   258  		state = sMgr.State()
   259  	}
   260  
   261  	if flagGet {
   262  		modsOutput, modsAbort, modsDiags := c.getModules(ctx, path, testsDirectory, rootModEarly, flagUpgrade)
   263  		diags = diags.Append(modsDiags)
   264  		if modsAbort || modsDiags.HasErrors() {
   265  			c.showDiagnostics(diags)
   266  			return 1
   267  		}
   268  		if modsOutput {
   269  			header = true
   270  		}
   271  	}
   272  
   273  	// With all of the modules (hopefully) installed, we can now try to load the
   274  	// whole configuration tree.
   275  	config, confDiags := c.loadConfigWithTests(path, testsDirectory)
   276  	// configDiags will be handled after the version constraint check, since an
   277  	// incorrect version of tofu may be producing errors for configuration
   278  	// constructs added in later versions.
   279  
   280  	// Before we go further, we'll check to make sure none of the modules in
   281  	// the configuration declare that they don't support this OpenTofu
   282  	// version, so we can produce a version-related error message rather than
   283  	// potentially-confusing downstream errors.
   284  	versionDiags := tofu.CheckCoreVersionRequirements(config)
   285  	if versionDiags.HasErrors() {
   286  		c.showDiagnostics(versionDiags)
   287  		return 1
   288  	}
   289  
   290  	// We've passed the core version check, now we can show errors from the
   291  	// configuration and backend initialization.
   292  
   293  	// Now, we can check the diagnostics from the early configuration and the
   294  	// backend.
   295  	diags = diags.Append(earlyConfDiags)
   296  	diags = diags.Append(backDiags)
   297  	if earlyConfDiags.HasErrors() {
   298  		c.Ui.Error(strings.TrimSpace(errInitConfigError))
   299  		c.showDiagnostics(diags)
   300  		return 1
   301  	}
   302  
   303  	// Now, we can show any errors from initializing the backend, but we won't
   304  	// show the errInitConfigError preamble as we didn't detect problems with
   305  	// the early configuration.
   306  	if backDiags.HasErrors() {
   307  		c.showDiagnostics(diags)
   308  		return 1
   309  	}
   310  
   311  	// If everything is ok with the core version check and backend initialization,
   312  	// show other errors from loading the full configuration tree.
   313  	diags = diags.Append(confDiags)
   314  	if confDiags.HasErrors() {
   315  		c.Ui.Error(strings.TrimSpace(errInitConfigError))
   316  		c.showDiagnostics(diags)
   317  		return 1
   318  	}
   319  
   320  	if cb, ok := back.(*cloud.Cloud); ok {
   321  		if c.RunningInAutomation {
   322  			if err := cb.AssertImportCompatible(config); err != nil {
   323  				diags = diags.Append(tfdiags.Sourceless(tfdiags.Error, "Compatibility error", err.Error()))
   324  				c.showDiagnostics(diags)
   325  				return 1
   326  			}
   327  		}
   328  	}
   329  
   330  	if state != nil {
   331  		// Since we now have the full configuration loaded, we can use it to migrate the in-memory state view
   332  		// prior to fetching providers.
   333  		migratedState, migrateDiags := tofumigrate.MigrateStateProviderAddresses(config, state)
   334  		diags = diags.Append(migrateDiags)
   335  		if migrateDiags.HasErrors() {
   336  			c.showDiagnostics(diags)
   337  			return 1
   338  		}
   339  		state = migratedState
   340  	}
   341  
   342  	// Now that we have loaded all modules, check the module tree for missing providers.
   343  	providersOutput, providersAbort, providerDiags := c.getProviders(ctx, config, state, flagUpgrade, flagPluginPath, flagLockfile)
   344  	diags = diags.Append(providerDiags)
   345  	if providersAbort || providerDiags.HasErrors() {
   346  		c.showDiagnostics(diags)
   347  		return 1
   348  	}
   349  	if providersOutput {
   350  		header = true
   351  	}
   352  
   353  	// If we outputted information, then we need to output a newline
   354  	// so that our success message is nicely spaced out from prior text.
   355  	if header {
   356  		c.Ui.Output("")
   357  	}
   358  
   359  	// If we accumulated any warnings along the way that weren't accompanied
   360  	// by errors then we'll output them here so that the success message is
   361  	// still the final thing shown.
   362  	c.showDiagnostics(diags)
   363  	_, cloud := back.(*cloud.Cloud)
   364  	output := outputInitSuccess
   365  	if cloud {
   366  		output = outputInitSuccessCloud
   367  	}
   368  
   369  	c.Ui.Output(c.Colorize().Color(strings.TrimSpace(output)))
   370  
   371  	if !c.RunningInAutomation {
   372  		// If we're not running in an automation wrapper, give the user
   373  		// some more detailed next steps that are appropriate for interactive
   374  		// shell usage.
   375  		output = outputInitSuccessCLI
   376  		if cloud {
   377  			output = outputInitSuccessCLICloud
   378  		}
   379  		c.Ui.Output(c.Colorize().Color(strings.TrimSpace(output)))
   380  	}
   381  	return 0
   382  }
   383  
   384  func (c *InitCommand) getModules(ctx context.Context, path, testsDir string, earlyRoot *configs.Module, upgrade bool) (output bool, abort bool, diags tfdiags.Diagnostics) {
   385  	testModules := false // We can also have modules buried in test files.
   386  	for _, file := range earlyRoot.Tests {
   387  		for _, run := range file.Runs {
   388  			if run.Module != nil {
   389  				testModules = true
   390  			}
   391  		}
   392  	}
   393  
   394  	if len(earlyRoot.ModuleCalls) == 0 && !testModules {
   395  		// Nothing to do
   396  		return false, false, nil
   397  	}
   398  
   399  	ctx, span := tracer.Start(ctx, "install modules", trace.WithAttributes(
   400  		attribute.Bool("upgrade", upgrade),
   401  	))
   402  	defer span.End()
   403  
   404  	if upgrade {
   405  		c.Ui.Output(c.Colorize().Color("[reset][bold]Upgrading modules..."))
   406  	} else {
   407  		c.Ui.Output(c.Colorize().Color("[reset][bold]Initializing modules..."))
   408  	}
   409  
   410  	hooks := uiModuleInstallHooks{
   411  		Ui:             c.Ui,
   412  		ShowLocalPaths: true,
   413  	}
   414  
   415  	installAbort, installDiags := c.installModules(ctx, path, testsDir, upgrade, false, hooks)
   416  	diags = diags.Append(installDiags)
   417  
   418  	// At this point, installModules may have generated error diags or been
   419  	// aborted by SIGINT. In any case we continue and the manifest as best
   420  	// we can.
   421  
   422  	// Since module installer has modified the module manifest on disk, we need
   423  	// to refresh the cache of it in the loader.
   424  	if c.configLoader != nil {
   425  		if err := c.configLoader.RefreshModules(); err != nil {
   426  			// Should never happen
   427  			diags = diags.Append(tfdiags.Sourceless(
   428  				tfdiags.Error,
   429  				"Failed to read module manifest",
   430  				fmt.Sprintf("After installing modules, OpenTofu could not re-read the manifest of installed modules. This is a bug in OpenTofu. %s.", err),
   431  			))
   432  		}
   433  	}
   434  
   435  	return true, installAbort, diags
   436  }
   437  
   438  func (c *InitCommand) initCloud(ctx context.Context, root *configs.Module, extraConfig rawFlags, enc encryption.Encryption) (be backend.Backend, output bool, diags tfdiags.Diagnostics) {
   439  	ctx, span := tracer.Start(ctx, "initialize cloud backend")
   440  	_ = ctx // prevent staticcheck from complaining to avoid a maintenence hazard of having the wrong ctx in scope here
   441  	defer span.End()
   442  
   443  	c.Ui.Output(c.Colorize().Color("\n[reset][bold]Initializing cloud backend..."))
   444  
   445  	if len(extraConfig.AllItems()) != 0 {
   446  		diags = diags.Append(tfdiags.Sourceless(
   447  			tfdiags.Error,
   448  			"Invalid command-line option",
   449  			"The -backend-config=... command line option is only for state backends, and is not applicable to cloud backend-based configurations.\n\nTo change the set of workspaces associated with this configuration, edit the Cloud configuration block in the root module.",
   450  		))
   451  		return nil, true, diags
   452  	}
   453  
   454  	backendConfig := root.CloudConfig.ToBackendConfig()
   455  
   456  	opts := &BackendOpts{
   457  		Config: &backendConfig,
   458  		Init:   true,
   459  	}
   460  
   461  	back, backDiags := c.Backend(opts, enc.State())
   462  	diags = diags.Append(backDiags)
   463  	return back, true, diags
   464  }
   465  
   466  func (c *InitCommand) initBackend(ctx context.Context, root *configs.Module, extraConfig rawFlags, enc encryption.Encryption) (be backend.Backend, output bool, diags tfdiags.Diagnostics) {
   467  	ctx, span := tracer.Start(ctx, "initialize backend")
   468  	_ = ctx // prevent staticcheck from complaining to avoid a maintenence hazard of having the wrong ctx in scope here
   469  	defer span.End()
   470  
   471  	c.Ui.Output(c.Colorize().Color("\n[reset][bold]Initializing the backend..."))
   472  
   473  	var backendConfig *configs.Backend
   474  	var backendConfigOverride hcl.Body
   475  	if root.Backend != nil {
   476  		backendType := root.Backend.Type
   477  		if backendType == "cloud" {
   478  			diags = diags.Append(&hcl.Diagnostic{
   479  				Severity: hcl.DiagError,
   480  				Summary:  "Unsupported backend type",
   481  				Detail:   fmt.Sprintf("There is no explicit backend type named %q. To configure cloud backend, declare a 'cloud' block instead.", backendType),
   482  				Subject:  &root.Backend.TypeRange,
   483  			})
   484  			return nil, true, diags
   485  		}
   486  
   487  		bf := backendInit.Backend(backendType)
   488  		if bf == nil {
   489  			detail := fmt.Sprintf("There is no backend type named %q.", backendType)
   490  			if msg, removed := backendInit.RemovedBackends[backendType]; removed {
   491  				detail = msg
   492  			}
   493  
   494  			diags = diags.Append(&hcl.Diagnostic{
   495  				Severity: hcl.DiagError,
   496  				Summary:  "Unsupported backend type",
   497  				Detail:   detail,
   498  				Subject:  &root.Backend.TypeRange,
   499  			})
   500  			return nil, true, diags
   501  		}
   502  
   503  		b := bf(nil) // This is only used to get the schema, encryption should panic if attempted
   504  		backendSchema := b.ConfigSchema()
   505  		backendConfig = root.Backend
   506  
   507  		var overrideDiags tfdiags.Diagnostics
   508  		backendConfigOverride, overrideDiags = c.backendConfigOverrideBody(extraConfig, backendSchema)
   509  		diags = diags.Append(overrideDiags)
   510  		if overrideDiags.HasErrors() {
   511  			return nil, true, diags
   512  		}
   513  	} else {
   514  		// If the user supplied a -backend-config on the CLI but no backend
   515  		// block was found in the configuration, it's likely - but not
   516  		// necessarily - a mistake. Return a warning.
   517  		if !extraConfig.Empty() {
   518  			diags = diags.Append(tfdiags.Sourceless(
   519  				tfdiags.Warning,
   520  				"Missing backend configuration",
   521  				`-backend-config was used without a "backend" block in the configuration.
   522  
   523  If you intended to override the default local backend configuration,
   524  no action is required, but you may add an explicit backend block to your
   525  configuration to clear this warning:
   526  
   527  terraform {
   528    backend "local" {}
   529  }
   530  
   531  However, if you intended to override a defined backend, please verify that
   532  the backend configuration is present and valid.
   533  `,
   534  			))
   535  		}
   536  	}
   537  
   538  	opts := &BackendOpts{
   539  		Config:         backendConfig,
   540  		ConfigOverride: backendConfigOverride,
   541  		Init:           true,
   542  	}
   543  
   544  	back, backDiags := c.Backend(opts, enc.State())
   545  	diags = diags.Append(backDiags)
   546  	return back, true, diags
   547  }
   548  
   549  // Load the complete module tree, and fetch any missing providers.
   550  // This method outputs its own Ui.
   551  func (c *InitCommand) getProviders(ctx context.Context, config *configs.Config, state *states.State, upgrade bool, pluginDirs []string, flagLockfile string) (output, abort bool, diags tfdiags.Diagnostics) {
   552  	ctx, span := tracer.Start(ctx, "install providers")
   553  	defer span.End()
   554  
   555  	// Dev overrides cause the result of "tofu init" to be irrelevant for
   556  	// any overridden providers, so we'll warn about it to avoid later
   557  	// confusion when OpenTofu ends up using a different provider than the
   558  	// lock file called for.
   559  	diags = diags.Append(c.providerDevOverrideInitWarnings())
   560  
   561  	// First we'll collect all the provider dependencies we can see in the
   562  	// configuration and the state.
   563  	reqs, hclDiags := config.ProviderRequirements()
   564  	diags = diags.Append(hclDiags)
   565  	if hclDiags.HasErrors() {
   566  		return false, true, diags
   567  	}
   568  	if state != nil {
   569  		stateReqs := state.ProviderRequirements()
   570  		reqs = reqs.Merge(stateReqs)
   571  	}
   572  
   573  	potentialProviderConflicts := make(map[string][]string)
   574  
   575  	for providerAddr := range reqs {
   576  		if providerAddr.Namespace == "hashicorp" || providerAddr.Namespace == "opentofu" {
   577  			potentialProviderConflicts[providerAddr.Type] = append(potentialProviderConflicts[providerAddr.Type], providerAddr.ForDisplay())
   578  		}
   579  
   580  		if providerAddr.IsLegacy() {
   581  			diags = diags.Append(tfdiags.Sourceless(
   582  				tfdiags.Error,
   583  				"Invalid legacy provider address",
   584  				fmt.Sprintf(
   585  					"This configuration or its associated state refers to the unqualified provider %q.\n\nYou must complete the Terraform 0.13 upgrade process before upgrading to later versions.",
   586  					providerAddr.Type,
   587  				),
   588  			))
   589  		}
   590  	}
   591  
   592  	for name, addrs := range potentialProviderConflicts {
   593  		if len(addrs) > 1 {
   594  			diags = diags.Append(tfdiags.Sourceless(
   595  				tfdiags.Warning,
   596  				"Potential provider misconfiguration",
   597  				fmt.Sprintf(
   598  					"OpenTofu has detected multiple providers of type %s (%s) which may be a misconfiguration.\n\nIf this is intentional you can ignore this warning",
   599  					name,
   600  					strings.Join(addrs, ", "),
   601  				),
   602  			))
   603  		}
   604  	}
   605  
   606  	previousLocks, moreDiags := c.lockedDependencies()
   607  	diags = diags.Append(moreDiags)
   608  
   609  	if diags.HasErrors() {
   610  		return false, true, diags
   611  	}
   612  
   613  	var inst *providercache.Installer
   614  	if len(pluginDirs) == 0 {
   615  		// By default we use a source that looks for providers in all of the
   616  		// standard locations, possibly customized by the user in CLI config.
   617  		inst = c.providerInstaller()
   618  	} else {
   619  		// If the user passes at least one -plugin-dir then that circumvents
   620  		// the usual sources and forces OpenTofu to consult only the given
   621  		// directories. Anything not available in one of those directories
   622  		// is not available for installation.
   623  		source := c.providerCustomLocalDirectorySource(pluginDirs)
   624  		inst = c.providerInstallerCustomSource(source)
   625  
   626  		// The default (or configured) search paths are logged earlier, in provider_source.go
   627  		// Log that those are being overridden by the `-plugin-dir` command line options
   628  		log.Println("[DEBUG] init: overriding provider plugin search paths")
   629  		log.Printf("[DEBUG] will search for provider plugins in %s", pluginDirs)
   630  	}
   631  
   632  	// We want to print out a nice warning if we don't manage to pull
   633  	// checksums for all our providers. This is tracked via callbacks
   634  	// and incomplete providers are stored here for later analysis.
   635  	var incompleteProviders []string
   636  
   637  	// Because we're currently just streaming a series of events sequentially
   638  	// into the terminal, we're showing only a subset of the events to keep
   639  	// things relatively concise. Later it'd be nice to have a progress UI
   640  	// where statuses update in-place, but we can't do that as long as we
   641  	// are shimming our vt100 output to the legacy console API on Windows.
   642  	evts := &providercache.InstallerEvents{
   643  		PendingProviders: func(reqs map[addrs.Provider]getproviders.VersionConstraints) {
   644  			c.Ui.Output(c.Colorize().Color(
   645  				"\n[reset][bold]Initializing provider plugins...",
   646  			))
   647  		},
   648  		ProviderAlreadyInstalled: func(provider addrs.Provider, selectedVersion getproviders.Version) {
   649  			c.Ui.Info(fmt.Sprintf("- Using previously-installed %s v%s", provider.ForDisplay(), selectedVersion))
   650  		},
   651  		BuiltInProviderAvailable: func(provider addrs.Provider) {
   652  			c.Ui.Info(fmt.Sprintf("- %s is built in to OpenTofu", provider.ForDisplay()))
   653  		},
   654  		BuiltInProviderFailure: func(provider addrs.Provider, err error) {
   655  			diags = diags.Append(tfdiags.Sourceless(
   656  				tfdiags.Error,
   657  				"Invalid dependency on built-in provider",
   658  				fmt.Sprintf("Cannot use %s: %s.", provider.ForDisplay(), err),
   659  			))
   660  		},
   661  		QueryPackagesBegin: func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints, locked bool) {
   662  			if locked {
   663  				c.Ui.Info(fmt.Sprintf("- Reusing previous version of %s from the dependency lock file", provider.ForDisplay()))
   664  			} else {
   665  				if len(versionConstraints) > 0 {
   666  					c.Ui.Info(fmt.Sprintf("- Finding %s versions matching %q...", provider.ForDisplay(), getproviders.VersionConstraintsString(versionConstraints)))
   667  				} else {
   668  					c.Ui.Info(fmt.Sprintf("- Finding latest version of %s...", provider.ForDisplay()))
   669  				}
   670  			}
   671  		},
   672  		LinkFromCacheBegin: func(provider addrs.Provider, version getproviders.Version, cacheRoot string) {
   673  			c.Ui.Info(fmt.Sprintf("- Using %s v%s from the shared cache directory", provider.ForDisplay(), version))
   674  		},
   675  		FetchPackageBegin: func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) {
   676  			c.Ui.Info(fmt.Sprintf("- Installing %s v%s...", provider.ForDisplay(), version))
   677  		},
   678  		QueryPackagesFailure: func(provider addrs.Provider, err error) {
   679  			switch errorTy := err.(type) {
   680  			case getproviders.ErrProviderNotFound:
   681  				sources := errorTy.Sources
   682  				displaySources := make([]string, len(sources))
   683  				for i, source := range sources {
   684  					displaySources[i] = fmt.Sprintf("  - %s", source)
   685  				}
   686  				diags = diags.Append(tfdiags.Sourceless(
   687  					tfdiags.Error,
   688  					"Failed to query available provider packages",
   689  					fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s\n\n%s",
   690  						provider.ForDisplay(), err, strings.Join(displaySources, "\n"),
   691  					),
   692  				))
   693  			case getproviders.ErrRegistryProviderNotKnown:
   694  				// We might be able to suggest an alternative provider to use
   695  				// instead of this one.
   696  				suggestion := fmt.Sprintf("\n\nAll modules should specify their required_providers so that external consumers will get the correct providers when using a module. To see which modules are currently depending on %s, run the following command:\n    tofu providers", provider.ForDisplay())
   697  				alternative := getproviders.MissingProviderSuggestion(ctx, provider, inst.ProviderSource(), reqs)
   698  				if alternative != provider {
   699  					suggestion = fmt.Sprintf(
   700  						"\n\nDid you intend to use %s? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on %s, run the following command:\n    tofu providers",
   701  						alternative.ForDisplay(), provider.ForDisplay(),
   702  					)
   703  				}
   704  
   705  				if provider.Hostname == addrs.DefaultProviderRegistryHost {
   706  					suggestion += "\n\nIf you believe this provider is missing from the registry, please submit a issue on the OpenTofu Registry https://github.com/opentofu/registry/issues/"
   707  				}
   708  
   709  				diags = diags.Append(tfdiags.Sourceless(
   710  					tfdiags.Error,
   711  					"Failed to query available provider packages",
   712  					fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s",
   713  						provider.ForDisplay(), err, suggestion,
   714  					),
   715  				))
   716  			case getproviders.ErrHostNoProviders:
   717  				switch {
   718  				case errorTy.Hostname == svchost.Hostname("github.com") && !errorTy.HasOtherVersion:
   719  					// If a user copies the URL of a GitHub repository into
   720  					// the source argument and removes the schema to make it
   721  					// provider-address-shaped then that's one way we can end up
   722  					// here. We'll use a specialized error message in anticipation
   723  					// of that mistake. We only do this if github.com isn't a
   724  					// provider registry, to allow for the (admittedly currently
   725  					// rather unlikely) possibility that github.com starts being
   726  					// a real Terraform provider registry in the future.
   727  					diags = diags.Append(tfdiags.Sourceless(
   728  						tfdiags.Error,
   729  						"Invalid provider registry host",
   730  						fmt.Sprintf("The given source address %q specifies a GitHub repository rather than a OpenTofu provider. Refer to the documentation of the provider to find the correct source address to use.",
   731  							provider.String(),
   732  						),
   733  					))
   734  
   735  				case errorTy.HasOtherVersion:
   736  					diags = diags.Append(tfdiags.Sourceless(
   737  						tfdiags.Error,
   738  						"Invalid provider registry host",
   739  						fmt.Sprintf("The host %q given in provider source address %q does not offer a OpenTofu provider registry that is compatible with this OpenTofu version, but it may be compatible with a different OpenTofu version.",
   740  							errorTy.Hostname, provider.String(),
   741  						),
   742  					))
   743  
   744  				default:
   745  					diags = diags.Append(tfdiags.Sourceless(
   746  						tfdiags.Error,
   747  						"Invalid provider registry host",
   748  						fmt.Sprintf("The host %q given in provider source address %q does not offer a OpenTofu provider registry.",
   749  							errorTy.Hostname, provider.String(),
   750  						),
   751  					))
   752  				}
   753  
   754  			case getproviders.ErrRequestCanceled:
   755  				// We don't attribute cancellation to any particular operation,
   756  				// but rather just emit a single general message about it at
   757  				// the end, by checking ctx.Err().
   758  
   759  			default:
   760  				diags = diags.Append(tfdiags.Sourceless(
   761  					tfdiags.Error,
   762  					"Failed to resolve provider packages",
   763  					fmt.Sprintf("Could not resolve provider %s: %s",
   764  						provider.ForDisplay(), err,
   765  					),
   766  				))
   767  			}
   768  
   769  		},
   770  		QueryPackagesWarning: func(provider addrs.Provider, warnings []string) {
   771  			displayWarnings := make([]string, len(warnings))
   772  			for i, warning := range warnings {
   773  				displayWarnings[i] = fmt.Sprintf("- %s", warning)
   774  			}
   775  
   776  			diags = diags.Append(tfdiags.Sourceless(
   777  				tfdiags.Warning,
   778  				"Additional provider information from registry",
   779  				fmt.Sprintf("The remote registry returned warnings for %s:\n%s",
   780  					provider.String(),
   781  					strings.Join(displayWarnings, "\n"),
   782  				),
   783  			))
   784  		},
   785  		LinkFromCacheFailure: func(provider addrs.Provider, version getproviders.Version, err error) {
   786  			diags = diags.Append(tfdiags.Sourceless(
   787  				tfdiags.Error,
   788  				"Failed to install provider from shared cache",
   789  				fmt.Sprintf("Error while importing %s v%s from the shared cache directory: %s.", provider.ForDisplay(), version, err),
   790  			))
   791  		},
   792  		FetchPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) {
   793  			const summaryIncompatible = "Incompatible provider version"
   794  			switch err := err.(type) {
   795  			case getproviders.ErrProtocolNotSupported:
   796  				closestAvailable := err.Suggestion
   797  				switch {
   798  				case closestAvailable == getproviders.UnspecifiedVersion:
   799  					diags = diags.Append(tfdiags.Sourceless(
   800  						tfdiags.Error,
   801  						summaryIncompatible,
   802  						fmt.Sprintf(errProviderVersionIncompatible, provider.String()),
   803  					))
   804  				case version.GreaterThan(closestAvailable):
   805  					diags = diags.Append(tfdiags.Sourceless(
   806  						tfdiags.Error,
   807  						summaryIncompatible,
   808  						fmt.Sprintf(providerProtocolTooNew, provider.ForDisplay(),
   809  							version, tfversion.String(), closestAvailable, closestAvailable,
   810  							getproviders.VersionConstraintsString(reqs[provider]),
   811  						),
   812  					))
   813  				default: // version is less than closestAvailable
   814  					diags = diags.Append(tfdiags.Sourceless(
   815  						tfdiags.Error,
   816  						summaryIncompatible,
   817  						fmt.Sprintf(providerProtocolTooOld, provider.ForDisplay(),
   818  							version, tfversion.String(), closestAvailable, closestAvailable,
   819  							getproviders.VersionConstraintsString(reqs[provider]),
   820  						),
   821  					))
   822  				}
   823  			case getproviders.ErrPlatformNotSupported:
   824  				switch {
   825  				case err.MirrorURL != nil:
   826  					// If we're installing from a mirror then it may just be
   827  					// the mirror lacking the package, rather than it being
   828  					// unavailable from upstream.
   829  					diags = diags.Append(tfdiags.Sourceless(
   830  						tfdiags.Error,
   831  						summaryIncompatible,
   832  						fmt.Sprintf(
   833  							"Your chosen provider mirror at %s does not have a %s v%s package available for your current platform, %s.\n\nProvider releases are separate from OpenTofu CLI releases, so this provider might not support your current platform. Alternatively, the mirror itself might have only a subset of the plugin packages available in the origin registry, at %s.",
   834  							err.MirrorURL, err.Provider, err.Version, err.Platform,
   835  							err.Provider.Hostname,
   836  						),
   837  					))
   838  				default:
   839  					diags = diags.Append(tfdiags.Sourceless(
   840  						tfdiags.Error,
   841  						summaryIncompatible,
   842  						fmt.Sprintf(
   843  							"Provider %s v%s does not have a package available for your current platform, %s.\n\nProvider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.",
   844  							err.Provider, err.Version, err.Platform,
   845  						),
   846  					))
   847  				}
   848  
   849  			case getproviders.ErrRequestCanceled:
   850  				// We don't attribute cancellation to any particular operation,
   851  				// but rather just emit a single general message about it at
   852  				// the end, by checking ctx.Err().
   853  
   854  			default:
   855  				// We can potentially end up in here under cancellation too,
   856  				// in spite of our getproviders.ErrRequestCanceled case above,
   857  				// because not all of the outgoing requests we do under the
   858  				// "fetch package" banner are source metadata requests.
   859  				// In that case we will emit a redundant error here about
   860  				// the request being cancelled, but we'll still detect it
   861  				// as a cancellation after the installer returns and do the
   862  				// normal cancellation handling.
   863  
   864  				diags = diags.Append(tfdiags.Sourceless(
   865  					tfdiags.Error,
   866  					"Failed to install provider",
   867  					fmt.Sprintf("Error while installing %s v%s: %s", provider.ForDisplay(), version, err),
   868  				))
   869  			}
   870  		},
   871  		FetchPackageSuccess: func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) {
   872  			var keyID string
   873  			if authResult != nil && authResult.Signed() {
   874  				keyID = authResult.KeyID
   875  			}
   876  			if keyID != "" {
   877  				keyID = c.Colorize().Color(fmt.Sprintf(", key ID [reset][bold]%s[reset]", keyID))
   878  			}
   879  
   880  			if authResult != nil && authResult.SigningSkipped() {
   881  				c.Ui.Warn(fmt.Sprintf("- Installed %s v%s. Signature validation was skipped due to the registry not containing GPG keys for this provider", provider.ForDisplay(), version))
   882  			} else {
   883  				c.Ui.Info(fmt.Sprintf("- Installed %s v%s (%s%s)", provider.ForDisplay(), version, authResult, keyID))
   884  			}
   885  		},
   886  		ProvidersLockUpdated: func(provider addrs.Provider, version getproviders.Version, localHashes []getproviders.Hash, signedHashes []getproviders.Hash, priorHashes []getproviders.Hash) {
   887  			// We're going to use this opportunity to track if we have any
   888  			// "incomplete" installs of providers. An incomplete install is
   889  			// when we are only going to write the local hashes into our lock
   890  			// file which means a `tofu init` command will fail in future
   891  			// when used on machines of a different architecture.
   892  			//
   893  			// We want to print a warning about this.
   894  
   895  			if len(signedHashes) > 0 {
   896  				// If we have any signedHashes hashes then we don't worry - as
   897  				// we know we retrieved all available hashes for this version
   898  				// anyway.
   899  				return
   900  			}
   901  
   902  			// If local hashes and prior hashes are exactly the same then
   903  			// it means we didn't record any signed hashes previously, and
   904  			// we know we're not adding any extra in now (because we already
   905  			// checked the signedHashes), so that's a problem.
   906  			//
   907  			// In the actual check here, if we have any priorHashes and those
   908  			// hashes are not the same as the local hashes then we're going to
   909  			// accept that this provider has been configured correctly.
   910  			if len(priorHashes) > 0 && !reflect.DeepEqual(localHashes, priorHashes) {
   911  				return
   912  			}
   913  
   914  			// Now, either signedHashes is empty, or priorHashes is exactly the
   915  			// same as our localHashes which means we never retrieved the
   916  			// signedHashes previously.
   917  			//
   918  			// Either way, this is bad. Let's complain/warn.
   919  			incompleteProviders = append(incompleteProviders, provider.ForDisplay())
   920  		},
   921  		ProvidersFetched: func(authResults map[addrs.Provider]*getproviders.PackageAuthenticationResult) {
   922  			thirdPartySigned := false
   923  			for _, authResult := range authResults {
   924  				if authResult.Signed() {
   925  					thirdPartySigned = true
   926  					break
   927  				}
   928  			}
   929  			if thirdPartySigned {
   930  				c.Ui.Info(fmt.Sprintf("\nProviders are signed by their developers.\n" +
   931  					"If you'd like to know more about provider signing, you can read about it here:\n" +
   932  					"https://opentofu.org/docs/cli/plugins/signing/"))
   933  			}
   934  		},
   935  	}
   936  	ctx = evts.OnContext(ctx)
   937  
   938  	mode := providercache.InstallNewProvidersOnly
   939  	if upgrade {
   940  		if flagLockfile == "readonly" {
   941  			c.Ui.Error("The -upgrade flag conflicts with -lockfile=readonly.")
   942  			return true, true, diags
   943  		}
   944  
   945  		mode = providercache.InstallUpgrades
   946  	}
   947  	newLocks, err := inst.EnsureProviderVersions(ctx, previousLocks, reqs, mode)
   948  	if ctx.Err() == context.Canceled {
   949  		c.showDiagnostics(diags)
   950  		c.Ui.Error("Provider installation was canceled by an interrupt signal.")
   951  		return true, true, diags
   952  	}
   953  	if err != nil {
   954  		// The errors captured in "err" should be redundant with what we
   955  		// received via the InstallerEvents callbacks above, so we'll
   956  		// just return those as long as we have some.
   957  		if !diags.HasErrors() {
   958  			diags = diags.Append(err)
   959  		}
   960  
   961  		return true, true, diags
   962  	}
   963  
   964  	// If the provider dependencies have changed since the last run then we'll
   965  	// say a little about that in case the reader wasn't expecting a change.
   966  	// (When we later integrate module dependencies into the lock file we'll
   967  	// probably want to refactor this so that we produce one lock-file related
   968  	// message for all changes together, but this is here for now just because
   969  	// it's the smallest change relative to what came before it, which was
   970  	// a hidden JSON file specifically for tracking providers.)
   971  	if !newLocks.Equal(previousLocks) {
   972  		// if readonly mode
   973  		if flagLockfile == "readonly" {
   974  			// check if required provider dependences change
   975  			if !newLocks.EqualProviderAddress(previousLocks) {
   976  				diags = diags.Append(tfdiags.Sourceless(
   977  					tfdiags.Error,
   978  					`Provider dependency changes detected`,
   979  					`Changes to the required provider dependencies were detected, but the lock file is read-only. To use and record these requirements, run "tofu init" without the "-lockfile=readonly" flag.`,
   980  				))
   981  				return true, true, diags
   982  			}
   983  
   984  			// suppress updating the file to record any new information it learned,
   985  			// such as a hash using a new scheme.
   986  			diags = diags.Append(tfdiags.Sourceless(
   987  				tfdiags.Warning,
   988  				`Provider lock file not updated`,
   989  				`Changes to the provider selections were detected, but not saved in the .terraform.lock.hcl file. To record these selections, run "tofu init" without the "-lockfile=readonly" flag.`,
   990  			))
   991  			return true, false, diags
   992  		}
   993  
   994  		// Jump in here and add a warning if any of the providers are incomplete.
   995  		if len(incompleteProviders) > 0 {
   996  			// We don't really care about the order here, we just want the
   997  			// output to be deterministic.
   998  			sort.Slice(incompleteProviders, func(i, j int) bool {
   999  				return incompleteProviders[i] < incompleteProviders[j]
  1000  			})
  1001  			diags = diags.Append(tfdiags.Sourceless(
  1002  				tfdiags.Warning,
  1003  				incompleteLockFileInformationHeader,
  1004  				fmt.Sprintf(
  1005  					incompleteLockFileInformationBody,
  1006  					strings.Join(incompleteProviders, "\n  - "),
  1007  					getproviders.CurrentPlatform.String())))
  1008  		}
  1009  
  1010  		if previousLocks.Empty() {
  1011  			// A change from empty to non-empty is special because it suggests
  1012  			// we're running "tofu init" for the first time against a
  1013  			// new configuration. In that case we'll take the opportunity to
  1014  			// say a little about what the dependency lock file is, for new
  1015  			// users or those who are upgrading from a previous Terraform
  1016  			// version that didn't have dependency lock files.
  1017  			c.Ui.Output(c.Colorize().Color(`
  1018  OpenTofu has created a lock file [bold].terraform.lock.hcl[reset] to record the provider
  1019  selections it made above. Include this file in your version control repository
  1020  so that OpenTofu can guarantee to make the same selections by default when
  1021  you run "tofu init" in the future.`))
  1022  		} else {
  1023  			c.Ui.Output(c.Colorize().Color(`
  1024  OpenTofu has made some changes to the provider dependency selections recorded
  1025  in the .terraform.lock.hcl file. Review those changes and commit them to your
  1026  version control system if they represent changes you intended to make.`))
  1027  		}
  1028  
  1029  		moreDiags = c.replaceLockedDependencies(newLocks)
  1030  		diags = diags.Append(moreDiags)
  1031  	}
  1032  
  1033  	return true, false, diags
  1034  }
  1035  
  1036  // backendConfigOverrideBody interprets the raw values of -backend-config
  1037  // arguments into a hcl Body that should override the backend settings given
  1038  // in the configuration.
  1039  //
  1040  // If the result is nil then no override needs to be provided.
  1041  //
  1042  // If the returned diagnostics contains errors then the returned body may be
  1043  // incomplete or invalid.
  1044  func (c *InitCommand) backendConfigOverrideBody(flags rawFlags, schema *configschema.Block) (hcl.Body, tfdiags.Diagnostics) {
  1045  	items := flags.AllItems()
  1046  	if len(items) == 0 {
  1047  		return nil, nil
  1048  	}
  1049  
  1050  	var ret hcl.Body
  1051  	var diags tfdiags.Diagnostics
  1052  	synthVals := make(map[string]cty.Value)
  1053  
  1054  	mergeBody := func(newBody hcl.Body) {
  1055  		if ret == nil {
  1056  			ret = newBody
  1057  		} else {
  1058  			ret = configs.MergeBodies(ret, newBody)
  1059  		}
  1060  	}
  1061  	flushVals := func() {
  1062  		if len(synthVals) == 0 {
  1063  			return
  1064  		}
  1065  		newBody := configs.SynthBody("-backend-config=...", synthVals)
  1066  		mergeBody(newBody)
  1067  		synthVals = make(map[string]cty.Value)
  1068  	}
  1069  
  1070  	if len(items) == 1 && items[0].Value == "" {
  1071  		// Explicitly remove all -backend-config options.
  1072  		// We do this by setting an empty but non-nil ConfigOverrides.
  1073  		return configs.SynthBody("-backend-config=''", synthVals), diags
  1074  	}
  1075  
  1076  	for _, item := range items {
  1077  		eq := strings.Index(item.Value, "=")
  1078  
  1079  		if eq == -1 {
  1080  			// The value is interpreted as a filename.
  1081  			newBody, fileDiags := c.loadHCLFile(item.Value)
  1082  			diags = diags.Append(fileDiags)
  1083  			if fileDiags.HasErrors() {
  1084  				continue
  1085  			}
  1086  			// Generate an HCL body schema for the backend block.
  1087  			var bodySchema hcl.BodySchema
  1088  			for name := range schema.Attributes {
  1089  				// We intentionally ignore the `Required` attribute here
  1090  				// because backend config override files can be partial. The
  1091  				// goal is to make sure we're not loading a file with
  1092  				// extraneous attributes or blocks.
  1093  				bodySchema.Attributes = append(bodySchema.Attributes, hcl.AttributeSchema{
  1094  					Name: name,
  1095  				})
  1096  			}
  1097  			for name, block := range schema.BlockTypes {
  1098  				var labelNames []string
  1099  				if block.Nesting == configschema.NestingMap {
  1100  					labelNames = append(labelNames, "key")
  1101  				}
  1102  				bodySchema.Blocks = append(bodySchema.Blocks, hcl.BlockHeaderSchema{
  1103  					Type:       name,
  1104  					LabelNames: labelNames,
  1105  				})
  1106  			}
  1107  			// Verify that the file body matches the expected backend schema.
  1108  			_, schemaDiags := newBody.Content(&bodySchema)
  1109  			diags = diags.Append(schemaDiags)
  1110  			if schemaDiags.HasErrors() {
  1111  				continue
  1112  			}
  1113  			flushVals() // deal with any accumulated individual values first
  1114  			mergeBody(newBody)
  1115  		} else {
  1116  			name := item.Value[:eq]
  1117  			rawValue := item.Value[eq+1:]
  1118  			attrS := schema.Attributes[name]
  1119  			if attrS == nil {
  1120  				diags = diags.Append(tfdiags.Sourceless(
  1121  					tfdiags.Error,
  1122  					"Invalid backend configuration argument",
  1123  					fmt.Sprintf("The backend configuration argument %q given on the command line is not expected for the selected backend type.", name),
  1124  				))
  1125  				continue
  1126  			}
  1127  			value, valueDiags := configValueFromCLI(item.String(), rawValue, attrS.Type)
  1128  			diags = diags.Append(valueDiags)
  1129  			if valueDiags.HasErrors() {
  1130  				continue
  1131  			}
  1132  			synthVals[name] = value
  1133  		}
  1134  	}
  1135  
  1136  	flushVals()
  1137  
  1138  	return ret, diags
  1139  }
  1140  
  1141  func (c *InitCommand) AutocompleteArgs() complete.Predictor {
  1142  	return complete.PredictDirs("")
  1143  }
  1144  
  1145  func (c *InitCommand) AutocompleteFlags() complete.Flags {
  1146  	return complete.Flags{
  1147  		"-backend":        completePredictBoolean,
  1148  		"-cloud":          completePredictBoolean,
  1149  		"-backend-config": complete.PredictFiles("*.tfvars"), // can also be key=value, but we can't "predict" that
  1150  		"-force-copy":     complete.PredictNothing,
  1151  		"-from-module":    completePredictModuleSource,
  1152  		"-get":            completePredictBoolean,
  1153  		"-input":          completePredictBoolean,
  1154  		"-lock":           completePredictBoolean,
  1155  		"-lock-timeout":   complete.PredictAnything,
  1156  		"-no-color":       complete.PredictNothing,
  1157  		"-plugin-dir":     complete.PredictDirs(""),
  1158  		"-reconfigure":    complete.PredictNothing,
  1159  		"-migrate-state":  complete.PredictNothing,
  1160  		"-upgrade":        completePredictBoolean,
  1161  	}
  1162  }
  1163  
  1164  func (c *InitCommand) Help() string {
  1165  	helpText := `
  1166  Usage: tofu [global options] init [options]
  1167  
  1168    Initialize a new or existing OpenTofu working directory by creating
  1169    initial files, loading any remote state, downloading modules, etc.
  1170  
  1171    This is the first command that should be run for any new or existing
  1172    OpenTofu configuration per machine. This sets up all the local data
  1173    necessary to run OpenTofu that is typically not committed to version
  1174    control.
  1175  
  1176    This command is always safe to run multiple times. Though subsequent runs
  1177    may give errors, this command will never delete your configuration or
  1178    state. Even so, if you have important information, please back it up prior
  1179    to running this command, just in case.
  1180  
  1181  Options:
  1182  
  1183    -backend=false          Disable backend or cloud backend initialization
  1184                            for this configuration and use what was previously
  1185                            initialized instead.
  1186  
  1187                            aliases: -cloud=false
  1188  
  1189    -backend-config=path    Configuration to be merged with what is in the
  1190                            configuration file's 'backend' block. This can be
  1191                            either a path to an HCL file with key/value
  1192                            assignments (same format as terraform.tfvars) or a
  1193                            'key=value' format, and can be specified multiple
  1194                            times. The backend type must be in the configuration
  1195                            itself.
  1196  
  1197    -force-copy             Suppress prompts about copying state data when
  1198                            initializating a new state backend. This is
  1199                            equivalent to providing a "yes" to all confirmation
  1200                            prompts.
  1201  
  1202    -from-module=SOURCE     Copy the contents of the given module into the target
  1203                            directory before initialization.
  1204  
  1205    -get=false              Disable downloading modules for this configuration.
  1206  
  1207    -input=false            Disable interactive prompts. Note that some actions may
  1208                            require interactive prompts and will error if input is
  1209                            disabled.
  1210  
  1211    -lock=false             Don't hold a state lock during backend migration.
  1212                            This is dangerous if others might concurrently run
  1213                            commands against the same workspace.
  1214  
  1215    -lock-timeout=0s        Duration to retry a state lock.
  1216  
  1217    -no-color               If specified, output won't contain any color.
  1218  
  1219    -plugin-dir             Directory containing plugin binaries. This overrides all
  1220                            default search paths for plugins, and prevents the
  1221                            automatic installation of plugins. This flag can be used
  1222                            multiple times.
  1223  
  1224    -reconfigure            Reconfigure a backend, ignoring any saved
  1225                            configuration.
  1226  
  1227    -migrate-state          Reconfigure a backend, and attempt to migrate any
  1228                            existing state.
  1229  
  1230    -upgrade                Install the latest module and provider versions
  1231                            allowed within configured constraints, overriding the
  1232                            default behavior of selecting exactly the version
  1233                            recorded in the dependency lockfile.
  1234  
  1235    -lockfile=MODE          Set a dependency lockfile mode.
  1236                            Currently only "readonly" is valid.
  1237  
  1238    -ignore-remote-version  A rare option used for cloud backend and the remote backend
  1239                            only. Set this to ignore checking that the local and remote
  1240                            OpenTofu versions use compatible state representations, making
  1241                            an operation proceed even when there is a potential mismatch.
  1242                            See the documentation on configuring OpenTofu with
  1243                            cloud backend for more information.
  1244  
  1245    -test-directory=path    Set the OpenTofu test directory, defaults to "tests". When set, the
  1246                            test command will search for test files in the current directory and
  1247                            in the one specified by the flag.
  1248  
  1249    -json                   Produce output in a machine-readable JSON format, 
  1250                            suitable for use in text editor integrations and other 
  1251                            automated systems. Always disables color.
  1252  
  1253  `
  1254  	return strings.TrimSpace(helpText)
  1255  }
  1256  
  1257  func (c *InitCommand) Synopsis() string {
  1258  	return "Prepare your working directory for other commands"
  1259  }
  1260  
  1261  const errInitConfigError = `
  1262  [reset]OpenTofu encountered problems during initialization, including problems
  1263  with the configuration, described below.
  1264  
  1265  The OpenTofu configuration must be valid before initialization so that
  1266  OpenTofu can determine which modules and providers need to be installed.
  1267  `
  1268  
  1269  const errInitCopyNotEmpty = `
  1270  The working directory already contains files. The -from-module option requires
  1271  an empty directory into which a copy of the referenced module will be placed.
  1272  
  1273  To initialize the configuration already in this working directory, omit the
  1274  -from-module option.
  1275  `
  1276  
  1277  const outputInitEmpty = `
  1278  [reset][bold]OpenTofu initialized in an empty directory![reset]
  1279  
  1280  The directory has no OpenTofu configuration files. You may begin working
  1281  with OpenTofu immediately by creating OpenTofu configuration files.
  1282  `
  1283  
  1284  const outputInitSuccess = `
  1285  [reset][bold][green]OpenTofu has been successfully initialized![reset][green]
  1286  `
  1287  
  1288  const outputInitSuccessCloud = `
  1289  [reset][bold][green]Cloud backend has been successfully initialized![reset][green]
  1290  `
  1291  
  1292  const outputInitSuccessCLI = `[reset][green]
  1293  You may now begin working with OpenTofu. Try running "tofu plan" to see
  1294  any changes that are required for your infrastructure. All OpenTofu commands
  1295  should now work.
  1296  
  1297  If you ever set or change modules or backend configuration for OpenTofu,
  1298  rerun this command to reinitialize your working directory. If you forget, other
  1299  commands will detect it and remind you to do so if necessary.
  1300  `
  1301  
  1302  const outputInitSuccessCLICloud = `[reset][green]
  1303  You may now begin working with cloud backend. Try running "tofu plan" to
  1304  see any changes that are required for your infrastructure.
  1305  
  1306  If you ever set or change modules or OpenTofu Settings, run "tofu init"
  1307  again to reinitialize your working directory.
  1308  `
  1309  
  1310  // providerProtocolTooOld is a message sent to the CLI UI if the provider's
  1311  // supported protocol versions are too old for the user's version of tofu,
  1312  // but a newer version of the provider is compatible.
  1313  const providerProtocolTooOld = `Provider %q v%s is not compatible with OpenTofu %s.
  1314  Provider version %s is the latest compatible version. Select it with the following version constraint:
  1315  	version = %q
  1316  
  1317  OpenTofu checked all of the plugin versions matching the given constraint:
  1318  	%s
  1319  
  1320  Consult the documentation for this provider for more information on compatibility between provider and OpenTofu versions.
  1321  `
  1322  
  1323  // providerProtocolTooNew is a message sent to the CLI UI if the provider's
  1324  // supported protocol versions are too new for the user's version of tofu,
  1325  // and the user could either upgrade tofu or choose an older version of the
  1326  // provider.
  1327  const providerProtocolTooNew = `Provider %q v%s is not compatible with OpenTofu %s.
  1328  You need to downgrade to v%s or earlier. Select it with the following constraint:
  1329  	version = %q
  1330  
  1331  OpenTofu checked all of the plugin versions matching the given constraint:
  1332  	%s
  1333  
  1334  Consult the documentation for this provider for more information on compatibility between provider and OpenTofu versions.
  1335  Alternatively, upgrade to the latest version of OpenTofu for compatibility with newer provider releases.
  1336  `
  1337  
  1338  // No version of the provider is compatible.
  1339  const errProviderVersionIncompatible = `No compatible versions of provider %s were found.`
  1340  
  1341  // incompleteLockFileInformationHeader is the summary displayed to users when
  1342  // the lock file has only recorded local hashes.
  1343  const incompleteLockFileInformationHeader = `Incomplete lock file information for providers`
  1344  
  1345  // incompleteLockFileInformationBody is the body of text displayed to users when
  1346  // the lock file has only recorded local hashes.
  1347  const incompleteLockFileInformationBody = `Due to your customized provider installation methods, OpenTofu was forced to calculate lock file checksums locally for the following providers:
  1348    - %s
  1349  
  1350  The current .terraform.lock.hcl file only includes checksums for %s, so OpenTofu running on another platform will fail to install these providers.
  1351  
  1352  To calculate additional checksums for another platform, run:
  1353    tofu providers lock -platform=linux_amd64
  1354  (where linux_amd64 is the platform to generate)`